简介:一份面向安卓安全学习者的中间人攻击实验包,用于了解MITM攻击在移动端的常见形态及实验环境。压缩包共100个文件,大小仅5.38MB,内容以Java源码、XML布局、PNG图片为主,同时包含APK安装包、DEX执行文件、构建相关配置,以及arpspoof_x86、tcpdump_x86等安全测试相关可执行文件,适合在本地环境进行流量截获、协议分析或应用安全测试。资源内附AndroidManifest.xml、.project、README.md等工程说明,便于快速理清项目结构与构建流程;fastjson、commons-codec等依赖则有助于分析安卓应用的网络通信与数据解析逻辑。结合资源描述中关于HTTPS证书欺骗、完整性校验与端到端加密的要点,可帮助读者建立移动端通信安全防护意识。目前已有76人学习下载,适合对移动安全感兴趣、想动手搭建实验场景的初中级Android开发者或安全测试人员。
1. 安卓中间人攻击 mitm.zip:一句好记的咒语,也是抓到 app 流量的第一步
做安卓应用安全评估或逆向,开场的动作永远是同一件事:把 app 发出的 HTTPS 流量完整截下来。你可能会听见前辈说“拿 mitm.zip 过一遍就好了”——这个 mitm.zip 通常不是某个固定开源仓库,而是把中间人攻击(MITM)需要的证书、转换脚本、证书安装器、代理配置命令和 Frida 绕过脚本打包成的“安卓抓包工具箱”。它的目标是解决现代 Android 的证书信任限制、SSL Pinning 拦截、模拟器挂载只读分区这类组合问题,让你用一条命令完成从代理配置到流量解析的整个链路。适合应用安全测试工程师、反编译分析人员和需要自己打通抓包链路的开发者,前提是你只对已获授权的目标执行测试。
2. 中间人攻击实现基础:为什么你的抓包工具没流量
2.1 MITM 的真正障碍:TLS 证书信任,而不是代理连通性
很多人在安卓模拟器里给 WiFi 配好 HTTP 代理,随后发现抓包工具里只有一连串CONNECT请求,紧接着就是Connection reset或SSL error。这不是代理没通,而是 TLS 握手被掐断:代理后面的中间人服务器提供了自己的假证书,但安卓客户端不信任。
中间人攻击要成立,必须满足两个条件:一是攻击者确实能接管客户端和服务器之间的 TCP 数据流,这在安卓上通常用iptables重定向或常规 HTTP 代理实现;二是客户端会对攻击者提供的证书完成“信任链校验”。第二个条件才是绝大多数抓包工具抓不到包的真正原因。你需要的不是让代理可连通,而是让目标 app 信任一个由你掌握的 CA 签发的证书——也就是说,把私钥握在你手里,伪造出对目标域名有效的 TLS 证书。
2.2 Android 7 以后证书信任策略变化
从 Android 7(API 24)开始,系统默认只信任/system/etc/security/cacerts/下的系统证书。用户通过“设置 -> 安全 -> 安装证书”装进去的根证书,属于用户证书存储区,普通 app 默认不信任。例外只有两种:app 在network_security_config.xml显式声明信任用户证书,或者它的targetSdkVersion低于 24。到了 Android 9(API 28)及更高版本,targetSdkVersion >= 28时默认还禁用了明文 HTTP 流量。也就是说,只“装个证书”在模拟器里碰运气已经废了,必须把 CA 挪进系统证书存储区。
2.3 两种主流 MITM 引擎怎么选:mitmproxy 与 Burp Suite
| 对比维度 | mitmproxy | Burp Suite |
|---|---|---|
| 证书生成 | 首次运行自动生成~/.mitmproxy/mitmproxy-ca-cert.cer | 需要手动导出 DER 格式的 CA 证书 |
| 自动化能力 | Python 插件可改写请求、响应、拦截 WebSocket | Extender API 功能强,但编写和调试成本更高 |
| 运行形态 | 终端环境,适合脚本驱动、长时间挂机 | 图形界面,重放和状态码分析更顺手 |
| 资源占用 | 低,能连续跑几小时不卡 | UI 较重,堆大量并发流量时容易吃内存 |
| 安卓配合 | 命令行友好,容易封装成 adb 脚本 | 需要额外在 Burp 里设置监听端口,再与 adb 脚本对接 |
选型上我一般这么处理:自动化批量测试、需要长时间监听背景流量时用 mitmproxy,手动分析某个接口的请求头、篡改响应做重放时用 Burp。两者最终都要面对同一个门槛——系统证书信任。绕过这个门槛的通用方式是把 CA 放进/system/etc/security/cacerts/,并且文件名必须符合 AOSP 规则:取证书主题的subject_hash_old,再加.0后缀。
3. 手工搭建一套可复制到任意安卓的环境:mitm.zip 内容与生成
3.1 mitm.zip 的目录设计:需要哪些文件
把重复劳动固化成 zip 包,目录我会这样设计:
mitm/ ├── install.sh # 一键推证书 + 设置全局代理 ├── ca.crt # 导出的 DER 或 PEM 格式 CA 证书,用于导入 Burp/浏览器 ├── <hash>.0 # 安卓系统证书文件名,install.sh 会用到 ├── frida_bypass.js # 常用的 SSL Pinning 绕过脚本 ├── simulator_on.sh # 模拟器专用:写入系统证书 + 挂载可写分区 └── tools/ ├── adb # 平台工具 └── mitmdump # 或 mitmproxy 的静态二进制这些文件里最关键的是<hash>.0。它的名称不能随手取,必须用命令从 CA 证书里计算出来,否则安卓加载系统证书时根本不会识别它。
3.2 用 OpenSSL 生成自签 CA 并把证书格式转成 Android
先创建自己的 CA 私钥和证书,有效期建议 5 年,省得测试中途又要重新部署:
# 生成 CA 私钥和自签根证书 openssl req -x509 -newkey rsa:2048 -days 1825 \ -keyout ca.key -out ca.pem -nodes \ -subj "/CN=Android Test CA" # 计算 subject hash,生成 Android 系统证书文件名 HASH=$(openssl x509 -inform PEM -subject_hash_old -in ca.pem | head -1) cp ca.pem ${HASH}.0 # 导出 DER 格式,给 Burp 或系统设置界面用 openssl x509 -in ca.pem -outform DER -out ca.der-nodes表示私钥不加密,这样后续脚本推入设备时不需要额外输入密码;-subject_hash_old是 Android 官方工具openssl x509用来生成本地发行者哈希的参数,必须加上,直接sha256之类的指纹是不行的。得到的<hash>.0文件会同时包含证书和私钥吗?不会。这里生成的ca.key是单独的私钥文件,<hash>.0只包含公钥证书部分,但安卓系统校验这个文件时只需要公钥签名属性,CA 私钥继续安全保存在宿主机上供 mitmproxy 使用。
3.3 用脚本把证书和工具打成 zip,还带一键安装器
有了证书,还要把配置代理、挂载分区、推入门这些行为自动化。我一般写一个install.sh,由它在执行时接收 hash 值:
#!/system/bin/sh set -e # 需要已执行 adb root if ! adb shell id | grep -q uid=0; then echo "need root to continue" exit 1 fi # 重新挂载系统分区,Android 9 的 system-as-root 可能需要直接 emulator -writable-system adb shell mount -o rw,remount /system 2>/dev/null || adb remount # 推入证书并设置 644 权限 adb push ${HASH}.0 /system/etc/security/cacerts/ adb shell chmod 644 /system/etc/security/cacerts/${HASH}.0 # 设置全局 HTTP 代理,mitmproxy 监听在宿主机 8080 端口 adb shell settings put global http_proxy 192.168.1.10:8080 # 启动 mitmproxy,让产生的证书用在当前会话 mitmproxy --set confdir=$HOME/.mitmproxy --listen-port 8080settings put global http_proxy只对遵守系统代理的设置网络栈生效,部分 app 会忽略它,后面再用 iptables 兜底。adb remount在模拟器里通常可行,真机若是会损坏 dm-verity 验证,就要用 Magisk 模块方式慢一档。把这些文件和 install.sh 打在一起:
zip -r mitm.zip install.sh ca.crt ${HASH}.0 frida_bypass.js tools/ unzip -t mitm.zip # 打包后立即自检一次,避免分发时报 corruptionunzip -t是拿到包之后第一步就要做的验证,能提前发现“证书文件复制不完整”这类低级问题,不然终端上到处是error read zip archive,排查半天最后发现只是 zip 本身坏了。
4. 从安装到转包:在安卓模拟器上跑通完整 MITM 流量
4.1 模拟器与 adb 准备
模拟器是入门最快的地方,因为它允许直接开启可写 system 分区:
emulator -avd test_avd -writable-system -no-snapshot & adb wait-for-device adb root adb remount-writable-system让/system变成可写挂载,这是把 CA 直接放进系统证书目录的前提。adb root必须成功,否则后面的 push 和 remount 都无权操作系统分区。如果使用 Google Play 版的模拟器镜像,adb root会被禁用,换成不带系统应用的 AOSP 镜像。
4.2 配置全局代理还是 iptables 重定向
两种常用方案各有侧重。
全局代理对targetSdkVersion < 24或者遵守系统代理的应用有效:
adb shell settings put global http_proxy 192.168.1.10:8080iptables 重定向不依赖应用是否读取系统代理,适用于那些用 OkHttp、网络进程直接发 SYN 包的应用:
# 将本机所有对外 443 流量劫持到 mitmproxy 监听端口 adb shell iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination 127.0.0.1:8080注意,iptables 劫持只对设备自身的 OUTPUT 链生效。如果目标是模拟器内部转发到宿主机,需要把 DNAT 目标指向宿主机的数据库,同时宿主机要开启 IP 转发。我通常优先用全局代理,因为切换和撤销都简单,落网时看 tcpdump 流量也更直观;遇到不读系统代理的 app 才切到 iptables。
4.3 把用户证书变成系统证书再重挂载
模拟器已经 mount 好可写分区,这时直接把<hash>.0推到系统证书目录:
adb push ${HASH}.0 /system/etc/security/cacerts/ adb shell chmod 644 /system/etc/security/cacerts/${HASH}.0 adb shell killall com.android.settings 2>/dev/null || true如果还是提示证书不在信任列表,检查文件名是否真用了subject_hash_old的结果。常见错误是拿现代 openssl 的-subject_hash代替,两者在同名情况下可能得到不同值,Android 8 以上系统只认新的subject_hash吗?恰恰相反,AOSP 从 API 24 到 30 都用subject_hash_old作为系统证书的命名方式,到 Android 14 开始部分设备两者兼容,但为了稳妥,统一用subject_hash_old生成文件名。
4.4 用 mitmproxy 的 Python 脚本实时修改返回体
中间人攻击不只是“看”,还要能改。把下面脚本存成modify_response.py:
from mitmproxy import http def response(flow: http.HTTPFlow) -> None: # 只篡改 /api/config 的 JSON 返回 if "/api/config" in flow.request.pretty_url: text = flow.response.get_text() text = text.replace('"banner":0', '"banner":1') flow.response.set_text(text) print(f"[modify] {flow.request.pretty_url}")启动 mitmproxy 时加载:
mitmdump --listen-port 8080 -s modify_response.pyresponse钩子会在得到宿主机服务器响应后触发。这里用get_text/set_text会自动处理 content-length 和 charset 更新,不要直接改flow.response.content后忘了重算头部,容易被客户端当成非法响应丢弃。这个脚本你可以扩展成记录特定接口参数、把错误码改成 200 做超时测试,都只需要在这个函数里加分支。
5. 进阶对抗:SSL Pinning 与高版本 Android 的三种绕过姿势
5.1 识别 Pinning:怎么知道它 pin 了哪些指纹
常规抓包流量里出现Certificate validation failed,先怀疑是 SSL Pinning 而不是证书安装问题。用 jadx 反编译 APK,搜索CertificatePinner、ssl_pinning、publicKey等关键词,能直接定位到 OkHttp 的 pin 配置。
更方便的是用 objection 运行命令检查:
objection -g com.example.app explore android ssl pinning list该命令会枚举当前已加载的请求库和 hook 点,帮助你判断是否由 OkHttp、TrustManager 还是 WebView 做校验。识别结果直接决定后面用哪种 hook。
5.2 用 Frida 一键 hook 掉 OkHttp、TrustManager
OkHttp 的CertificatePinner.check是所有证书固定校验的汇聚点,用 Frida 把这个方法空实现即可绕过:
Java.perform(function () { var CertificatePinner = Java.use("okhttp3.CertificatePinner"); CertificatePinner.check.overload('java.lang.String', 'java.util.List') .implementation = function (hostname, peerCerts) { console.log("[bypass] skip pinning: " + hostname); }; // 同时兜底 TrustManager var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager"); X509TrustManager.checkServerTrusted.implementation = function (chain, authType) { console.log("[bypass] TrustManager skipped"); }; });保存为frida_bypass.js,运行:
frida -U -f com.example.app -l frida_bypass.js --no-pauseoverload指定了匹配String和List两个参数的实现,这是 OkHttp 4.x 常见的签名。若遇到不同库,如httpclient的HttpsURLConnection,还要再 hookTrustManagerImpl的verify方法。Frida 需要设备上运行 frida-server,且模拟器里 Synxx 架构要用对应的 server 二进制。
5.3 不 root 的替代方案:重新打包 debug 版与 Network Security Config
如果测试设备不能 root,或者咖啡因依赖导致无法上 Frida,可以直接修改 APK 的网络安全配置。用 apktool 解包:
apktool d com.example.apk -f打开AndroidManifest.xml,给 application 节点加上networkSecurityConfig:
<application android:networkSecurityConfig="@xml/network_security_config" ...>再在res/xml/network_security_config.xml里声明信任用户证书:
<network-security-config> <base-config cleartextTrafficPermitted="true"> <trust-anchors> <certificates src="system"/> <certificates src="user"/> </trust-anchors> </base-config> </network-security-config>重新打包、签名并安装。这种方式对绝大多数 CN、WWW 域名的未加固 app 有效;但如果 app 在代码里自己做了校验,仍需配合 Frida。注意重打包会破坏原签名,若 app 本身有签名校验或安装在 Android 14 以上,需要同时处理APK Signature Scheme v2的绕过策略。
5.4 常见失败定位:为什么证书装了还是 502 / SSL_ERROR
遇到 mitmproxy 里出现 502 或 SSL_ERROR 时,先按顺序排查:
- 证书文件名是否为
<subject_hash_old>.0,并且推入后chmod 644。 - 应用是否走了独立网络栈,比如 WebView、Flutter 的 dart:io 不走系统代理且不读用户证书。
- 证书是否已经被系统缓存,模拟器里
killall com.android.settings或重启再试。 - 用
openssl s_client -connect 192.168.1.10:8080从设备上直接连接代理端口,看是否有 tcp 握手失败。
一个快速验证方法:安装一个不校验 pin 的浏览器(比如常规版火狐),如果它能直接浏览 HTTPS 网页,说明 mitmproxy 和系统证书层面已经连通,剩下的就是目标应用自己的 Pinning 问题。
6. 收尾技巧:验证 MITM 环境、修复破坏的 zip 包与快速重置代理
拿到跑通的流量后,先确认 3 件事:代理生效范围、证书可信状态、zip 包完整性。
用 adb 查看当前代理设置:
adb shell settings get global http_proxy输出形如192.168.1.10:8080才说明全局代理生效。如果显示null:0,说明上一步没有写入成功,常见原因是设备里有另一个前台应用在单独配置 WiFi 代理,覆盖了 global 值。
验证系统证书是否被识别,启动 mitmproxy 后发起一个已知域名的请求,看 mitmproxy 页面是否出现爆破过的证书链。更严谨的方式是在设备端执行:
echo | openssl s_client -connect www.example.com:443 -CAfile /sdcard/ca.der返回Verify return code: 0 (ok)说明设备信任了这个 CA。这里用 DER 格式是因为部分设备上的 openssl 老版本读 PEM 会报格式不支持。
zip 包坏了是分发中最常见的问题,当你或同事下载 mitm.zip 之后执行unzip -t mitm.zip,输出error read zip archive或unexpected end of archive,先别急着改代码,尝试修复:
zip -FF mitm.zip --out mitm_repair.zip它会扫描 zip 剩余内容,重建中央目录。修复后如果还是崩,再考虑重新打包,同时确认不是下载传送方式的问题——用scp或adb push时记得校验 MD5。
最后,测试完不要留下后门。清理代理和证书:
adb shell settings delete global http_proxy adb shell rm /system/etc/security/cacerts/<hash>.0如果设备还停留在 root 状态,就恢复厂商原版镜像或关闭可写分区。整个 mitm.zip 的核心价值,是把这些交错繁琐的步骤收拢成一次可重复执行的流程;下次拿到新设备,unzip -t自检通过后,十分钟内就能回到抓包状态。
本文还有配套的精品资源,点击获取