安卓中间人攻击实战:mitm.zip证书信任与抓包全流程

发布时间:2026/9/15 2:11:07
安卓中间人攻击实战:mitm.zip证书信任与抓包全流程 简介一份聚焦安卓中间人攻击MITM主题的打包资源面向安卓应用开发者、移动安全测试人员以及广大对网络安全感兴趣的读者。包体约5.38MB共100个文件包含30个Java源码、27个XML配置、17个PNG图片、7个JAR库以及APK、DEX、tcpdump_x86、arpspoof_x86等可执行或格式文件此外还有properties、project、README等工程配置文件能用于还原项目构建、权限配置与代码混淆相关结构但并未直接给出MITM攻击或防范的完整实施代码。因此这份资源更适合作为了解安卓网络通信和项目工程组织的入门素材帮助读者在已有安全知识基础上结合应用配置文件梳理应用组件与权限上下文。目前已有76人学习下载对于希望以实际安卓工程为切入点、进一步查阅安全编码规范和测试工具的用户仍具有一定的参考价值和上手意义。1. 为什么“安卓中间人攻击 mitm.zip”是从工具包开始的实战手头拿到一份mitm.zip常见的场景是同事丢过来一个“抓包工具包”里面压缩着 mitmproxy 可执行文件、CA 证书、一份 README有时还带几个流量脚本。多数人解压后直接双击运行然后在安卓设备的 Wi-Fi 设置里填上代理 IP 和端口接着抓到的 HTTPS 流量全是密文怎么也解不开。问题不在工具本身而在一个反直觉的知识点上Android 7.0 及以上系统里App 默认只信任系统级 CA 证书手动安装到“用户证书”区的证书对大多数应用并不生效。换句话说单纯装证书、开代理远够不上一次完整的中间人测量。这篇文章不介绍公网扫描或任何灰色用法而是沿着“解压工具包 → 让流量走进代理 → 解决证书信任 → 自动化导出 → 验证拦截结果”这条链把安卓场景下最常踩的坑拆开讲。适合做安全测试、App 逆向、自家应用流量排查的一线工程师。哪怕你已经写了五年代码第三节的安卓 11 分区差异和第五节的可信链检查也值得再对一遍。2. 起底 mitm.zip解开工具结构选择运行方式2.1 解压前先看 zip 内部结构别急着双击拿到 zip 后第一件事不是解压而是列目录。Linux/macOS 下用unzip -lWindows 下用tar -tf或者直接双击后看一眼路径层级目的只有一个确认是单文件还是目录树防止解压出大量散落的文件污染工作目录。unzip -l mitm.zip-l参数只列出文件清单不真正落盘。清单里如果能看到bin/、certs/、scripts/这些目录说明包的结构是完整的如果一个几十 MB 的 zip 解开只有一个 exe那它多半是便携版免安装包直接解压就能跑不需要额外装 Python 环境。对于被加了口令的内部分发包先用 README 里写的默认密码试试不要一上来就上暴力破解工具——本场景下密码通常只是防误下载。包内常见内容典型路径拿到后做什么mitmproxy / mitmdumpbin/mitmproxy、bin/mitmdump确认有执行权限chmod x证书文件certs/mitmproxy-ca-cert.pem后续转成系统 CA 证书用拦截脚本scripts/addon_*.py看是否匹配你要抓的接口路径说明文档README.md先读避免乱试参数解压时如果要保持目录结构用unzip mitm.zip -d ~/android-mitm把内容固定在某一个目录下后面写启动脚本和归档时都方便。尤其注意不要在带空格的 Windows 路径下直接跑 mitmproxy代理证书路径一旦带空格注入 HTTPS 拦截时会碰到奇怪的读取问题。2.2 启动 mitmproxy监听地址和端口决定了手机能不能连上解压完成后常见做法是把 mitmdump 或 mitmproxy 跑起来。密码学上中间人工具的核心是让所有经过它的 TLS 流量都被自己的证书重新签名所以工具进程必须监听到手机上能访问的 IP。cd ~/android-mitm chmod x bin/mitmdump bin/mitmproxy bin/mitmweb ./bin/mitmproxy --listen-host 0.0.0.0 --listen-port 8888 --set block_globaltrue--listen-host 0.0.0.0是最容易被忽略的参数。mitmproxy 默认只监听 localhost如果保留默认值PC 本机访问正常手机却永远显示“连接被拒绝”。--set block_globaltrue的作用相反它限制只有被显式代理到本机的连接才放行防止局域网里其他设备撞到你开着的端口相当于给抓包现场加一道白名单。首次启动会生成~/.mitmproxy/mitmproxy-ca-cert.pem这就是后面要安装到安卓里的根证书。如果更习惯看图形化的请求列表替换成./bin/mitmweb --listen-host 0.0.0.0 --listen-port 8888 --web-port 8081也行web 界面能看到每个请求的 URL、状态码和耗时推荐不熟悉 CLI 操作的人用。2.3 让安卓流量走进中间人代理配置的两个入口工具跑起来后需要把手机的 HTTP 流量指到这台 PC 上。最常见的方式是在安卓的 Wi-Fi 设置里手动配置代理设置项值说明代理手动Android 系统级代理对大多 App 生效主机名192.168.x.xPC 在局域网内的 IP不是 127.0.0.1端口8888与 mitmproxy 的--listen-port一致不连 Wi-Fi 的真机或者想让代理设置可脚本化用 adb 直接写全局代理更合适adb shell settings put global http_proxy 192.168.1.100:8888这条命令把全局代理写入系统设置不需要在手机上逐级点菜单。清除时执行adb shell settings put global http_proxy :0。如果手机是 root 过的包括模拟器里adb root的安卓 11 环境还可以用 iptables 做透明流量导向常见命令是adb shell iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination 192.168.1.100:8888OUTPUT链做 DNAT含义是本机 443 出口流量全部重定向到抓包机适合目标 App 不走系统代理的场景。干完活要清规则adb shell iptables -t nat -F会清空 nat 表注意别在生产环境随手执行。无论用哪种方式配完后打开手机浏览器访问一个 HTTPS 页面PC 终端能刷出请求到这里第一段链路通了一半剩下的就是证书信任。3. 证书入系统目录解决 Android 7 不认用户证书的问题3.1 为什么用户证书装了却不生效安卓从 6.0 开始区分“用户证书”和“系统证书”两类 CA。用户在设置里手动安装的证书默认进入settings put global之外的 credential storage这类证书对系统浏览器和部分应用有效但对 targetSdkVersion 24 以上的 App 无效。原因是 Android 7.0 起App 的网络安全配置默认只信任系统 CA用户安装的 CA 除非在 App 的networkSecurityConfig里显式声明否则一律视为不可信。也就是说抓包时经常遇到的“证书已安装但 App 请求全部失败提示 SSL 错误”九成是用户证书不被目标 App 接受。调试自己开发的 App 时可以在AndroidManifest.xml里加一个 debug 专用的网络安全配置network-security-config base-config cleartextTrafficPermittedfalse trust-anchors certificates srcsystem / /trust-anchors /base-config debug-overrides trust-anchors certificates srcuser / /trust-anchors /debug-overrides /network-security-configdebug-overrides只在应用是android:debuggabletrue时生效release 包会被忽略。也就是说如果目标 App 不是你自己能以 debug 模式编译的这条路走不通必须让 CA 直接进系统证书目录。3.2 openssl 生成 Android 系统证书文件名就是 hash.0Android 系统在启动时扫描/system/etc/security/cacerts/目录CA 文件的命名规则是“证书 subject 的 hash 值 .0后缀”。文件名不对文件即使放进目录也不会被加载。HASH$(openssl x509 -inform PEM -subject_hash_old -in ~/.mitmproxy/mitmproxy-ca-cert.pem | head -n 1) cp ~/.mitmproxy/mitmproxy-ca-cert.pem ${HASH}.0-subject_hash_old输出旧式哈希包含换行符用head -n 1只截第一行。这个哈希不是 SHA-256 摘要不能手算替代。如果你拿到的是.cer或.der格式的证书先加一步转换openssl x509 -inform DER -in cert.cer -out cert.pem再执行上面的命令。产出的xx.0文件就是待推送的系统 CA。3.3 adb root 写入系统证书按安卓版本选路径写出命令前先把因果理清。Android 9 和 10 的/system可以通过adb remount挂载为可写Android 11 开始启用动态分区单纯 remount 会失败需要先adb disable-verity消除启动时的 dm-verity 校验到了 Android 13 以上CA 存储实际由 Conscrypt APEX 模块管理直接往/system塞文件可能重启后被 APEX 覆盖。最稳妥的通用做法还是先试标准流程adb root adb remount adb push ${HASH}.0 /system/etc/security/cacerts/ adb shell chmod 644 /system/etc/security/cacerts/${HASH}.0 adb rebootadb root让 adbd 以 root 身份运行只有 Google 官方镜像和部分模拟器支持真机如果提示adbd cannot run as root in production builds需要借助 Magisk 模块注入系统证书而不是硬改/system。chmod 644保证其他用户可读因为系统进程可能以非 root 身份读取证书目录。执行顺序有讲究adb remount要在 root 之后否则会失败。模拟器上看到remount of the / superblock failed时先试一遍adb root adb disable-verity adb reboot adb remount adb push ${HASH}.0 /system/etc/security/cacerts/安卓版本证书路径写入方式Android 6.0 及以下/system/etc/security/cacerts/传统 remountAndroid 7.0 - 10/system/etc/security/cacerts/传统 remountAndroid 11 - 12/system/etc/security/cacerts/只读增强adb disable-verity后 remountAndroid 13/apex/com.android.conscrypt/cacertsMagisk 模块或 APEX 替换区分重点不是命令本身而是“目标环境怎么认证书”。拿到设备先跑一句adb shell readlink -f /system/etc/security/cacerts如果输出里带apex字样说明 CA 目录是链接出来的普通 push 很容易搞坏系统。绝大多数抓包场景里最常见的落点其实是 Android 9/10 的模拟器因为它支持adb root且路径稳定。3.4 验证证书是否被系统加载重启后检查文件是否在位adb shell ls -l /system/etc/security/cacerts/ | grep $(openssl x509 -in ~/.mitmproxy/mitmproxy-ca-cert.pem -noout -subject_hash_old)如果输出为空先看设备上/system/etc/security/cacerts是否存在再检查文件权限是否为 644。文件在但抓不到时用adb shell find / -name *.0 2/dev/null找一下有没有异构 CA 目录。到这里系统已经信任 mitmproxy 的根证书剩下的工作交给数据清洗。4. 把 zip 里的工具链收拢成自动化用 mitmdump 跑规则脚本4.1 为什么自动化优先选 mitmdump 而不是图形界面命令行工具的好处是可重复。同一个 App 反复抓包每次手点无比麻烦。mitmdump 没有交互界面适合放在脚本里做批处理也适合把上一章准备的证书和脚本塞回 zip下次换台设备解压即用。自动化脚本的保存位置建议分类目录内容作用addons/Python 插桩脚本处理请求/响应输出日志certs/系统 CA 的.0文件推送到设备bin/预编译的 mitmdump/mitmproxy免去目标机器装 Python 依赖flows/写出的流量文件离线再分析4.2 一个可抄的 addon命中 API 路径并落盘常见需求是把 App 所有指向/api/的请求与响应记录下来排除图片和静态资源。利用 mitmdump 的 addon 事件接口import time from mitmproxy import http def request(flow: http.HTTPFlow) - None: url flow.request.pretty_url if /api/ in url: print(f[req] {time.strftime(%H:%M:%S)} {flow.request.method} {url}) def response(flow: http.HTTPFlow) - None: url flow.request.pretty_url if /api/ in url and flow.response: with open(api_log.txt, a, encodingutf-8) as f: f.write(f{url}\t{flow.response.status_code}\t{len(flow.response.content)}\n)request回调在请求发出前执行适合记 URLresponse回调在拿到响应后执行flow.response.status_code能区分 200、302 和 401。文件追加模式打开日志不会覆盖上一轮记录。注意pretty_url带 query 参数如果只想记录 path改成flow.request.path。把脚本存成addons/save_api.py启动时挂载./bin/mitmdump -s addons/save_api.py -p 8888 --set block_globaltrue -w flows_$(date %Y%m%d).mitm参数含义分别是-s加载脚本-p监听端口-w把全部流量写入二进制文件。-w写出的.mitm文件可以用mitmproxy -r flows.mitm离线回放方便事后排查。如果只想要日志不要全流量把-w去掉磁盘压力小很多。4.3 把产物和工具重新打成 zip方便换机继续一次完整的抓包整理工作做下来api_log.txt、flows_*.mitm、证书文件分布在各处下回换台 PC 或换部手机全要重建。工作收尾时顺手归档mkdir -p mitm-session-$RANDOM cp api_log.txt certs/*.0 flows_*.mitm mitm-session-$RANDOM/ zip -r mitm-session-$(date %Y%m%d).zip mitm-session-$RANDOM/这样产出物是自洽的日志证明抓到了什么.0证书能在另一台设备上快速导入.mitm文件能离线复盘。标题里的mitm.zip本质上就是一个可迁移的工具包不要把它理解成只能解压一次的安装包。4.4 对目标 App 的常见假设检查自动化抓包常遇到一个陷阱App 不走 HTTP 代理走了也没反应。这种情况下先确认流量是否真的走到了 mitmdump 端口——在request回调里无条件打印所有 URL 会看到大量系统健康检查请求说明链路是通的迟迟不出现目标域名再检查 App 是否通过 DNS 直连。安卓 11 之后的系统对明文流量限制更严如果目标是自家 App改用上文的debug-overrides最省心不用碰系统证书。5. 三次验收拦截确认流量真的经过 mitm5.1 第一次检查代理链路是否连通在手机上打开浏览器访问http://mitm.it这是 mitmproxy 内置的证书下载页。能打开这个页面证明 8888 端口的代理链路是通的页面通常还会显示本机 IP。如果浏览器能打开但 App 就是断网别急着怀疑 CA先确认 App 是不是走了系统代理以外的网络通道——用全局 iptables 重定向试一下能区分这个问题。5.2 第二次检查证书链上的 Verify return codePC 端用 openssl 验证当前 PC 上信任链是否正确拼接echo | openssl s_client -connect www.example.com:443 -servername www.example.com -CAfile ~/.mitmproxy/mitmproxy-ca-cert.pem 2/dev/null | grep Verify return code输出Verify return code: 0 (ok)表示 PC 用 mitmproxy 证书完成了目标站点的证书链验证。如果报20 (unable to get local issuer certificate)说明-CAfile路径给错了或者这台 PC 上根本没有安装过 mitmproxy 的根证书。这一步验证的是“中间人自己的链是完整的”跟安卓端的信任隔离不同勿混淆。5.3 第三次检查目标 App 有没有二次 pinning证书装好、代理也通了但目标请求全部是Client TLS handshake failed大概率是 App 在代码层面做了 SSL Pinning只认自己预置的证书指纹绕过系统 CA 校验。常见做法是用 Frida 注入拦截 okhttp 的CertificatePinner.checkJava.perform(function () { var CertificatePinner Java.use(okhttp3.CertificatePinner); CertificatePinner.check.overload(java.lang.String, java.util.List).implementation function() { console.log(bypass pinning); }; });这段代码只是思路演示okhttp 版本不同方法签名略有差异。更通用的定位方式是先执行frida-trace -U -i *.CertificatePinner* 应用包名看 App 实际调用了哪些校验类再针对性地写 hook。Pinning 的绕过验证完再回看api_log.txt里的状态码分布——出现大量 200 且响应体长度一致说明这一整条“工具包 → 代理 → 证书 → 自动化”的链路已经闭环把证书和脚本重新压回 zip 存好下次直接沿用。本文还有配套的精品资源点击获取