2
月
在社区成长 2 月
LEVEL 1
1 XP
一、背景
在移动端应急响应中,我们经常会收到来源不明的 APK:可能是仿冒应用、被二次打包的软件,也可能是携带广告插件、短信窃取、远程控制或下载器功能的恶意样本。仅凭杀毒软件的单次扫描结果,很难回答以下关键问题:样本申请了哪些高风险权限?哪些组件可以被外部调用?是否存在动态加载?敏感数据会发送到哪里?卸载后是否还会留下关联风险?
本文给出一套可复现的 Android APK 防御性分析流程,覆盖样本固化、静态逆向、组件攻击面检查、动态行为观察、网络取证和报告整理。所有操作仅用于自有 APK、授权靶场或隔离的本地实验样本,不应在真实用户设备、生产账号或未授权目标上测试。
二、分析原理
静态分析是在不运行样本的情况下检查 APK 的结构、清单、资源、DEX 字节码和原生库。它适合快速定位权限、导出组件、硬编码地址、可疑 API、加固特征与动态加载逻辑,但可能受代码混淆、反编译误差和运行时解密影响。
动态分析则是在受控环境中运行样本,记录进程、文件、日志、网络、权限调用和组件行为。它能够确认真实执行路径,但恶意代码可能通过延迟执行、模拟器检测、服务端开关或特定触发条件隐藏行为。
因此,可靠结论应尽量满足“静态证据与动态证据互相印证”。例如,静态代码中出现读取通讯录和 HTTP 上传逻辑,并不等于运行时一定发生上传;只有结合权限授予情况、调用栈、网络请求及落地文件,才能提高结论可信度。
三、前置条件
建议准备一台与办公网络隔离的分析主机,以及可随时恢复快照的 Android 模拟器。不要使用个人手机、真实 SIM 卡、常用 Google 账号或生产环境凭据。
常用工具包括:
1. Android SDK Platform Tools:adb、logcat、apkanalyzer。
2. JADX:查看 Java/Kotlin 反编译代码。
3. Apktool:解包资源、清单及 Smali。
4. mitmproxy:观察实验应用的 HTTP/HTTPS 流量。
5. MobSF:辅助生成静态与动态分析报告。
6. sha256sum、file、strings、readelf:样本固化和原生库检查。
7. 可选的 Frida:在自有或授权样本中进行运行时方法观测。
实验网络应限制样本访问内网地址。更稳妥的方式是使用仅主机网络,并将可疑域名重定向至本地模拟服务,避免样本连接真实控制端。分析前为虚拟机和模拟器创建快照。
四、分步骤实践
步骤 1:固化样本并记录来源
首先不要直接双击安装。将样本复制到只读分析目录,计算摘要:
sha256sum suspicious.apk
md5sum suspicious.apk
file suspicious.apk
unzip -l suspicious.apk | head -n 40
SHA-256 应作为报告中的主要样本标识。记录文件名、大小、获取时间、获取渠道和分析人员。若后续重新下载到同名文件,应重新计算摘要,不能仅凭名称判断是否为同一样本。
还可以检查 APK 是否能够正常解析:
apkanalyzer manifest application-id suspicious.apk
apkanalyzer manifest version-name suspicious.apk
apkanalyzer manifest version-code suspicious.apk
apksigner verify --verbose --print-certs suspicious.apk
签名证书不能直接证明开发者身份,但证书摘要有助于比较多个样本是否由同一密钥签署。若“官方版本”和“可疑版本”的包名相同、证书却不同,应重点调查是否存在二次打包。
步骤 2:解包并建立文件视图
分别使用 Apktool 和 JADX:
apktool d -f suspicious.apk -o apktool_out
jadx -d jadx_out suspicious.apk
Apktool 更适合查看 AndroidManifest.xml、资源、Smali 和原始目录结构;JADX 更适合阅读高级语言逻辑。反编译结果不一定完全准确,遇到语法异常、空方法或控制流错乱时,应回到 Smali 或 DEX 指令核对。
重点关注以下目录:
assets/:可能存放二阶段 DEX、配置、证书或加密数据。
lib/:原生 so 文件,可能隐藏核心逻辑。
res/xml/:常见于网络安全配置、FileProvider 路径、无障碍服务配置。
unknown/:Apktool 无法归类的附加文件。
可以先做一次关键词检索:
grep -RniE "DexClassLoader|PathClassLoader|Runtime\.getRuntime|ProcessBuilder|loadLibrary|AccessibilityService|DeviceAdminReceiver" jadx_out
grep -RniE "http://|https://|wss://|api_key|token|secret" jadx_out apktool_out/assets
关键词命中只是线索,不能单独作为恶意判定。例如,正常热更新框架也可能使用 DexClassLoader,合法客服应用也可能声明无障碍服务。
步骤 3:审计 Manifest 与组件暴露面
查看权限及组件:
apkanalyzer manifest permissions suspicious.apk
apkanalyzer manifest print suspicious.apk > manifest.txt
grep -nE "uses-permission|activity|service|receiver|provider|exported|permission" manifest.txt
优先检查:
1. READ_SMS、RECEIVE_SMS、READ_CONTACTS、RECORD_AUDIO、QUERY_ALL_PACKAGES 等敏感权限是否与业务匹配。
2. Activity、Service、Receiver、Provider 是否设置 android:exported="true"。
3. 导出组件是否受到 signature 级自定义权限保护。
4. Intent Filter 是否过于宽泛。
5. FileProvider 的 paths 配置是否暴露根目录或外部存储大范围路径。
6. 是否声明无障碍服务、设备管理器、通知监听或 VPN 服务。
例如,若某个导出 Service 没有权限保护,同时接收 Intent 中的 URL、文件路径或命令字段,就应继续跟踪其 onStartCommand、onBind 或对应 Binder 方法。防御修复通常包括:无外部调用需求时设置 exported=false;确需导出时增加 signature 级权限;对 Intent 参数执行类型、长度、协议和路径校验;禁止将调用方传入的字符串直接交给 Runtime.exec、WebView 或文件 API。
步骤 4:定位动态加载与原生代码
搜索动态加载相关调用后,检查加载文件来自哪里、是否校验完整性:
find apktool_out -type f \( -name "*.dex" -o -name "*.jar" -o -name "*.so" \) -print
find apktool_out/lib -name "*.so" -exec file {} \;
readelf -d apktool_out/lib/arm64-v8a/*.so
strings -a apktool_out/lib/arm64-v8a/*.so | grep -Ei "https?://|socket|exec|/proc/|frida|su"
正常应用也可能包含反调试字符串,因此应继续查看引用位置。对于 DexClassLoader,重点确认 DEX 是否来自网络下载、外部存储或 APK 内置资源,以及加载前是否执行可信签名或摘要校验。仅校验固定 MD5 并不能建立发布者信任,推荐使用数字签名验证,并将公钥固定在可信代码中。
步骤 5:构建隔离动态环境
恢复模拟器快照后安装样本:
adb install -r suspicious.apk
adb shell pm list packages | grep -i sample
adb shell dumpsys package com.example.sample > package_state.txt
安装前后分别记录软件包列表、进程和文件变化。不要授予全部权限,应先观察默认状态,再按测试用例逐项授予:
adb shell pm grant com.example.sample android.permission.RECORD_AUDIO
adb shell pm revoke com.example.sample android.permission.RECORD_AUDIO
权限授予必须与分析目的对应。对于短信、通话、通讯录等测试数据,应使用虚构内容,避免把真实隐私带入实验环境。
步骤 6:观察进程、日志和文件行为
启动前清空日志:
adb logcat -c
adb logcat -v threadtime > runtime_log.txt
在另一个终端观察进程:
adb shell pidof com.example.sample
adb shell ps -A | grep com.example.sample
adb shell dumpsys activity services com.example.sample
adb shell dumpsys jobscheduler | grep -A 15 com.example.sample
分别测试首次启动、拒绝权限、授予权限、切换前后台、重启设备和等待定时任务等场景。若应用在退出界面后仍保留前台服务、周期性 Job 或异常唤醒,应记录触发条件与时间。
对于可调试的自有测试应用,可检查其私有目录:
adb shell run-as com.example.sample ls -la
adb shell run-as com.example.sample find files shared_prefs databases -maxdepth 2 -type f
不要尝试越权读取其他真实应用的数据。重点检查 SharedPreferences、SQLite、缓存和日志中是否明文保存令牌、身份证号、短信内容或加密密钥。修复时应减少落盘数据,对确需保存的敏感信息采用 Android Keystore 保护密钥,并设置合理的缓存清理策略。
步骤 7:网络行为取证
将模拟器代理指向本地 mitmproxy:
mitmweb --listen-host 0.0.0.0 --listen-port 8080
adb shell settings put global http_proxy 10.0.2.2:8080
完成测试后恢复代理:
adb shell settings put global http_proxy :0
只应为受控模拟器安装测试 CA。Android 7 及以上版本默认不会让普通应用信任用户证书,若是自有测试应用,可通过 debug 专用 network_security_config 信任测试 CA;不要为分析而修改生产包并把修改结果误当作原始样本行为。
记录域名、IP、请求方法、路径、请求头、数据类型、响应和触发动作。不要只凭“访问陌生域名”判断恶意,应结合域名用途、证书、请求内容和业务上下文。若流量无法解密,仍可采集 DNS、连接目标、SNI、证书信息、包长和时序。对于可能的真实控制端,建议在网关阻断外联,并使用本地 Mock 服务返回安全响应,避免向对方暴露分析环境。
步骤 8:运行时方法观测
当静态代码严重混淆时,可以在授权样本中使用 Frida 观察关键方法的入参与返回值。例如,只记录实验应用发起的 URL:
Java.perform(function () {
var URL = Java.use('java.net.URL');
URL.$init.overload('java.lang.String').implementation = function (s) {
console.log(' ' + s); return this.$init(...时描述攻击面、触发条件、影响与修复方案,逆向分析报告才能真正服务于应急处置和产品安全改进。
在移动端应急响应中,我们经常会收到来源不明的 APK:可能是仿冒应用、被二次打包的软件,也可能是携带广告插件、短信窃取、远程控制或下载器功能的恶意样本。仅凭杀毒软件的单次扫描结果,很难回答以下关键问题:样本申请了哪些高风险权限?哪些组件可以被外部调用?是否存在动态加载?敏感数据会发送到哪里?卸载后是否还会留下关联风险?
本文给出一套可复现的 Android APK 防御性分析流程,覆盖样本固化、静态逆向、组件攻击面检查、动态行为观察、网络取证和报告整理。所有操作仅用于自有 APK、授权靶场或隔离的本地实验样本,不应在真实用户设备、生产账号或未授权目标上测试。
二、分析原理
静态分析是在不运行样本的情况下检查 APK 的结构、清单、资源、DEX 字节码和原生库。它适合快速定位权限、导出组件、硬编码地址、可疑 API、加固特征与动态加载逻辑,但可能受代码混淆、反编译误差和运行时解密影响。
动态分析则是在受控环境中运行样本,记录进程、文件、日志、网络、权限调用和组件行为。它能够确认真实执行路径,但恶意代码可能通过延迟执行、模拟器检测、服务端开关或特定触发条件隐藏行为。
因此,可靠结论应尽量满足“静态证据与动态证据互相印证”。例如,静态代码中出现读取通讯录和 HTTP 上传逻辑,并不等于运行时一定发生上传;只有结合权限授予情况、调用栈、网络请求及落地文件,才能提高结论可信度。
三、前置条件
建议准备一台与办公网络隔离的分析主机,以及可随时恢复快照的 Android 模拟器。不要使用个人手机、真实 SIM 卡、常用 Google 账号或生产环境凭据。
常用工具包括:
1. Android SDK Platform Tools:adb、logcat、apkanalyzer。
2. JADX:查看 Java/Kotlin 反编译代码。
3. Apktool:解包资源、清单及 Smali。
4. mitmproxy:观察实验应用的 HTTP/HTTPS 流量。
5. MobSF:辅助生成静态与动态分析报告。
6. sha256sum、file、strings、readelf:样本固化和原生库检查。
7. 可选的 Frida:在自有或授权样本中进行运行时方法观测。
实验网络应限制样本访问内网地址。更稳妥的方式是使用仅主机网络,并将可疑域名重定向至本地模拟服务,避免样本连接真实控制端。分析前为虚拟机和模拟器创建快照。
四、分步骤实践
步骤 1:固化样本并记录来源
首先不要直接双击安装。将样本复制到只读分析目录,计算摘要:
sha256sum suspicious.apk
md5sum suspicious.apk
file suspicious.apk
unzip -l suspicious.apk | head -n 40
SHA-256 应作为报告中的主要样本标识。记录文件名、大小、获取时间、获取渠道和分析人员。若后续重新下载到同名文件,应重新计算摘要,不能仅凭名称判断是否为同一样本。
还可以检查 APK 是否能够正常解析:
apkanalyzer manifest application-id suspicious.apk
apkanalyzer manifest version-name suspicious.apk
apkanalyzer manifest version-code suspicious.apk
apksigner verify --verbose --print-certs suspicious.apk
签名证书不能直接证明开发者身份,但证书摘要有助于比较多个样本是否由同一密钥签署。若“官方版本”和“可疑版本”的包名相同、证书却不同,应重点调查是否存在二次打包。
步骤 2:解包并建立文件视图
分别使用 Apktool 和 JADX:
apktool d -f suspicious.apk -o apktool_out
jadx -d jadx_out suspicious.apk
Apktool 更适合查看 AndroidManifest.xml、资源、Smali 和原始目录结构;JADX 更适合阅读高级语言逻辑。反编译结果不一定完全准确,遇到语法异常、空方法或控制流错乱时,应回到 Smali 或 DEX 指令核对。
重点关注以下目录:
assets/:可能存放二阶段 DEX、配置、证书或加密数据。
lib/:原生 so 文件,可能隐藏核心逻辑。
res/xml/:常见于网络安全配置、FileProvider 路径、无障碍服务配置。
unknown/:Apktool 无法归类的附加文件。
可以先做一次关键词检索:
grep -RniE "DexClassLoader|PathClassLoader|Runtime\.getRuntime|ProcessBuilder|loadLibrary|AccessibilityService|DeviceAdminReceiver" jadx_out
grep -RniE "http://|https://|wss://|api_key|token|secret" jadx_out apktool_out/assets
关键词命中只是线索,不能单独作为恶意判定。例如,正常热更新框架也可能使用 DexClassLoader,合法客服应用也可能声明无障碍服务。
步骤 3:审计 Manifest 与组件暴露面
查看权限及组件:
apkanalyzer manifest permissions suspicious.apk
apkanalyzer manifest print suspicious.apk > manifest.txt
grep -nE "uses-permission|activity|service|receiver|provider|exported|permission" manifest.txt
优先检查:
1. READ_SMS、RECEIVE_SMS、READ_CONTACTS、RECORD_AUDIO、QUERY_ALL_PACKAGES 等敏感权限是否与业务匹配。
2. Activity、Service、Receiver、Provider 是否设置 android:exported="true"。
3. 导出组件是否受到 signature 级自定义权限保护。
4. Intent Filter 是否过于宽泛。
5. FileProvider 的 paths 配置是否暴露根目录或外部存储大范围路径。
6. 是否声明无障碍服务、设备管理器、通知监听或 VPN 服务。
例如,若某个导出 Service 没有权限保护,同时接收 Intent 中的 URL、文件路径或命令字段,就应继续跟踪其 onStartCommand、onBind 或对应 Binder 方法。防御修复通常包括:无外部调用需求时设置 exported=false;确需导出时增加 signature 级权限;对 Intent 参数执行类型、长度、协议和路径校验;禁止将调用方传入的字符串直接交给 Runtime.exec、WebView 或文件 API。
步骤 4:定位动态加载与原生代码
搜索动态加载相关调用后,检查加载文件来自哪里、是否校验完整性:
find apktool_out -type f \( -name "*.dex" -o -name "*.jar" -o -name "*.so" \) -print
find apktool_out/lib -name "*.so" -exec file {} \;
readelf -d apktool_out/lib/arm64-v8a/*.so
strings -a apktool_out/lib/arm64-v8a/*.so | grep -Ei "https?://|socket|exec|/proc/|frida|su"
正常应用也可能包含反调试字符串,因此应继续查看引用位置。对于 DexClassLoader,重点确认 DEX 是否来自网络下载、外部存储或 APK 内置资源,以及加载前是否执行可信签名或摘要校验。仅校验固定 MD5 并不能建立发布者信任,推荐使用数字签名验证,并将公钥固定在可信代码中。
步骤 5:构建隔离动态环境
恢复模拟器快照后安装样本:
adb install -r suspicious.apk
adb shell pm list packages | grep -i sample
adb shell dumpsys package com.example.sample > package_state.txt
安装前后分别记录软件包列表、进程和文件变化。不要授予全部权限,应先观察默认状态,再按测试用例逐项授予:
adb shell pm grant com.example.sample android.permission.RECORD_AUDIO
adb shell pm revoke com.example.sample android.permission.RECORD_AUDIO
权限授予必须与分析目的对应。对于短信、通话、通讯录等测试数据,应使用虚构内容,避免把真实隐私带入实验环境。
步骤 6:观察进程、日志和文件行为
启动前清空日志:
adb logcat -c
adb logcat -v threadtime > runtime_log.txt
在另一个终端观察进程:
adb shell pidof com.example.sample
adb shell ps -A | grep com.example.sample
adb shell dumpsys activity services com.example.sample
adb shell dumpsys jobscheduler | grep -A 15 com.example.sample
分别测试首次启动、拒绝权限、授予权限、切换前后台、重启设备和等待定时任务等场景。若应用在退出界面后仍保留前台服务、周期性 Job 或异常唤醒,应记录触发条件与时间。
对于可调试的自有测试应用,可检查其私有目录:
adb shell run-as com.example.sample ls -la
adb shell run-as com.example.sample find files shared_prefs databases -maxdepth 2 -type f
不要尝试越权读取其他真实应用的数据。重点检查 SharedPreferences、SQLite、缓存和日志中是否明文保存令牌、身份证号、短信内容或加密密钥。修复时应减少落盘数据,对确需保存的敏感信息采用 Android Keystore 保护密钥,并设置合理的缓存清理策略。
步骤 7:网络行为取证
将模拟器代理指向本地 mitmproxy:
mitmweb --listen-host 0.0.0.0 --listen-port 8080
adb shell settings put global http_proxy 10.0.2.2:8080
完成测试后恢复代理:
adb shell settings put global http_proxy :0
只应为受控模拟器安装测试 CA。Android 7 及以上版本默认不会让普通应用信任用户证书,若是自有测试应用,可通过 debug 专用 network_security_config 信任测试 CA;不要为分析而修改生产包并把修改结果误当作原始样本行为。
记录域名、IP、请求方法、路径、请求头、数据类型、响应和触发动作。不要只凭“访问陌生域名”判断恶意,应结合域名用途、证书、请求内容和业务上下文。若流量无法解密,仍可采集 DNS、连接目标、SNI、证书信息、包长和时序。对于可能的真实控制端,建议在网关阻断外联,并使用本地 Mock 服务返回安全响应,避免向对方暴露分析环境。
步骤 8:运行时方法观测
当静态代码严重混淆时,可以在授权样本中使用 Frida 观察关键方法的入参与返回值。例如,只记录实验应用发起的 URL:
Java.perform(function () {
var URL = Java.use('java.net.URL');
URL.$init.overload('java.lang.String').implementation = function (s) {
console.log(' ' + s); return this.$init(...时描述攻击面、触发条件、影响与修复方案,逆向分析报告才能真正服务于应急处置和产品安全改进。