news 2026/5/21 11:06:09

Mac安卓安全测试全链路:抓包、单向认证绕过与脱壳协同方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Mac安卓安全测试全链路:抓包、单向认证绕过与脱壳协同方案

1. 这不是“破解”,而是一次对安卓生态安全水位的实地测绘

你有没有遇到过这样的场景:手头有个内部测试用的安卓App,文档里写着“已启用双向TLS认证+全量加固”,但开发同事临时请假,你又急需验证某个接口返回字段是否符合新协议?或者,你在做第三方SDK合规审计,发现某家厂商的SDK在Mac上抓不到任何HTTPS流量,连证书替换都失败——这时候,你第一反应是找现成的“脱壳工具”或“绕过脚本”,但很快会意识到:Mac平台下这套流程根本没人系统梳理过。我去年帮三家金融类客户做移动安全评估时,反复卡在这个环节。他们用的都是主流加固方案(360、腾讯云、梆梆),但所有公开资料几乎都默认你用Windows配Frida+Android Studio,或者Linux跑adb shell。Mac用户被默认“不配拥有完整链路”。这其实暴露了一个更本质的问题:单向认证(即客户端校验服务端证书)和加固机制,在Mac环境下不是“不能绕过”,而是缺少一套从网络层到应用层、从静态分析到动态注入的连贯路径。本文标题里的“从抓包到脱壳”,说的正是这条被长期忽视的链路——它不教你怎么黑进别人App,而是帮你理解:当一个App宣称“已加固+证书锁定”时,它的真实防御纵深到底在哪一层、哪一环最容易松动、哪些操作在Mac上必须换思路。关键词覆盖了Mac环境适配、安卓单向认证绕过、加固App脱壳、抓包工具链协同、Frida动态插桩。适合两类人:一是需要在Mac上完成移动安全评估的渗透测试人员;二是正在调试自家加固后App的iOS/前端工程师(别笑,很多团队真用Mac写安卓代码)。全文没有一行代码是为越狱或非法用途设计的,所有操作均基于合法授权的测试环境,目标明确:还原App在运行时的真实行为,仅此而已。

2. 单向认证的真相:你以为的“证书锁定”,其实只是第一道纸糊的门

很多人一听到“单向认证”,脑子里立刻跳出“证书固定(Certificate Pinning)”这个词,然后条件反射去翻OkHttp的CertificatePinner配置,或者X509TrustManager的重写逻辑。但实际拆解下来,单向认证在安卓上的实现远比教科书定义更琐碎、更多样。它根本不是单一技术点,而是一组分层嵌套的校验机制,每一层的绕过策略和工具链都不同。我在实测27款主流加固App后,把它们的单向认证实现归纳为四个典型层级,按从外到内、从易到难排序:

2.1 网络库层:OkHttp与Retrofit的明面战场

这是最“友好”的一层。绝大多数App用OkHttp做网络请求,其证书固定逻辑集中在OkHttpClient.BuildercertificatePinner()方法里。绕过方式非常直接:用Frida HookOkHttpClient$Builder.certificatePinner,在构造器执行时强行注入空的CertificatePinner实例。实操中要注意两点:一是必须Hook到Builderbuild()方法之后,因为certificatePinner字段是在build()内部才真正赋值的;二是某些加固方案会把OkHttp类名混淆成a.b.c这种格式,此时不能硬编码类名,得用Java.use("okhttp3.OkHttpClient\$Builder")配合Java.enumerateLoadedClassesSync()动态扫描。我试过用frida-trace -i "okhttp3.OkHttpClient\$Builder#build"先确认调用栈,再针对性Hook,成功率接近100%。这里的关键认知是:这一层绕过不等于“抓包成功”,它只解除了客户端对服务端证书的校验,但流量仍可能被加固层拦截加密。

2.2 加固壳层:Native层SSL/TLS劫持的隐性关卡

当OkHttp层绕过失败时,大概率是加固壳在Native层做了手脚。典型如360加固的libjiagu.so,它会在SSL_CTX_set_verifySSL_set_verify函数处埋点,强制校验服务端证书链。这类绕过必须进入Native世界。我在Mac上用lldb配合Frida双引擎调试:先用frida -U -f com.xxx.app --no-pause启动App,再用lldb -p $(pgrep -f "com.xxx.app")附加进程,通过image list | grep jiagu定位加固so基址,然后breakpoint set -n SSL_set_verify下断点。实测发现,360的校验逻辑会读取内存中硬编码的证书公钥哈希值(SHA256),与服务端证书比对。绕过方案不是Patch二进制,而是用Frida的Interceptor.replace直接替换SSL_set_verify的回调函数,让其始终返回SSL_VERIFY_NONE。这里有个Mac专属坑:lldb在M1/M2芯片上需用--arch arm64e参数指定架构,否则断点永远不触发。这个细节在Windows教程里根本不会提。

2.3 自研加密层:Java层混淆后的“影子证书”校验

有些App不依赖标准库,而是用Bouncy Castle或自研加解密库,在HttpsURLConnectionconnect()之后,手动解析响应体并校验其中嵌入的证书指纹。这类校验往往藏在高度混淆的Java方法里,比如a.a.b.c.d()这种命名。静态分析极难定位,但动态Hook很有效。我的做法是:用Frida遍历所有java.net.HttpsURLConnection子类,Hook其getInputStream()方法,在返回流之前,用Java.array('byte', stream.readAllBytes())获取原始字节,搜索特征字符串如-----BEGIN CERTIFICATE-----SHA256:。一旦捕获到,立即打印堆栈,就能精准定位校验逻辑所在类。去年审计某政务App时,就靠这招在3分钟内揪出隐藏在com.x.y.z.a类里的证书哈希比对代码。关键在于:Mac上用frida-ps -U查进程名时,要加-a参数显示全包名,否则容易漏掉后台Service进程。

2.4 系统API层:Android 7.0+的Network Security Config硬限制

这是最容易被忽略的“天花板”。从Android 7.0开始,App可通过res/xml/network_security_config.xml强制指定信任的CA证书集,甚至禁用用户安装的CA。此时,即使你用Charles装了根证书,App也会直接拒绝连接。绕过方案只有两个:一是修改APK的AndroidManifest.xml,将android:networkSecurityConfig属性指向一个宽松的配置文件(需重签名);二是在运行时用Frida Hookandroid.security.net.config.NetworkSecurityConfigisCertificateTransparencyEnforced()等方法,强制返回false。后者更实用,因为无需重打包。我在Mac上写了个通用脚本,用Java.use("android.security.net.config.NetworkSecurityConfig").$init.implementation = function() { this.$init.apply(this, arguments); Java.use("android.security.net.config.NetworkSecurityConfig").isCertificateTransparencyEnforced.value = false; },适配所有Android版本。注意:此Hook必须在App主Activity创建前执行,所以要用frida -U -f com.xxx.app -l hook.js --no-pause,而不是-l挂载后resume

提示:单向认证绕过的本质不是“打败技术”,而是“识别技术栈”。Mac用户的优势在于终端命令行生态成熟,otool -L libjiagu.so看依赖库、strings libjiagu.so | grep -i ssl搜关键词、nm -D libjiagu.so | grep verify查符号,这些操作在Mac Terminal里比Windows PowerShell流畅得多。别迷信图形化工具,命令行才是Mac上逆向的底层武器。

3. 抓包链路重构:为什么Charles在Mac上总“失灵”,以及如何用mitmproxy+adb reverse重建可信通道

在Mac上做安卓抓包,最大的幻觉就是“装个Charles,配好代理,手机连Wi-Fi,万事大吉”。现实是:90%的加固App在Mac上根本连不上Charles。原因不在Charles本身,而在Mac与安卓设备间的网络拓扑和证书信任链被多重截断。我拆解过12个失败案例,问题根源集中在三个层面:Wi-Fi代理的不可控性、安卓系统证书存储的隔离性、以及加固壳对http_proxy环境变量的主动检测。解决方案不是换工具,而是重构整个抓包链路——把“手机连Mac代理”变成“安卓设备反向连接Mac本地服务”。

3.1 Wi-Fi代理的致命缺陷:DNS污染与加固壳的主动探测

当手机通过Wi-Fi连接Mac的Charles代理时,所有DNS查询都经由Mac转发。但加固壳(如腾讯云御安全)会周期性发起www.baidu.com的DNS请求,并比对返回IP是否为真实百度IP。一旦发现IP被Charles劫持(比如返回127.0.0.1),立即终止网络模块。更麻烦的是,安卓8.0+默认启用Private DNS,会强制走DoT加密DNS,彻底绕过Charles的DNS劫持能力。我在Mac上实测,用scutil --dns查看系统DNS配置,发现默认启用了1.1.1.1的DoH,这直接导致Charles的DNS spoofing失效。解决思路是放弃Wi-Fi代理,改用adb reverse建立点对点隧道。命令极其简单:adb reverse tcp:8080 tcp:8080,这行命令让安卓设备的8080端口映射到Mac本机的8080端口。此时,App的所有HTTP请求都发往127.0.0.1:8080,完全不经过Wi-Fi路由,自然规避了DNS探测。

3.2 mitmproxy:Mac原生支持的抓包核弹,比Charles更懂安卓

Charles在Mac上卡顿、崩溃频发,根本原因是其Java GUI框架与macOS Sonoma+的Metal渲染冲突。而mitmproxy是纯Python终端工具,原生适配Mac M系列芯片。更重要的是,mitmproxy的证书生成和安装流程专为安卓优化。步骤如下:

  1. 在Mac终端执行mitmproxy --mode reverse:http://127.0.0.1:8000(假设后端服务在8000端口),此时mitmproxy监听8080端口;
  2. 执行adb reverse tcp:8080 tcp:8080,建立反向隧道;
  3. 在安卓App里,将所有网络请求的目标地址从https://api.xxx.com改为http://127.0.0.1:8080(注意是HTTP,非HTTPS);
  4. 启动mitmproxy,它会自动在~/.mitmproxy/生成mitmproxy-ca-cert.pem
  5. 将该PEM文件传到安卓设备,通过设置 > 安全 > 加密与凭据 > 从存储设备安装导入。

关键点在于第3步:为什么改用HTTP?因为mitmproxy的reverse模式会自动将HTTP请求升级为HTTPS转发到真实后端,同时用自己的证书解密流量。这样,App端只需处理明文HTTP,完全绕过了证书固定和Network Security Config的限制。我在测试某银行App时,用此法将抓包成功率从12%提升到100%,且mitmproxy的mitmdump -s script.py支持Python脚本实时修改请求头,比Charles的Breakpoint功能更灵活。

3.3 证书安装的Mac专属路径:从PEM到安卓系统证书的三步转化

安卓7.0+要求用户证书必须安装到“用户凭据”目录,但很多App(尤其金融类)会检查/system/etc/security/cacerts/下的系统证书。此时,单纯安装mitmproxy证书无效。解决方案是利用Mac的OpenSSL工具链,将mitmproxy的PEM证书转换为系统证书格式:

  1. openssl x509 -inform PEM -in ~/.mitmproxy/mitmproxy-ca-cert.pem -outform DER -out ca.der
  2. openssl x509 -inform PEM -in ~/.mitmproxy/mitmproxy-ca-cert.pem -noout -hash,得到哈希值(如d5a537d0);
  3. mv ca.der d5a537d0.0,重命名为哈希值加.0后缀。

然后,用adb push d5a537d0.0 /sdcard/传到手机,再通过adb shell su -c "cp /sdcard/d5a537d0.0 /system/etc/security/cacerts/"复制到系统目录(需root)。Mac上openssl版本必须≥1.1.1,否则-hash参数不识别。这个流程在Windows上需要额外装Cygwin,而Mac自带Terminal和Homebrew,brew install openssl一步到位。

注意:adb reverse命令在安卓5.0以下不支持,此时必须降级用adb forward tcp:8080 tcp:8080,但forward是单向的,需App主动连接Mac,不如reverse稳定。另外,某些加固壳会检测adb进程,建议在抓包前用adb shell ps | grep adb确认无异常进程残留。

4. 脱壳实战:Mac上从DEX抽取到OAT反编译的全链路,为什么JADX-GUI在M系列芯片上会崩溃

“脱壳”这个词在Mac圈子里常被误解为“一键解密APK”。实际上,对于360、梆梆这类主流加固,脱壳是分阶段的:先从内存中dump出运行时解密的DEX字节码,再修复DEX头结构使其可被JADX识别,最后处理OAT文件中的优化代码。Mac用户的最大障碍不是技术,而是工具链兼容性——JADX-GUI在M1/M2芯片上因Java AWT渲染问题频繁崩溃,而命令行版JADX又缺乏可视化交互。我的方案是:用Mac原生工具链构建一条“内存dump → DEX修复 → OAT解析 → 源码还原”的纯终端流水线。

4.1 内存dump:Frida + r2frida在Mac上的黄金组合

传统方案用adb shell进设备执行cat /proc/self/maps找DEX内存地址,再dd拷贝。但加固App会主动清空/proc/self/maps中的DEX段标记,或使用mmap随机分配地址。更可靠的方式是用Frida HookDexFile::OpenMemory函数,在DEX加载进内存的瞬间捕获其指针和大小。我在Mac上写了个Frida脚本:

Java.perform(function () { var DexFile = Java.use("dalvik.system.DexFile"); DexFile.$init.overload('java.lang.String').implementation = function (sourceName) { console.log("[+] Loading dex from: " + sourceName); return this.$init.call(this, sourceName); }; Interceptor.attach(Module.findExportByName("libart.so", "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEjPS8_"), { onEnter: function (args) { this.baseAddr = args[0]; this.size = args[1]; console.log("[*] DexFile::OpenMemory at " + args[0] + ", size: " + args[1]); }, onLeave: function (retval) { if (this.baseAddr && this.size) { var buffer = Memory.readByteArray(this.baseAddr, this.size); send("dex_dump", buffer); } } }); });

脚本输出的buffer用frida -U -f com.xxx.app -l dump.js --no-pause运行,再用Python接收:

def on_message(message, data): if message['type'] == 'send' and message['payload'] == 'dex_dump': with open('dumped.dex', 'wb') as f: f.write(data)

这里的关键是:Mac上r2fridafrida-trace更适合内存分析。r2 -A -P frida://usb//com.xxx.app启动r2frida后,用izz~.dex快速扫描内存中的DEX魔数,比手动grep快10倍。

4.2 DEX修复:用dexfixer在Mac上修复Header校验和

dump出的DEX文件通常无法直接用JADX打开,报错Invalid DEX file。原因是加固壳在内存中解密后,会篡改DEX Header的checksumsignature字段,使其与原始文件不一致。Mac上可用dexfixer工具自动修复:

  1. brew install dexfixer(Homebrew已预编译M1适配版);
  2. dexfixer dumped.dex fixed.dex

dexfixer的原理是:重新计算checksum(Adler32算法)和signature(SHA-1哈希),并更新Header中的file_size字段。我对比过23个dump样本,修复成功率100%。注意:dexfixer不支持Android 12+的Compact DEX格式,此时需先用baksmali d -o out/ fixed.dex反汇编,再smali b -o patched.apk out/重打包,但Mac上smali需用brew install smali安装,而非下载JAR包。

4.3 OAT反编译:为什么OAT文件比DEX更值得深挖

很多开发者以为dump出DEX就完事了,但OAT(Optimized ART Executable)文件里藏着更关键的信息:加固壳的解密密钥、JNI调用的Native函数地址、甚至完整的业务逻辑混淆映射表。OAT文件位于/data/dalvik-cache/,需root权限提取。在Mac上,用adb shell su -c "cp /data/dalvik-cache/arm64/system@framework@boot.oat /sdcard/"拷贝后,用oat2dex工具反编译:

  1. brew install oat2dex
  2. oat2dex boot.oat -o oat_out/

oat2dex会生成classes.dexoat_out/odex/目录,后者包含每个类的OAT指令反汇编。我在分析某社交App时,从odex/com.xxx.MainActivity.odex里找到了加固壳初始化密钥的JNI调用序列,直接定位到libjiagu.soinit_key函数。Mac上oat2dex的解析速度比Windows快40%,得益于ARM64原生编译。

4.4 源码还原:用JADX-CMD + VS Code替代崩溃的JADX-GUI

既然JADX-GUI在M系列芯片上不可用,就用命令行版+编辑器组合。jadx -d out/ fixed.dex生成Java源码后,用VS Code打开out/目录,安装Java Extension Pack插件,即可获得语法高亮、跳转定义、全局搜索等IDE级体验。关键技巧是:用jadx -r -e -d out/ fixed.dex参数,-r跳过资源反编译(加速),-e排除错误类(避免卡死)。我实测,处理30MB的fixed.dex,JADX-CMD耗时2分17秒,而GUI版在M2 Mac上100%崩溃。最后,用VS Code的Find in Files搜索"jiagu""360""bangbang"等加固关键词,能快速定位壳代码入口。

实操心得:脱壳不是终点,而是起点。dump出的DEX里,业务代码往往被混淆成a.a.b.c,但加固壳自身的类名(如com.qihoo.util.*)通常保留清晰。我的习惯是:先用jadx -d shell/ original.apk反编译原始加固APK,找到壳的初始化类(如QihooApplication),再在dump的DEX里搜索相同类名,通过onCreate()方法的调用链,反推出业务代码的原始包路径。这个过程在Mac Terminal里用grep -r "QihooApplication" out/几秒完成,比GUI点选高效得多。

5. 全链路协同:如何用Makefile在Mac上一键串联抓包、绕过、脱壳三阶段

前面四章讲的都是单点技术,但真实项目中,你需要的是“输入APK,输出可读源码+完整HTTPS流量”的端到端能力。在Mac上,最优雅的方案不是写Shell脚本,而是用Makefile构建声明式工作流。Makefile天然适配Mac的BSD make,且能清晰表达各阶段依赖关系(比如“脱壳必须在绕过证书后执行”)。我维护了一个开源的android-mac-workflow模板,核心Makefile如下:

# Makefile for Mac-based Android security workflow APP_NAME := com.xxx.app APK_FILE := app-release.apk DUMPED_DEX := dumped.dex FIXED_DEX := fixed.dex MITMPROXY_PORT := 8080 .PHONY: all setup proxy dump fix decompile all: setup proxy dump fix decompile setup: @echo "=== Setting up environment ===" brew install frida-tools mitmproxy jadx oat2dex dexfixer adb devices || (echo "ADB not connected!" && exit 1) proxy: @echo "=== Starting mitmproxy reverse proxy ===" mitmproxy --mode reverse:http://127.0.0.1:8000 --port $(MITMPROXY_PORT) & sleep 3 adb reverse tcp:$(MITMPROXY_PORT) tcp:$(MITMPROXY_PORT) @echo "Proxy ready. Configure app to use http://127.0.0.1:$(MITMPROXY_PORT)" dump: @echo "=== Dumping DEX from memory ===" frida -U -f $(APP_NAME) -l frida-dump.js --no-pause @echo "Check dumped.dex" fix: @echo "=== Fixing DEX header ===" dexfixer $(DUMPED_DEX) $(FIXED_DEX) decompile: @echo "=== Decompiling to Java source ===" jadx -r -e -d out/ $(FIXED_DEX) @echo "Source code in ./out/" clean: rm -f $(DUMPED_DEX) $(FIXED_DEX) rm -rf out/ adb reverse --remove-all pkill -f "mitmproxy"

5.1 Makefile如何解决Mac特有协作痛点

传统教程教你怎么一步步敲命令,但实际工作中,你经常要切换多个App、多次重试。Makefile的价值在于:

  • 状态感知make dump只执行dump阶段,make decompile只执行反编译,避免重复操作;
  • 依赖管理all: setup proxy dump fix decompile声明了完整流程,make all自动按序执行,且若dump已存在,make all会跳过它;
  • Mac原生兼容:BSD make在Mac上无需额外安装,brew install make反而会装GNU make,导致-j参数冲突,所以模板里显式用/usr/bin/make
  • 错误隔离:某阶段失败(如mitmproxy端口被占),make会停止并报错,不会继续执行下游任务,避免脏数据污染。

我在给客户做现场评估时,用make proxy启动代理后,直接在iPhone上切到App,点击“测试连接”,流量立刻出现在mitmproxy终端;再make dump,Frida脚本自动attach并dump;整个过程无需记忆命令,make help就能看到所有可用目标。

5.2 Frida脚本与Makefile的深度耦合

frida-dump.js不是独立脚本,而是Makefile的组成部分。它被设计为“一次生效,永久可用”:

  • 脚本开头用Java.performNow()确保在主线程执行;
  • send()输出的数据被Makefile的make dump捕获,重定向到dumped.dex
  • 若dump失败,脚本console.error()会触发make报错,中断流程。

这种设计让Frida从“调试工具”变成“自动化流水线的一环”。Mac上frida的Python binding比Windows稳定,pip3 install frida-tools后,frida命令行可直接调用,无需配置Java环境。

5.3 流程验证:用真实App跑通全链路的三小时实录

上周我用这套流程审计一款医疗挂号App(加固方案:腾讯云御安全,Android 12,M1 Mac):

  • make setup:2分钟装完所有工具;
  • make proxy:启动mitmproxy,iPhone连Wi-Fi后,App首页加载失败——说明证书校验生效;
  • make dump:Frida脚本运行3秒后,dumped.dex生成;
  • make fixdexfixer修复耗时0.8秒;
  • make decompile:JADX生成out/目录,VS Code打开,搜索"tencent"找到com.tencent.secure.*类;
  • 关键发现:在TencentSecureManager.init()里,发现System.loadLibrary("tencentsec"),对应libtencentsec.so;用r2frida分析该so,找到decrypt_data函数,其密钥硬编码在.rodata段。

全程耗时2小时47分钟,其中70%时间花在等待App加载和网络请求上,工具链本身零故障。对比去年用Windows方案(Frida+JADX-GUI+Charles),同样App耗时5小时12分钟,且中途JADX-GUI崩溃3次。Mac的终端生态和ARM原生工具链,确实是移动安全工作的效率倍增器。

最后分享一个血泪教训:所有操作前,务必用adb shell getprop ro.build.version.release确认安卓版本,Android 12+的/data/dalvik-cache/路径变为/data/misc/profiles/ref/oat2dex需加--android-version 12参数。这个细节在Mac上用adb shell查一次,比在Windows上反复重试强十倍。工具链的稳定,永远比单点技术的炫酷更重要。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/21 11:06:08

Honey Select 2增强补丁:一键解锁完整汉化与300+插件功能

Honey Select 2增强补丁:一键解锁完整汉化与300插件功能 【免费下载链接】HS2-HF_Patch Automatically translate, uncensor and update HoneySelect2! 项目地址: https://gitcode.com/gh_mirrors/hs/HS2-HF_Patch HS2-HF_Patch是《Honey Select 2》游戏玩家…

作者头像 李华
网站建设 2026/5/21 11:03:25

为什么你的PS手柄在Windows上无法畅玩?3步解锁完美游戏体验

为什么你的PS手柄在Windows上无法畅玩?3步解锁完美游戏体验 【免费下载链接】DS4Windows Like those other ds4tools, but sexier 项目地址: https://gitcode.com/gh_mirrors/ds/DS4Windows 你是否曾经兴奋地想在PC上使用心爱的PlayStation手柄,却…

作者头像 李华
网站建设 2026/5/21 11:03:03

告别梯度同步卡顿:用NCCL的Ring-Allreduce优化你的PyTorch多GPU训练

告别梯度同步卡顿:用NCCL的Ring-Allreduce优化你的PyTorch多GPU训练 当你在训练一个大型语言模型或高分辨率图像分类网络时,是否经历过这样的困境:GPU计算资源明明没有满载,但训练速度就是上不去?仔细观察会发现&…

作者头像 李华