Android恶意样本分析,第一件要做的事,不是急着脱壳也不是急着抓包,而是先搞清楚自己手上是个什么东西。很多刚入门的同学拿到一个样本APK,第一反应就是扔进在线扫描平台看报告,这个习惯我不是说不好,而是它容易让你产生依赖——报告是别人出的,特征是别人提取的,你只是看了一个结论,自己什么都没学会。
我自己做移动安全这些年,最深的体会就是:病毒分析本质上是一场信息博弈。样本在想办法隐藏自己,你在想办法把它看穿。这个系列的文章,我会按照实际分析的推进顺序来写,从环境准备、静态分析、动态监控、脱壳对抗到行为判定,一步步把一套完整的分析思路拆给你看。
这一篇作为系列的第一篇,先解决“从零开始怎么看一个安卓样本”的问题,不涉及太深的反混淆和脱壳对抗,重点把这套基础流程跑通。等这套基本功扎实了,后面遇到加固、混淆、HOOK对抗的时候,你才有底气说“我能分析”。
1. 分析环境搭建:没准备好战场,别急着上枪
1.1 为什么必须用独立的分析环境
我一直跟身边的人强调,分析恶意样本绝不能用主力机,连插着SIM卡的测试机都不行。因为现在很多安卓恶意样本带有非常强的环境感知能力,它们会检测你手机上是否安装了常用社交软件、是否有通话记录、是否插了SIM卡、甚至会不会收到特定的验证码。这些检测一旦被你手动过掉或者环境里不存在,样本就会进入“休眠模式”,什么都不做,你的分析就会陷入“明明拿到了样本却看不到任何恶意行为”的尴尬局面。
我见过最典型的一个案例:某金融类木马样本,在无SIM卡、无通讯录的模拟器上运行时非常安静,所有的恶意代码全部不触发。后来我们把通话记录、通讯录、短信等数据全部模拟出来之后,样本立刻开始活跃,加密上传通讯录、拦截短信验证码、隐藏自身图标,动作一个接一个。这个例子充分说明了“模拟真实用户环境”的重要性。
独立分析环境要满足以下几个条件:
- 系统是干净的,没有任何个人数据、常用APP、甚至不要登录任何账号
- 网络是可控的,最好能实时看到样本对外发出的每一个请求
- 有完整的快照回滚能力,样本搞坏了系统可以一键恢复
- 工具的安装要克制,不要装一堆大而全的监控软件——工具本身可能被样本反检测
1.2 我推荐的模拟器配置方案
安卓样本分析的主流载体还是模拟器,因为快照、回滚、网络监控都方便。我自己常用的组合是这样的:
- 夜神模拟器安卓7:作为主力动态分析环境
- 安卓9或更高版本的系统镜像:用于分析那些强制要求高版本系统的样本
- 真机(刷了原生系统的旧手机):用于模拟器上跑不了或者行为异常的情况
选择安卓7是有讲究的。早期的恶意样本绝大多数都兼容安卓7以下版本,而且安卓7的ART运行时对Xposed框架的兼容性极好,很多HOOK方案在安卓7上不需要考虑SELinux和Siginfo太多问题。另外,许多加固方案的脱壳机对安卓7的支持也是最成熟的,比如你在FART或者Youpk上看到的兼容条件,绝大多数都写着“支持安卓7.0-9.0”。
模拟器的配置有几个细节值得注意:
- 不要开Root权限的默认设置:很多安全检测软件自己就是靠Root运行的,你先开Root,你的分析工具就已经暴露了
- CPU核心数给到4核以上,内存给到4G以上:样本很多时候要跑好几个小时,资源不够会假死
- 打开模拟器的“外部网络”访问权限:如果只允许模拟器访问局部网,有些样本的联网行为会异常
1.3 基础分析工具清单
这套工具是我每次拿到新样本都必须提前准备好的,整理成清单给你参考:
| 工具 | 作用 | 使用场景 |
|---|---|---|
| JADX | APK静态反编译,直接看Java层代码 | 快速浏览样本逻辑 |
| jadx-gui | JADX的可视化界面,带反编译功能 | 日常主要分析工具 |
| frida | 动态插桩框架,任意Java层函数HOOK | 动态分析核心 |
| objection | 基于Frida的免Root动态分析 | 快速绕过Root检测、SSL Pinning |
| Xposed/LSPosed | Java层及部分Native层HOOK框架 | 高版本模拟器动态分析 |
| Wireshark | 网络流量抓取和分析 | 抓样本通信数据 |
| Charles/Fiddler | HTTPS解密代理 | 分析样本与服务器的加密通信 |
| 好压/7-Zip | 解包APK | 查看资源文件和原始文件 |
强调一点:工具不是越多越好,而是越顺越好。你自己用得最顺手的工具组合,效率永远是最高的。我见过很多人电脑上装了一堆工具,结果每个工具都不会深度使用,拿样本分析的时候反而不知道怎么下手。
1.4 网络管控怎么做
模拟器的网络管控,最容易忽略但是最影响分析质量。我常用的方案是:
- 在宿主机上搭一个代理服务器,模拟器的流量全部指向这个代理
- 代理记录所有HTTP/HTTPS请求的域名、路径、参数
- HTTPS流量用Charles的中间人证书解密查看
这里面有一个非常大的坑,后面专门会讲:很多恶意样本做了SSL Pinning校验,你一挂代理抓HTTPS,它会直接报错退出。所以网络分析必须准备两套方案,一套是正常代理解密,另一套是用Frida绕过SSL校验后再抓包。
2. 静态分析三件套:解包、看清单、搜特征
2.1 把APK拆开看看里面有什么
拿到样本后,第一件事永远是解包。APK本质上是个ZIP包,你可以直接用7-Zip打开。解包后的目录结构是这样:
assets/ 存放本地资源文件、加密的payload等 lib/ 存放SO库(armeabi-v7a、arm64-v8a) META-INF/ 签名信息和清单文件 res/ 各种资源文件 AndroidManifest.xml 核心配置文件 classes.dex 程序主代码(Dalvik字节码)看APK目录能获得的第一手信息是很多的:
- assets目录里有没有可疑的加密文件——很多恶意样本会把真正的恶意代码藏在assets里的加密文件中,运行时再解密释放
- lib目录下的SO库的名字和数量——如果大量SO库名带“protect”“secure”“shell”之类的字样,那基本就是加了壳
- classes.dex的数量——正常APP一般一个dex就够,如果解压出来是好几个dex,要么功能确实很大,要么就是把恶意代码拆分隐藏了
我印象很深的一个样本,解包后在assets目录下看到一个文件叫"dd.dat",大小有4MB多,而这整个APK才6MB。正常APK的资源文件是不会有这种大体积的.dat文件的,后来确认这个文件就是加密的恶意payload,运行时解密释放到应用私有目录执行。
2.2 读懂AndroidManifest.xml
AndroidManifest.xml是APK的“身份证”,里面什么关键信息都有。拿JADX打开样本后,第一件事是看manifest。主要关注这几个点:
- 包名:包名是唯一的,很多恶意样本会伪装成正常的包名来迷惑用户
- 入口Activity:通常第一个启动的界面
- 权限列表:样本申请了哪些权限,是否有与其功能不符合的敏感权限
- Service、Receiver列表:看样本注册了哪些后台服务
- Application的name:有加固的话这个值会被替换成壳的Application
看权限列表是识别恶意样本最快的途径之一。你想想,一个计算器APP申请读取通讯录、发送短信、获取定位权限,这正常吗?明显不对。反过来,如果这个计算器APP申请了“系统设置修改”权限,那更不用想,绝对有鬼。
但也要注意,高级样本越来越会“克制”权限申请,它们会把敏感行为拆分成多次申请、或者运行时通过动态注册服务来避免在安装时暴露权限。所以看权限只是第一步,不是判断依据。
2.3 关键代码特征快速定位
解包和看清单只是前期侦查,真正要命的代码定位得靠搜索。我最常搜索的代码特征包括:
- 反射调用:搜索
Class.forName、getMethod、invoke,恶意样本经常用反射做动态加载来隐藏真实行为 - 动态加载:搜索
DexClassLoader、PathClassLoader,样本把恶意代码藏在加密文件里,运行时会动态加载 - 系统命令执行:搜索
Runtime.getRuntime().exec、ProcessBuilder,直接在Java层执行系统命令 - 隐私数据获取:搜索
getDeviceId、getSubscriberId、getSimSerialNumber,设备信息是黑产最想要的 - 短信/通讯录操作:搜索
SmsManager、ContentResolver查询通讯录 - 加密算法:搜索
AES、DES、RSA、Cipher,样本和服务器通信前数据一般都会加密
这些特征搜出来之后,结合JADX的“交叉引用”功能(谁调用了我、我调用了谁),就能一步一步把整个调用链串起来。这个过程有点像剥洋葱,一层一层剥开,最终露出样本的“心脏”。
有一个搜特征的技巧我记得特别牢:不要只搜索一个词,要多组合几个关键词。比如一个恶意行为需要三四个特征函数组合才能触发,你只搜一个肯定不够;反过来,如果你搜到三个以上的敏感行为特征出现在同一段代码里,那基本就能确定这是恶意逻辑。
2.4 静态分析的局限
静态分析不是万能的,它的最大敌人是代码混淆和运行时动态行为。静态分析只能看到“代码长的什么样”,看不到“代码运行时干了什么”。比如加密的payload在静态阶段是解密不了的,因为密钥可能在SO库里、可能在服务器上、甚至可能在用户输入里——静态阶段根本无从知晓。
另外,很多恶意样本的代码被做了控制流平坦化、字符串加密,静态分析看到的就是一堆乱糟糟的跳转和看不到明文的字符串,这时候还得上动态分析。
3. 动态分析实操:让样本自己跑起来现原形
3.1 动态分析的三大前置条件
动态分析就是把样本放到隔离环境里,让它真正运行起来,然后观察它的行为。但跑之前有三个前置条件必须准备到位:
- 快照备份:模拟器当前状态做一个快照,样本跑挂了直接恢复
- 网络代理已配置:Charles/Wireshark已经挂好,流量等着抓
- Hook工具已注入:Frida server已经在模拟器里跑起来了
有一次我犯了个低级错误:样本还没开始跑,Frida server没启动,等样本起来了我才发现,结果大部分通信流量已经错过了。所以这三样东西,必须在样本安装之前就全部就绪。
3.2 安装、启动、观察、记录四步法
接下来是动态分析的常规流程:
- 安装样本:注意看安装时的权限申请弹窗,很多样本会诱导用户给权限
- 启动样本:启动后不要急着点,先在系统进程里找到它,确认进程存活
- 观察行为:看进程列表变化、看文件读写、看网络流量、看界面跳转
- 记录每步操作:包括点击了哪里、弹了什么窗、触发了什么行为
动态分析最关键的一点是:要像真实用户一样操作样本。你需要点击它的界面按钮,模拟正常交互。有些样本的恶意行为要特定操作才会触发,比如双击五次、长按三秒、输入特定代码等等。
3.3 用Frida看Java层动态调用
Frida是目前动态分析的主流方案,它的能力是直接在运行时修改Java层方法逻辑,打印所有关键函数的调用参数和返回值。以抓取getDeviceId为例,Frida脚本可以这样写:
Java.perform(function() { var TelephonyManager = Java.use("android.telephony.TelephonyManager"); TelephonyManager.getDeviceId.implementation = function() { var result = this.getDeviceId(); console.log("getDeviceId called, result = " + result); return result; }; });这个脚本运行后,一旦样本调用了getDeviceId,我们就能看到它拿走了什么设备信息。这种方式的优势是对样本完全透明,样本感知不到自己被监视。
我自己常用的是用objection直接做Frida插桩,因为它自动化程度高,你可以直接输入命令查看当前调用了哪些方法、实时拦截和修改返回值。像这样:
objection -g com.example.malware explore进入交互模式后,输入android hooking list activities可以列出所有Activity,输入android hooking watch class android.telephony.TelephonyManager就能监控TelephonyManager的所有方法调用。
3.4 动态加载的代码怎么分析
前面静态分析时提到的DexClassLoader,在动态分析阶段就能暴露真面目了。当样本运行起来,在JADX里看到DexClassLoader加载的dex路径上,你会看到一个真实存在的文件,这时候你就可以把那个文件拉出来,再扔进JADX里反编译——这其实就是脱壳的雏形。
加密payload也一样,运行时它必须解密才能加载,你只要在解密后、加载前的那一刻把文件抓出来,整个加密就破了。这个时机抓得好不好,直接决定了你的分析效率。
4. 流量分析:看样本和自己的服务器说了什么
4.1 流量抓取的两个层次
样本运行起来之后的网络行为,是你判断它的用途、他的上线指令、它的远控地址最直接证据。流量分析分两个层次:
第一层:抓包看HTTP/HTTPS明文。这是最直观的。样本跟服务器通信,GET/POST请求、响应包内容,一目了然。Charles上直接可以看到域名、路径、参数、返回值。
第二层:看TCP/UDP层的直接通信。有些样本不走HTTP,直接裸TCP发数据——这通常是为了逃避HTTP层的检测规则。Wireshark能看到这些原始流量。
4.2 HTTPS解密与SSL Pinning绕过
现在绝大多数安卓恶意样本的通信都是HTTPS加密的,直接抓包只能看到证书握手,看不到内容。所以要用Charles的中间人证书来解密HTTPS流量。在模拟器上信任Charles的证书之后,HTTP请求就能被解密查看。
但真正麻烦的是SSL Pinning。样本把自己的服务器证书固定在了客户端代码里,中间人证书不被信任,SSL握手直接失败。绕过SSL Pinning的标准做法是用Frida Hook掉证书校验函数:
Java.perform(function() { var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl"); TrustManagerImpl.verifyChain.implementation = function() { return []; // 直接返回空数组,跳过证书校验 }; });跑上这个脚本后,再开Charles抓包,HTTPS流量的内容就能看到了。
4.3 通信内容分析
能从流量里发现的典型恶意特征包括:
- 上传本机信息:POST请求的Body里带设备ID、手机号码、通讯录数据
- 接收远程指令:服务器响应一个JSON,包含
action=sendSms之类的命令字段 - 下载恶意载荷:样本从服务器下载一个APK或DEX,然后动态加载执行
- 心跳保活机制:每30秒或者1分钟发一次心跳包,保持和控制端的长连接
我曾经分析过一个样本,它的网络行为会周期性向服务器发送加密心跳包。抓包后发现心跳包的内容是一串经过AES加密的base64字符串,用硬编码在样本里的密钥做解密后,能直接看到里面包含手机的IMEI号、SIM卡运营商信息和当前电量——这些数据本身不敏感,但组合起来就拼凑出这个手机号用户的完整画像了,这就是黑产泄数前的情报收集行为。
5. 恶意行为判定与分类
5.1 常见恶意软件的判定标准
把前面静态、动态、流量三个阶段的信息收集汇总之后,就能判定样本是否属于恶意软件了。常见的分类维度如下:
- 隐私窃取类:偷偷获取通讯录、短信、定位、设备信息并上传
- 资费消耗类:后台发短信、订购SP服务、下载应用
- 远程控制类:接收服务器指令执行操作,包括静默安装、卸载应用
- 勒索类:加密用户文件,弹窗索要赎金
- 广告推送类:强推广告、静默安装推广应用
判定标准最好用一个简单粗暴的逻辑:你愿不愿意让这个APP在你的主力手机上运行?如果你犹豫了,这个APP基本就有问题。
5.2 行为特征、证据链完整记录
分析完要写报告,报告的核心是形成一条完整的证据链。恶意行为的证据链要满足“时间、地点、人物、事件”四要素:
- 时间:样本在什么时间触发了恶意行为(启动后多久?点击后多久?)
- 地点:样本在什么路径下释放了什么文件(assets/dd.dat被解压到/data/data目录)
- 对象:样本操作了哪些数据(获取的IMEI、通讯录条目数)
- 事件:具体的恶意动作(上传了内容、执行了命令)
写报告的时候,这套证据链越完整,你的分析结论就越可信。
6. 反分析对抗:样本不想让你看到的那些事
6.1 Root检测、模拟器检测与绕过
越高端的样本越会检查自己是不是运行在一个“安全环境”里。常见的手段有:
- Root检测:检查
su文件是否存在、检查Root权限 - 模拟器检测:检查Build.FINGERPRINT里是否包含
generic关键字 - 调试检测:检查
Debug.isDebuggerConnected()返回值,或者检测TracerPid - HOOK框架检测:检测Xposed相关包名是否存在
这些检测一旦命中,样本要么直接退出,要么故意返回假数据,坑的就是分析者。绕过思路也很清晰:让样本认为它运行在一台真实手机上、而且这台真实手机没有Root、没有调试、没有HOOK。
最暴力的办法是直接用Frida修改检测函数的返回值:
Java.perform(function() { var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer"); RootBeer.isRooted.implementation = function() { return false; }; });这种逐个Hook的方法在“代码阅读”阶段很有用,但在“样本实际运行”阶段我觉得最靠谱的还是:直接不触发检测条件——不要在模拟器里开Root标志、不要装Xposed全家桶、用Frida的时候用frida-server而不是Xposed。
6.2 加固、混淆与动态脱壳
现在市面上主流的安卓加固方案(如360、腾讯乐固、爱加密等)都有对应的脱壳方案。第一篇文章不展开讲脱壳,但我先把我踩过的坑说出来:脱壳机不是一键就能出的,很多时候脱壳出来的dex内存是残缺的,还需要配合修复工具做DEX修复,这个过程很消磨耐心。
遇到加固样本时,我的建议是“能绕则绕”:
- 先看样本是不是有“未加固版本”的上线更新检测,有时候老的版本没加固,新版本加了,你分析旧通知就行
- 优先用Frida做运行时HOOK,基里糊涂地绕过硬校验直接看运行时行为
- 脱壳机出来之后需要修复,修复里最核心的是重新计算DEX的checksum和结构对齐,用现成的脚本是可以搞定的
6.3 反调试与Anti-Frida技巧
真正高端的样本会直接检测Frida的特征,比如检查/data/local/tmp/frida-server路径是否存在、扫描进程列表里的frida进程、甚至检查特定端口(27042)是否被监听。针对这类样本,我的建议是:
- 使用
frida-server改名,比如改成crash、systemd之类的合法名称 - 用
frida -H远程模式,把frida-server放在一台单独的机器上,与样本跑在同一个设备,但进程列表里看不到frida - 用
r0capture这类不依赖Frida的解法做脱壳
7. 实战案例复盘:一个模拟远控木马的分析过程
7.1 样本基本信息
下面用我之前分析过的一个模拟项目X的远控木马样本,完整走一遍整个分析流程。这是一个伪装成某办公软件的APP,包名叫com.system.update,一眼看上去人畜无害。
JADX打开manifest,看到申请的权限里有:
- 读取联系人
- 读取短信
- 获取定位
- 发送短信
- 安装其他应用
- 系统设置修改
一个办公软件要发送短信和安装应用?这个权限组合开了一个很不好的头。
7.2 静态阶段发现的核心线索
在JADX里搜索Runtime.getRuntime().exec,找到了一个非常可疑的函数,代码大概是这样的:
private void execShell(String cmd) { try { Process p = Runtime.getRuntime().exec(cmd); // ... } catch (Exception e) { } }继续追踪这个函数的调用来源,发现它被一个监听短信的BroadcastReceiver调用。也就是说,这个样本在收到特定的短信指令后,会执行系统命令。这已经是教科书级的远控木马行为了。
7.3 动态阶段的关键证据
样本启动后,Charles里很快就看到它有一个POST请求到https://update.xx-xxxxx.com/api/v1/report,请求的Body是一串加密过的JSON密文。Frida hook解密函数拿到明文内容后发现,里面包含了设备的IMEI、手机号码、通讯录前10条记录的全部内容。
这还不够,继续观察运行行为,发现样本在收到服务器返回的一条指令序列后,会触发DexClassLoader动态加载一个下发的恶意dex。这个dex才是完整的勒索模块,会扫描存储卡上的文件并加密。
整个链条到这里就非常清晰了:诱骗安装->窃取个人信息上传->接收远程指令->下载执行勒索模块。这已经是一个完整的多阶段攻击链。
8. 常见问题与排查技巧实录
8.1 问题速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 样本安装后打开闪退 | 缺少依赖库,或检测到模拟器环境 | 用真机分析,或修改模拟器特征 |
| Frida连接不上模拟器 | 版本不匹配、端口被占 | 检查frida-server版本与客户端一致,换端口 |
| 抓包抓不到HTTPS内容 | SSL Pinning未绕过 | 用Frida hook证书校验函数 |
| 样本运行后没有任何行为 | 依赖特定环境(SIM卡、通讯录) | 在模拟器里模拟真实用户环境 |
| 脱壳后的dex无法反编译 | DEX结构损坏 | 用修复工具修复checksum和结构对齐 |
| 网络流量一片空白 | 网络代理配置错误 | 检查模拟器代理设置与宿主机端口 |
8.2 分析流程中的几个关键时间点
我分享一下我分析样本时最常记录的“关键时间点”:
- 安装时:包名是不是正常签名?安装弹窗有没有诱导词?
- 首次启动时:有没有申请敏感权限?有没有先弹出隐私政策?(恶意样本经常伪造隐私政策走形式)
- 点击主要功能时:点击按钮后,流量里发生了什么事?
- 收到特定指令时:比如来短信、接入USB、连上WiFi,这类事件触发下样本是什么反应?
把每个关键时间点记录下来,分析完成后你会得到一张完整的“行为时间轴”,这就是报告的大纲。
8.3 心态调整:分析样本是在拼耐心
安卓病毒分析不是一场闪电战,而是一场持久战。我见过太多人拿一个棘手样本分析了一个小时就放弃了,要么说“这个样本太高级了”,要么说“工具不行”。实际上,大多数时候是耐心不够、方法不对。
我自己分析最久的一个样本,前前后后花了两周时间。验证下来发现它就是简单的加固加反调试,难点全在于“反调试”这一层。一旦绕过了反调试,里面内容非常简单。所以遇到卡住的时候,别急着换工具,先回想一下:是不是漏了某个检测、或者某个环境条件没满足。
写在最后
这一篇先把安卓病毒分析的基础流程理顺了:搭建环境、静态解剖、动态监控、流量取证、行为判定、对抗思路,再到一个完整案例的复盘。整个流程跑通一遍之后,你基本就具备了独立分析一个普通恶意样本的能力。
下一篇我会专门讲“脱壳实战”,会把常见商业壳的脱壳方法、DEX修复原理和Frida在脱壳中的高级用法一一展开。这个领域最深的水就在脱壳和反混淆里,但有了这一篇的地基,你会走得稳很多。
最后分享一个我个人的习惯:每分析完一个样本,把所有用到的命令、脚本、遇到的坑、突破的关键点整理成一份笔记。这个笔记既是积累也是武器,下次再遇到同类型样本,直接照着笔记推进,速度能快一大截。