为什么 Web 抓包一套流程走天下,App 抓包却要 root、要过检测、要上 Frida? 答案一句话:Web 环境信任由浏览器管理,App 环境信任由开发者代码管理。开发者不想让你抓,你就要跟它的检测机制打一场攻防战。
本文仅面向授权环境下的安全测试与学习,介绍检测机制的构成与绕过思路,不构成对任何未授权系统的利用指导。
一、为什么 App 抓包难:三个检测维度
抓包的本质是让流量经过你、并让你看到明文。Web 场景下这两件事都由系统说了算(代理设置 + 证书信任链),所以 Burp 一套标准流程直接拿下。App 场景下,开发者可以通过代码主动对抗,全部落在这三个维度:
| 维度 | 检测什么 | 对抗什么 |
|---|---|---|
| 信任层 | 证书是否被替换 | SSL Pinning(不认系统 CA,只认硬编码指纹) |
| 网络层 | 流量是否走了代理 | 代理检测、TCP 直连绕过系统代理 |
| 设备层 | 环境是否被”动过” | root 检测、Frida 检测、调试检测、模拟器检测 |
三关全过,才回到 Web 那种”装证书就能抓”的状态。 而最要命的是这三关互相耦合:过 Pinning 通常需要 Frida → 上 Frida 触发 Frida 检测 → 对抗 Frida 检测又往往依赖 root 环境(装系统证书、注入工具)→ root 又触发 root 检测。所以 App 抓包是一个连环套,不是单点问题。
二、基础抓包:Android 证书体系与代理
1. 证书装哪:系统 CA vs 用户 CA
Android 7.0(API 24)起,App 默认只信任系统 CA,不信任用户安装的 CA。这意味着:
- 你把 Burp 证书装到”用户证书”区 → 大多数 App 照样报证书错误
- 解决方案有三条路:
- root 后装进系统证书区(
/system/etc/security/cacerts/)——最通用 - 改 APK 的
network_security_config.xml,允许信任用户 CA——免 root 但破坏签名 - Frida 层面绕过证书校验(见第五节)
- root 后装进系统证书区(
2. 代理检测与透明代理
App 可以检测系统代理设置(Settings.Global.HTTP_PROXY),检测到就直接不走代理或拒绝联网。绕过思路是透明代理:
1 | App(不知道有代理)──TCP 直连──▶ iptables 重定向──▶ Burp 透明模式──▶ 服务器 |
原理:用 iptables 把 App 进程的出站流量重定向到 Burp 的透明代理端口,App 根本感知不到代理存在。工具:Burp 的 Invisible Proxy 模式 + iptables REDIRECT,或 ProxyDroid 这类工具。这条路的本质是在网络层截流,不依赖 App 配合。
3. 小程序为什么等于 Web 抓包
小程序跑在微信容器里,网络栈是 WebView 内核——证书信任和代理都遵循系统规则,不搞特殊。所以 Burp 一套标准流程直接抓,和抓网页没区别。这也是为什么移动端渗透测试里,小程序是最好打的突破口。
三、加固原理:App 为什么”抓不动”
1. 加固是什么
加固(加壳)的核心动作:把原始的 dex/so 加密隐藏起来,运行时再解密还原执行。目的是防逆向、防二次打包、防业务逻辑被脱端重写。京东安全小课堂把 Android 加壳技术按强度分为 4 代:
| 代际 | 技术 | 说明 |
|---|---|---|
| 第 1 代 | 整体 dex 加壳 | 把 dex 加密藏进壳,运行时解密 |
| 第 2 代 | 防调试防 Dump | 加反调试,防止内存 dump 出 dex |
| 第 3 代 | 方法体抽离 | 方法代码抽走,运行时回填 |
| 第 4 代 | VMP 加壳 / so 加壳 | 字节码转虚拟机指令,so 层也加固 |
关键认知:无论哪一代,运行时解密这一步躲不掉——所以即使是 VMP 壳,理论上也能在内存中修复、dump 出完整 dex。这就是”脱壳”的原理:脱壳不是破解加密,而是等它自己解密时把内存快照拿走。
2. 加固厂商格局
| 类型 | 厂商 |
|---|---|
| 免费 | 360 加固、百度加固、腾讯乐固、网易易盾、阿里聚安全 |
| 商业 | 爱加密(双 VMP:dex+so)、梆梆安全、顶象、娜迦 |
其中网易易盾、梆梆、360 属于把检测做到”又早又深”的顶级壳:签名校验早于 Java 桥可用、检测逻辑 native 化 + 内联 + 自实现加密,免 root 的常规绕过手段基本全部堵死(详见第五节)。
四、检测机制全貌:安全 SDK 在检测什么
这是本文的核心。从 Frida 学习笔记两篇实战(招行三套 SDK、网易易盾)和加固原理中,可以提炼出一张完整的检测机制地图:
1. Root 检测(RootBeer 系)
开源 RootBeer 的 7 项子检测,按调用方式分三类:
| 类型 | 检测手段 | 易漏点 |
|---|---|---|
| 包名扫描 | 枚举已安装应用,找 Superuser.apk / Magisk 管理器 | 无 |
| 文件探测 | stat/access/fopen 探测 /system/xbin/su 等 |
用 fopen 探测的路径,hook stat 系列会漏 |
| 命令执行 | Runtime.exec("which su") |
数组形式 exec(new String[]{"which","su"}),只查 cmds[0] 会漏 |
| 系统属性 | Build.TAGS 含 test-keys、ro.secure、ro.debuggable |
无 |
2. Frida 检测(四条独立路径)
招行 CmbShield 的反 Frida 由 4 条路径并联,两主动两被动:
| 路径 | 性质 | 原理 | 对策 |
|---|---|---|---|
| #1 anti_thread | 主动 | 开机拉线程扫描进程内所有线程,发现 frida worker 用栈耗尽攻击破坏 | 静态 patch 调度器 |
| #2 kill_inject | 主动 | hook dlopen,加载含 frida 特征库时用 mprotect 把 agent 内存页设成不可执行 | patch 检测点返 0 |
| #3 maps 扫描 | 被动 | 读 /proc/self/maps,匹配 frida / memfd:frida-agent |
hook fread 行级净化 |
| #4 dl_iterate_phdr | 被动 | 遍历 linker 的 solist 链表(不读文件,绕过 maps 净化) | hook 回调包装器 |
另加常见检测面:
- 线程名扫描:
/proc/<pid>/task/*/comm找gum-js-loop、gmain - D-Bus 协议握手:连接 27042 端口发 AUTH 字符串
- 内存指纹:扫描进程内存找
LIBFRIDA字节序列
3. 调试检测
- ptrace 反调试:检测
/proc/self/status的TracerPid,非 0 即被调试;或自己 ptrace 自己(anti-ptrace) - SIGTRAP 自检:给自己发信号,看 handler 是否被 Frida 拦截
- 硬件断点检测:检查调试寄存器
4. 签名校验(免 Root 的生死线)
1 | 取签名证书(getPackageInfo)→ 算 MD5 → 与写死的期望值比对 → 不一致直接 exit 自杀 |
关键特性:又早(+0.2s 跑完,早于 Frida 的 Java 桥可用)、又 native(不经标准库)、又 inline(比较内联在自实现加密代码里)——窗口期内没有干净拦截点。这就是网易易盾这类顶级壳”免 Root 实际绕不过”的原因。
5. 主动反击型检测
比被动检测更狠:不止”发现你”,而是直接破坏你的工具:
anti_thread:发信号让 frida worker 栈炸裂kill_inject:mprotect 把 agent 代码页设为不可执行 → 下次执行直接 SIGSEGV- 检测与弹窗解耦:检测命中写信号文件,等主线程跑到某处再读文件弹窗——hook 弹窗调用栈看到的是一条干净 Java 栈,完全追不到 native 检测点
- 周期轮询:30~150 秒再扫一遍,绕过了过一会儿又弹
6. 检测架构:多 SDK 并联
招行是典型:CmbShield(自研 native)+ RootBeer(开源改装)+ DEC(动态加载 dex 的第三方)三套独立并联,各自检测、各自弹窗、互不共享结果——拆一套,另两套照响。这是大厂 App 的标准姿势。
7. Pinning 与代理检测:流量层的两道闸
上面六类检测的是”环境”,还有两道闸直接卡流量本身:
- SSL Pinning(证书固定):App 不信任系统 CA 列表,而是硬编码服务器证书的指纹/公钥,甚至把校验下沉到 native(conscrypt 回调、自实现校验)。Burp 的假证书即使被系统信任,App 也不认——这是”抓不到包”最常见的直接原因
- 代理检测:App 读系统代理设置(
Settings.Global.HTTP_PROXY)判断流量是否被转发;更硬核的会检测代理特征(如 HTTP 代理的响应头/握手行为)。检测到就直接不走代理,或干脆拒绝联网
这两道闸的绕过方法见第五节的第 1、6 小节,与上面的环境类检测形成完整攻防闭环。
五、对抗手段:逐检测绕过手册
拿到一个陌生 App,先按下面的层级从便宜到贵试,再对着检测类型逐个拆:
总览:五级分层
| 级别 | 方案 | 成本 | 覆盖率 | 适用 |
|---|---|---|---|---|
| L1 | 改名 + 改端口 + spawn 模式 | 1 分钟 | 60% | 中小 App、无加固 |
| L2 | 改名 frida-server + signalProbe 过 SIGTRAP/线程名自检 | 即时 | 75% | 信号/线程名级检测 |
| L3 | 抢”干净窗口”attach + 晚挂分离检测面 | 30 分钟 | 85% | 受隐私门控、同意后才全量检测 |
| L4 | r2 反查 exit + 12-NOP + 静态补丁检测函数返 0 | 1 小时 | 92% | 网易易盾级(静态补丁) |
| L5 | 静态 patch 调度器 + 环境伪装 + Stalker/指令级 trace + strongR | 半天~一天 | 98% | 招行三 SDK、主动反击型 |
1. 绕过 SSL Pinning
Pinning 的本质是”App 只认自己写死的证书/公钥”。从通用到硬核:
① 通用 Frida 脚本(Java 层)——覆盖绝大多数 App:
1 | Java.perform(function () { |
② 一行命令(objection):
1 | objection -g com.target.app explore |
③ WebView 场景:WebViewClient.onReceivedSslError 里调用 handler.proceed()。
④ native 层 Pinning:Java hook 不生效时,App 可能在 native 校验(conscrypt/自实现)。hook SSL_CTX_set_custom_verify / SSL_set_verify,把回调返回值改成 X509_V_OK。注意边界:conscrypt 这类系统库可以 hook;App 自研加固库的 .text 段别碰(改字节会触发 inline hook 完整性检测)。
2. 绕过 Root 检测
| 检测手段 | 绕过方法 |
|---|---|
| 包名扫描(Superuser/Magisk 管理器) | Magisk Zygisk DenyList + Shamiko:对目标 App 隐藏 root 存在 |
文件探测(fopen su 路径) |
Shamiko 统一隐藏;或 Frida hook stat/fopen,对 su 相关路径返回不存在 |
exec("which su")(数组形式) |
Frida hook Runtime.exec,检查参数数组,命中 which/su 返回空流 |
| 系统属性(test-keys / ro.secure) | Magisk 自带隐藏;或 hook System.getProperty 清洗 |
注意:root 检测优先用 Magisk 生态(DenyList/Shamiko)解决,而不是 Frida hook——hook 本身会触发 Frida 检测,等于拆东墙补西墙。环境干净永远是第一优先级。
3. 绕过 Frida 检测(重点)
针对招行/易盾两条实战路线的逐项打法:
| 检测手段 | 绕过方法 |
|---|---|
| maps 扫描(/proc/self/maps 找 frida/memfd) | Frida hook fread:逐行读时把含 frida 特征的行替换成无害行 |
| dl_iterate_phdr(遍历 solist) | hook 回调包装器,跳过含 frida 的 so |
| 线程名(gum-js-loop/gmain) | 改用 frida-gadget 注入(无这些线程);或自编译改线程名 |
| D-Bus 握手(27042 端口) | 用 strongR-frida-android(去 D-Bus 关键字的 frida-server 构建) |
| 内存指纹(LIBFRIDA 字节序列) | 改 frida 源码字符串后自编译 |
| SIGTRAP 自检 | signalProbe 脚本模拟信号处理,让自检认为”handler 未被拦截” |
| 静态检测函数 | 黄金窗口 patch(见下) |
| anti_thread / kill_inject(主动攻击) | 静态 patch 调度器:定位起检测线程/包装 dlopen 的调度函数,mov w0,#0; ret 直接废掉 |
黄金窗口 patch 具体操作:
1 | Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), { |
为什么这个窗口最干净:android_dlopen_ext 的 onLeave 时机,.init_array 静态构造已跑完(无害)、JNI_OnLoad 还没跑(反检测逻辑在它里面)——patch 完,防护库从加载起就全失活。
4. 绕过调试检测
- TracerPid 检测:frida-server 的 ptrace attach 会留下 TracerPid → 改用 ZygiskFrida(Zygote fork 注入,全程无 ptrace),或 spawn 模式配合处理
- anti-ptrace:patch 掉
ptrace(PTRACE_TRACEME)调用 - SIGTRAP 自检:同上面的 signalProbe
5. 绕过签名校验:最好不去碰它
- 免 Root 注入 = 改 APK = 重签 = 签名变了。对易盾/梆梆/360 这种”又早(+0.2s)、又 native、又 inline”的校验,时间竞赛必输——窗口期内没有干净拦截点
- 正确姿势:Root 下注原版 APK,不重签。签名还是原值,签名墙根本不出现。ZygiskFrida 在 Zygote fork 极早期注入,签名校验还没机会跑
- 只有软目标(无签名校验或校验晚)才考虑:hook
getPackageInfo返回原签名值
6. 绕过代理检测
| 场景 | 方案 |
|---|---|
| App 检测系统代理设置 | 透明代理:不设系统代理,用 iptables 在网络层截流 |
| App 无视代理、TCP 直连 | 同上,网络层强制接管,App 感知不到 |
命令骨架(root 环境):
1 | # 把目标 App UID 的出站 443 流量重定向到本机 Burp 透明代理端口 |
7. 环境伪装:在数据源处做手脚
每个检测最终都读某个”数据源”:文件、系统属性、进程列表、maps。环境伪装 = 在数据源处返回干净数据,让 App 跑完所有检测但全部判定通过:
- 文件层:Magisk DenyList / Shamiko 隐藏 root 相关路径
- 属性层:hook
System.getProperty清洗 test-keys - 进程层:隐藏 Magisk / Frida 进程
- 设备层:伪造代理状态、MAC、IMEI 等设备指纹
为什么环境伪装是首选而非 hook 弹窗(UI 兜底):伪装让 App 真实判定 clean,服务器侧风控事件不会上报;UI 兜底只挡住弹窗,risk 照常上报,服务器可能直接风控封号。方法论一句话:在数据源处伪装 > 逐层硬刚 > UI 兜底。
8. 工具链兜底
- strongR-frida-android:去指纹 frida-server(应对 D-Bus/内存指纹检测)
- ZygiskFrida:Zygote 极早期注入,无 ptrace、无 memfd、无独立进程(应对进程/TracerPid 检测)
- frida-gadget 嵌入原版 + DenyList:无 server 进程(应对进程扫描)
- unidbg:把 SO 搬进模拟器离线跑签名算法——彻底脱离真机反制(终局方案)
六、进阶技巧:硬仗怎么打
1. 真凶追踪法(检测点与弹窗解耦时)
大厂 App 检测命中后不是立刻弹窗,而是写信号文件、等主线程跑到某处再读文件弹窗——hook 弹窗看到的是一条干净 Java 栈,追不到 native 检测点。正确流程:
1 | hook UI 显示点(Instrumentation.execStartActivity) |
2. 跨 ClassLoader hook(动态加载的检测类)
加固 App 的检测类常是运行时动态加载的 dex(如招行 DEC 的 com.a.b.c.*),Java.use 默认找不到。解法:
1 | Java.enumerateClassLoadersSync().forEach(function (loader) { |
3. 快层反调试:只 hook 非安全库
网易易盾的”快层”认 Java/ART Hook(蹦床/入口篡改),检测到就 inline-svc 静默杀线程。对策:不碰 Java Hook、不碰安全库 .text 段,只 hook 非安全库的明文读写点(如 conscrypt/libssl 的 SSL_write / SSL_read)——直接拿到 API 明文,全程不触发检测。
4. 分层覆盖
native(syscall/libc)→ JNI → Java framework → 业务类。任一层漏拦都可能命中检测,检查时要逐层确认。
5. IO 关联 trace
Java 方法 onEnter 设全局标记,让 native hook 把 IO 事件关联到具体调用者——快速定位”这个加密 I/O 是哪个方法发的”。
七、实战决策:三类需求三条路
| 需求 | 方案 | 关键限制 |
|---|---|---|
| 我只有 App,没 root | 免 Root Gadget 重打包 | 只对软目标有效;易盾/梆梆/360 级签名校验此路不通 |
| 有 root,长期抓流量 | ZygiskFrida 或 frida-server 注原版 + 防护组件补丁 + native SSL hook | 需破运行时反调试;不要重签 |
| 还原签名算法、批量出参 | 真机抓一次 I/O → 转 unidbg 离线复现 | 终局方案:没真机、没 frida、无 on-device 反制可检测 |


