简介:《Android APP 渗透测试方法》是一份面向移动安全测试人员、渗透测试学习者与移动应用开发者的 PDF 技术手册,系统梳理 Android 客户端安全测试的完整流程与方法论。文档从测试环境 SDK/工具准备讲起,内容覆盖数字签名检测、反编译检测、APK 解包与资源解码、smali 代码分析、odex 转换、so 库静态分析、XML 处理及应用完整性校验等关键环节,并针对代码混淆、加壳等场景给出风险判断和处理思路。资源为单文件 PDF,共 1 个文件,大小 15.27MB,合计 137 页,便于整体阅读和按章节索引。已有 727 人学习/下载,适合安全工程师和移动端开发者在应用安全评估、渗透测试及漏洞自查时作为对照手册使用。
1. 拿到 APK 先别急着装:这份 137 页的 Android 渗透测试笔记讲了什么
做 Android APP 渗透测试这行,最怕的不是目标加固得密不透风,而是拿到一个 APK 后不知道从哪下手。我自己早期的项目里就吃过亏:客户丢过来一个 APK 让我测,我打开就跑了个 drozer,结果连导出组件都没看全,更别说签名证书和自校验逻辑了。后来把整套动作梳理成固定流程,效率才上来。这份 137 页的 PDF《Android APP 渗透测试方法》就是干这个用的——它不是泛泛讲概念,而是把测试环境、签名检测、反编译、完整性校验、组件安全、drozer 利用整条链路按步骤写清楚了,每一步都有命令、有判定标准、有风险结论。适合三类人:一是刚入行的安全测试工程师,照着命令能跑完一个标准检测;二是做 App 开发的,想看看自家应用在测试者眼里会暴露哪些问题;三是甲方安全岗,需要一份可执行的核查清单来评估外包团队交付的 App。
2. 环境与签名验证:先从 jarsigner 一行命令看发布者是谁
2.1 测试环境的取舍:JDK 版本和工具链怎么配
这份 PDF 的环境清单很直白:Java JDK、Android SDK、7zip、dex2jar、jd-gui、apktool、IDA Pro、010 editor、SQLite Studio、ApkIDE。我实际搭环境时有一些调整,但核心里面没变。JDK 用 1.8 就够了,原文里的命令路径就是C:\Program Files\Java\jdk1.8.0_111\bin\jarsigner.exe,这是 Windows 环境,如果你在 Linux 上测,改成对应路径或者直接加入 PATH 即可。Android SDK 主要是为了拿 adb、aapt 和 android.jar,这三样后面都会用到。
工具安装有个先后顺序:先保证 JDK 和 Android SDK 能用,再装 apktool 和 dex2jar。因为 apktool 依赖 JAVA_HOME 环境变量,dex2jar 也是个批处理脚本,环境变量不对会直接报找不到命令。还有一个容易忽略的点:apktool 需要下载 wrapper jar 包而不是直接用源码,否则apktool d命令根本不会生效。我一般把 apktool.jar、dex2jar、jd-gui、signapk.jar 放在同一个工具目录里,后面跑反编译和重打包流程时不用来回切路径。
# 验证 Java 环境 java -version # 验证 Android SDK 里的 adb adb version # 验证 apktool 可执行 apktool --version这段命令逻辑很简单:三步确认三个基础组件可用,任何一个输出版本信息失败,先解决环境问题再做后续测试,省得后面所有命令都连环报错。PDF 里还提到了 ApkAnalyser 和 ApkIDE,这类图形化工具适合快速看结果,但自动化测试时我还是优先用命令行,方便把输出重定向到日志里留痕。
2.2 jarsigner 验签与证书风险分级
签名检测是整个渗透测试里最先做的一步,也是很多测试者会跳过去的一步。PDF 里给的命令是jarsigner -verify APK文件路径 -verbose -certs,输出“jar 已验证”表示签名正常。但通过验证只是第一步,更要紧的是看证书的 CN 字段。CN 是证书里的 Common Name,代表发布者身份,一份正常的商业 App 应该用公司主体证书签名,证书里 CN 和公司注册名能对上。
jarsigner -verify CQRCBank.apk -verbose -certs输出里会出现CN=Beijing XXX Technology Co., Ltd.这样的字段,这时需要和客户确认这是不是他们的正式证书。PDF 里给了很清晰的风险分级逻辑:用直接客户的证书签名才算安全;Debug 证书、第三方开发方证书一律视为风险。这个判断标准很实用,因为很多内测包用的是androiddebugkey签的名,这种证书任何人都能拿到,APK 可以被随意重打包再签名,毫无防篡改能力。
| 证书类型 | 判定结果 | 风险说明 |
|---|---|---|
| 客户公司正式证书 | 安全 | CN 与发布者身份一致 |
| Debug 证书 | 风险 | 常见于开发阶段,可被任意重签 |
| 第三方/开发方证书 | 风险 | 发布者身份不明,需与客户确认 |
| 自签名证书 | 视情况 | 需结合业务场景评估 |
2.3 debuggable 与 allowBackup:两个容易被放过的 Manifest 开关
PDF 里专门列了 debug 模式和应用程序数据可备份两个检测项,这两个问题在真实项目里出现频率非常高,而且都是 AndroidManifest.xml 里一行属性的事。android:debuggable="true"意味着应用可以被 jdb 直接调试,测试者能附加到进程上读写内存、修改业务逻辑,这种应用发布出去基本等于裸奔。
检查方法有两个:一是用 MobSF 这类静态扫描工具,二是直接打开反编译出来的 AndroidManifest.xml 看属性。我一般两个都做,MobSF 跑全量扫描,手检做二次确认。MobSF 部署用 Docker 一条命令就能跑起来,扫描结果里会把 debuggable、allowBackup、exported 组件这些高危项都列出来。
android:allowBackup这个属性默认值是 true,这是个非常隐蔽的坑。很多开发根本没意识到要显式关掉它。allowBackup 开启时,攻击者可以用 adb backup 把应用数据导出,数据库、SharedPreferences、本地缓存全都能拉走,如果里面有明文 token 或敏感配置,直接翻车。
<application android:allowBackup="false" android:debuggable="false" ...> </application>这是修复后的标准配置。allowBackup 设为 false 能防止 adb backup 和 adb restore,debuggable 设为 false 能防止调试器附加。注意设置了 debuggable 为 true 的应用,即使测试结束后改成 false,也要确认是重新签名发布而不是直接用旧包,否则签名不一致会导致安装失败。PDF 里强调 stop testing 的条件之一就是发现 allowBackup 开启,可见这个问题在甲方评审里的份量。
3. 反编译与改包:把 APK 拆到能看到源代码为止
3.1 dex2jar + jd-gui:不混淆就没秘密
反编译是 Android 渗透测试的核心动作,几乎所有后续分析都建立在能看到代码的基础上。最经典的路径是:把 APK 后缀改成 zip 解压,拿到 classes.dex,然后用 dex2jar 把它转成 jar,最后用 jd-gui 打开 jar 看 Java 代码。
# 改后缀解压 mv target.apk target.zip unzip target.zip -d apk_src # dex2jar 转换 dex2jar.bat apk_src/classes.dex # 输出 classes-dex2jar.jar 后用 jd-gui 打开命令背后的逻辑是:APK 本质是 zip 包,classes.dex 是 Dalvik 字节码,JVM 不直接认识这种格式,所以要先转成标准 class 文件再反编译。dex2jar 参数里不需要额外配置,默认输出文件名就是 classes-dex2jar.jar。如果 APK 有多个 dex,比如 multidex 的 classes2.dex,要对每个文件单独执行一次转换。
PDF 里有个很重要的提示:有时候 apktool 能解出 smali,但 dex2jar 会失败。这个我遇到过很多次,多半是某些加固壳或特殊混淆手段导致 DEX 结构异常。遇到这种情况不要死磕 dex2jar,退回 smali 分析反而更有效。反编译结果的判定逻辑是:如果完整恢复了可读的 Java 代码且没有混淆,不安全;如果代码被混淆或加壳,字段名变成 a、b、c 这类无意义名字,或者核心逻辑被抽到 so 库里,算安全。注意这里说的“安全”是相对的,指的是代码抵抗逆向的能力。
3.2 apktool 拆包:smali、资源和 Manifest 同时到手
apktool 是反编译工具里最能打的一个,它能把 APK 拆成四部分:smali 汇编代码、解码后的 AndroidManifest.xml、res 资源目录、以及原始签名信息。拆包命令格式是apktool d[ecode] [OPTS] <file.apk> [<dir>]。
# 标准拆包 apktool d CQRCBank_2.1.1.1121.apk -o output_dir # 只拆资源不反编译 dex,速度快很多 apktool d CQRCBank_2.1.1.1121.apk -s -o res_only # 只反编译 dex 不处理资源,避免重打包时资源报错 apktool d CQRCBank_2.1.1.1121.apk -r -o smali_only三个命令对应三种场景。标准拆包用在完整分析时,-s适合只要资源文件和 Manifest 的场景,-r适合只改 smali 代码的场景。PDF 特别强调了一个经验:如果只需要改 smali 代码,尽量加-r不解码资源,因为解码后的资源在回编时经常出问题(aapt 报各种错),这是无数人踩过的坑。
拆包完成后,smali 文件夹里就是 Dalvik VM 汇编代码。smali 虽然比 Java 代码难读,但逻辑结构直观,变量类型、方法调用、权限判定都能看出来。我在分析恶意逻辑时反而经常用 smali,因为有些混淆只对 Java 层有效,smali 层的信息伪造难度更大。
3.3 odex 与 so 库:真机厂商包怎么啃
从手机里直接拉出来的 APK 往往没有 classes.dex,而是 odex 文件——这是系统对 dex 优化后的产物,主要出现在 /system/app 和 /system/framework 里。直接拿 baksmali 反编译会说格式不对,必须先转回 dex。
# 第一步:baksmali 把 odex 解析为 smali java -jar baksmali.jar -x /path/to/classes.odex -d /path/to/framework/ # 第二步:smali 重新打包成 dex java -jar smali.jar out/ -o classes.dex这里有个关键细节:-d参数指定的 framework 路径不能随便填。odex 是在特定 Android 版本上生成的,对应的 /system/framework/ 目录下有一堆 jar 文件,baksmali 需要它们来解析系统 API 引用。如果 odex 是 Android 4.4 真机生成的,就复制那台真机的 framework 文件;如果是模拟器生成的,就对应模拟器的版本。版本不匹配会直接抛异常,后面避坑章节我会具体说。
so 库的处理相对简单。APK 解压后进 lib/armeabi/ 目录,把 .so 文件拖进 IDA Pro 就能静态分析。so 库通常是加密算法、核心校验逻辑的藏身处,PDF 里说能看到导入导出函数和 ARM 汇编。我的一般做法是先看导出函数名,如果出现check_signature、native_validate这类名字,基本就能锁定自校验逻辑的位置。
3.4 xml 解码与 aapt 资源重编:改资源文件时的关键动作
APK 里的 xml 大多数是二进制格式,直接打开是乱码,需要解码才能查看和修改。有两条路:一条是用 apktool 整体解包,另一条是用 AXMLPrinter 或 APKParser 单独处理某个 xml。
# 用 AXMLPrinter 解码 AndroidManifest.xml java -jar AXMLPrinter.jar AndroidManifest.xml > manifest_readable.txt # 用 aapt 重新编译资源 aapt.exe package -f \ -M apk-src/AndroidManifest.xml \ -I "ANDROID-SDK/platforms/android-19/android.jar" \ -S apk-src/res \ -F output.zipaapt 这步是重打包流程里的核心。-M指定 Manifest,-I指定对应版本的 android.jar 以解析资源引用,-S指定 res 目录,-F指定输出 zip。PDF 里特意标注了这些路径对应 build-tools rev.20 和 android-19 platform,如果你装了更高版本的 SDK,路径要改。通常较新版本的 SDK 出错概率更小,我一般直接用最新 build-tools,但要注意 android.jar 版本和 targetSdkVersion 尽量接近,否则有些资源 ID 解析不出来。
关于单独编译单个 xml 这个问题,PDF 里明确说了目前没有发现可行方法。想改 xml 里的内容再塞回 APK,要么走 apktool 全量重打包,要么就手动调 aapt 编整个 res。
4. 完整性校验与组件安全:自校验测的是同一件事
4.1 重打包重签名:三步判定应用有没有自校验
应用完整性校验测试,说人话就是:把 APK 解开,随手改点东西,再重新打包签名,看应用还能不能正常运行。能跑,说明没有自校验,攻击者可以植入任何代码;不能跑或者弹警告,说明有自校验,篡改会被发现。
PDF 给的标准流程分四步,我做个小改动,把资源文件修改放在最前面:
# 第一步:拆包 java -jar apktool.jar d -f target.apk -o unpacked/ # 第二步:随便改一个资源文件,推荐改 logo,容易肉眼确认 # 把 res/drawable-hdpi/ic_launcher.png 换成另一张图 # 第三步:重新打包成未签名 APK java -jar apktool.jar b -f unpacked/ -o repack_unsigned.apk # 第四步:用 testkey 签名 java -jar signapk.jar testkey.x509.pem testkey.pk8 repack_unsigned.apk repack_signed.apk这套流程的逻辑核心是验证「改包后的安装运行状态」。改 logo 是最优选择,因为不用改代码,不会引入编译错误,而且一旦应用启动后显示的是自己换的图,结果一目了然。-d和-f参数分别指定输入和输出,-o指定输出路径。
安装时要注意一个坑:如果原 APK 签名和重打包后的签名不一致,无法覆盖安装。
# 先卸载再安装 adb uninstall com.example.target adb install repack_signed.apkPDF 里特别提醒:APK 必须签名才能安装,即使开启了“允许未知来源”,不签名也无法安装。Debug 证书、自签名证书、过期证书在允许未知来源下都能装上,但完全没签名的包不行。这与前面签名检测那章是呼应的——一个人可以随便生成 testkey 签名你的应用,所以签名保护本身只防君子不防小人,真正的防线是代码里的自校验逻辑。
4.2 组件可导出判断规则:三条就够了
组件安全测试是渗透测试的重点章节,PDF 把 Android 四大组件的导出规则浓缩成了三条判断逻辑,非常实用:
1. 显式声明 android:exported="true" → 可导出 2. 显式声明 android:exported="false" → 不可导出 3. 未显式声明 exported: a. 非 Content Provider 组件:有 <intent-filter> → 可导出;无 → 不可导出 b. Content Provider:SDK 版本 < 17 → 可导出;≥ 17 → 不可导出这条判断规则的来历是 Android 官方的组件默认行为调整,低版本系统里 Content Provider 默认对任何应用开放,这是个历史遗留坑。判断组件是否可导出只是第一步,能不能构成危害要结合源码分析。PDF 里的态度很中肯:测试时把所有导出组件列全,交客户开发侧确认哪些确实需要导出,而不是一看到 exported=true 就报漏洞。
组件安全测试工具可以辅助检测 exported 属性,但 PDF 明确指出:代码里动态注册的组件,这种工具测不出来,必须读代码。这也是为什么前面反编译分析那么重要——静态工具扫完,最终还是要靠人肉跟代码。
4.3 drozer 联动:Activity、Service、BroadcastReceiver 逐个试
drozer 是组件安全测试里的重型武器,PDF 附录里给了大量命令。它的思路是:从测试机向目标应用的导出组件发起调用,观察能否产生越权动作。
# 查看目标应用的 Activity 列表 run app.activity.info -a com.example.package # 直接启动某个 Activity run app.activity.start --component com.example.package com.example.package.welcome # 查看 Service 信息 run app.service.info -a com.example.package # 启动 Service run app.service.start --component com.example.package com.example.package.SomeService # 查看 BroadcastReceiver run app.broadcast.info -a com.example.package # 发送广播 run app.broadcast.send --component com.example.package --action android.intent.action.XXXActivity 测试的核心是「未授权访问」:如果某个导出 Activity 含有页面跳转逻辑,直接 am start 或 drozer start 拉起,看它是否跳过了登录态校验。Service 测试关注的是 onStartCommand 和 onHandleIntent 里有没有处理不当的数据——比如接收外部 Intent 后直接拼接 SQL 或写文件。BroadcastReceiver 测试除了看导出,还要重点看 onReceive 方法里是否对传入数据做了校验,很多 App 一收广播就崩溃,形成拒绝服务。
| drozer 模块 | 对应命令 | 风险关注点 |
|---|---|---|
| Activity | run app.activity.start | 未授权页面、越权跳转 |
| Service | run app.service.start | 数据泄露、后台启动异常 |
| BroadcastReceiver | run app.broadcast.send | 拒绝服务、信息泄露 |
| Content Provider | run app.provider.info | 数据读取、SQL 注入 |
PDF 里还提供了一个关键补充:动态注册的 BroadcastReceiver 不在 Manifest 里,需要反编译后检索registerReceiver()方法,同时留意setPackage和receiverPermission参数——这两个参数是防止广播被外部应用截获的关键。
5. 避坑与常见问题:签名、打包、odex 和抓包的五个现场
5.1 签名与安装环节的两个典型翻车
现象一:用 signapk 签完名的 APK,adb install 时报INSTALL_FAILED_UPDATE_INCOMPATIBLE,或者提示“应用未安装”。原因:原 APK 用的是应用商店签名或客户正式证书,signapk 默认的 testkey 签名和它不一致,Android 不允许覆盖安装不同签名的应用。解决:先adb uninstall卸载原应用再安装测试包,或者从系统里提取原签名证书,用 apkksigner 配合原证书重新签一遍修改后的包。注意做完整性校验测试时一定要保留原包备份,卸载后万一测试环境被破坏还能恢复。
现象二:重打包安装后一启动就闪退,Logcat 里报Signature check failed或Unable to get system security。原因:应用内置了签名校验逻辑,常见做法是在启动时调用getPackageManager().getPackageInfo()对比签名哈希,或者通过 so 库里的 native 方法校验。解决:这种情况说明自校验存在,安全测试可以直接判定篡改会被拦截。如果甲方要求更深入的验证,则需进一步定位校验点——IDA 打开 so 库搜signature、verify相关导出函数,或者用 Frida hook 掉校验函数再重打包。
5.2 反编译与打包环节的经典报错
现象三:apktool 重打包时报aapt: brut.common.BrutException,错误信息长且带 resource 相关字样,比如invalid resource reference。原因:拆包时解码了资源,回编时资源 ID 与当前 framework 版本对不上。常见于 targetSdkVersion 较高的 APK 用了较老版本的 apktool。解决:两条路,一是拆包时用-r参数不解码资源,直接改 smali 不回编资源;二是用apktool if framework.apk安装对应版本的 framework,再重新拆包。PDF 原文的解决思路就是这个——添加 Android 4.4.2 framework 文件后重试,现在的新版本 apktool 对高版本 SDK 兼容性好很多,但原理没变。
现象四:dex2jar 反编译直接报格式错误,或者生成的 jar 里大量方法显示为null。原因:多为加固壳或特殊混淆导致的 DEX 结构异常,也可能是 dex2jar 版本太旧不支持最新的 dex 格式。解决:先更新 dex2jar 到最新版,如果还不行,回到 apktool 解 smali 的路线。smali 虽然不好读,但信息完整度往往比硬解出来的 Java 代码高。一句话血泪经验:反编译工具是黑匣子,一个工具不行就换路线,不要在一棵树上吊死。
5.3 数据提取与抓包环节容易被忽略的坑
现象五:adb backup 明明执行了,输出文件也有,但用dd if=backup.ab | openssl zlib -d解出来是一堆不完整的数据,或者 Android 7.0 以上设备直接报Backup失败。原因:allowBackup 为 true 时备份接口仍然受设备锁屏密码保护;高版本系统又加强了备份数据的加密和完整性校验。解决:root 设备上绕过备份接口,直接从/data/data/com.example.package/目录拉取对应文件夹,这是更直接的路径。非 root 设备就老老实实看 allowBackup 是否显式关闭,关闭了证明测试点不成立。
代理抓包还有另一层坑:Android 7.0+ 默认不信任用户安装的 CA 证书,Burp 或 Charles 的证书装上去,HTTPS 流量照样解不开。这在 PDF 写的时候还不是普遍问题,但现在已经成为每个代理测试者必踩的门槛。常见做法是adb shell settings put global http_proxy host:port配置全局代理,再用adb reverse tcp:8080 tcp:8080做端口映射,同时把抓包工具 CA 证书装进系统证书目录(需要 root),或者用 objection 这类工具做 SSL Pinning 绕过。
6. 进阶技巧:一次完整的静态到动态核查路径
6.1 拿新包后的标准动作
这章我把 PDF 里的散点收拢成一条可复用的路径。先说结论:静态分析解决“有什么问题”,动态验证解决“问题能不能打透”,两者缺一不可。我处理陌生 APK 的日常动作是这样的。
# 第一步:静态信息采集 jarsigner -verify target.apk -verbose -certs # 签名信息 apktool d target.apk -o unpacked/ # 全量拆包 grep -E "exported|debuggable|allowBackup" unpacked/AndroidManifest.xml # 第二步:组件与代码分析 dex2jar.bat unpacked/classes.dex # jd-gui 打开 jar,重点看 exported 组件的入口类 # 第三步:动态验证 adb install target.apk adb shell am start -n com.example.package/.WelcomeActivity # 直连导出 Activity drozer run app.activity.start --component com.example.package这套动作的核心逻辑是:每一步的结果决定下一步的方向。签名有问题,优先补测重打包;exported 组件集中在某个 Activity 上,就把 drozer 的重点放在那个组件;反编译出来没混淆,那么加固和自校验大概率不存在,完整性问题可以快速跳过。验证方法本身也很直接:所有动态操作的结论都要在设备上确认现象——Activity 要看到页面跳转,Service 要在adb shell dumpsys activity services里看到进程启动,广播测试要看 Logcat 里有没有 setPackage 相关提示。
最后一个习惯,强制执行的程序:每个新包,我都先跑一遍静态采集,再定动态测试计划,不跳过任何一步,也不允许自己“猜”某个组件安不安全。文档里的规则是死的,但人的判断会漂,流程能兜住判断的偏差。这份 PDF 对我最大的价值,就是它把这些流程拆成了可复现的步骤,让我拿到手就能照着跑,跑完再根据结果纵深。希望帮到你。
本文还有配套的精品资源,点击获取