Frida实战Android协议分析:从Hook到算法还原完整指南

📅 发布时间:2026/7/17 1:51:59
Frida实战Android协议分析:从Hook到算法还原完整指南 1. 项目概述从“抓包”到“协议分析”的认知升级看到这个标题很多朋友的第一反应可能是“哦不就是用Frida去Hook某个App的登录或者请求把加密参数打印出来吗” 如果你也这么想那说明你可能还停留在“抓包破解”的初级阶段。我干了十多年移动安全可以很负责任地说“协议分析”和“抓包破解”之间隔着一整个逆向工程的深度。抓包破解目标往往是单一的找到那个关键的sign参数怎么算的或者把token解出来。这就像你只想拿到一把钥匙开门。而协议分析你要做的是理解整座建筑的安保系统钥匙加密算法是怎么造的门禁认证流程有几道报警器反调试、环境检测装在哪里以及不同房间业务接口之间的通信规则是什么这是一个系统性工程。这次我们要聊的就是用Frida这个“瑞士军刀”去完成一次完整的Android应用协议分析实战。这不仅仅是技术炫技更是理解一个现代App如何保护其数据资产、如何进行客户端与服务器对话的核心视角。无论是做安全评估、爬虫开发还是单纯想深入理解移动应用架构这套方法都能给你带来实打实的提升。文章最后我也会分享一些压箱底的实战心得和避坑指南并准备了一份小福利。2. 环境搭建与工具选型打造你的分析工作台工欲善其事必先利其器。一个稳定、高效的分析环境能让你在后续的逆向过程中事半功倍避免很多莫名其妙的错误。2.1 核心三件套Frida、ADB与模拟器/真机Frida这是我们绝对的主角。它是一个动态代码插桩框架简单说就是能在App运行时像打针一样把我们的代码“注射”进去从而监控、修改其行为。别被“动态”吓到它的Python脚本接口对新手非常友好。注意Frida分为服务端frida-server和客户端frida-tools。服务端需要运行在你的Android设备模拟器或真机上客户端则安装在你的分析电脑上。很多新手卡在第一步就是因为没搞清楚这个关系。ADB (Android Debug Bridge)这是连接电脑和Android设备的桥梁。所有向设备推送文件、执行Shell命令、端口转发都靠它。确保你的ADB版本不要太旧并且设备已开启“开发者选项”和“USB调试”。测试设备优先推荐Root过的真机。性能稳定行为最接近真实用户环境。如果条件有限模拟器是第二选择。这里有个关键点请使用Android 7.0 (API 24) 或更高版本的系统镜像。低版本系统尤其是5.1、6.0的frida-server可能会遇到各种兼容性问题比如注入失败、进程崩溃等对于新手而言这是巨大的劝退坑。像雷电模拟器、夜神模拟器都支持高版本Android直接选用即可。2.2 Frida环境部署详解安装客户端在你的电脑假设是Windows/Mac上打开命令行执行pip install frida-tools。这会把frida、frida-ps、frida-ls-devices等命令行工具装好。部署服务端去Frida的GitHub Releases页面根据你设备的CPU架构下载对应的frida-server。现在大部分手机都是arm64模拟器可能是x86_64。下载后解压得到一个名为frida-server-xx.x.x-android-xx的文件我们把它重命名为frida-server方便输入命令。使用ADB推送到设备adb push frida-server /data/local/tmp/进入设备Shell并启动它adb shell su # 获取Root权限 cd /data/local/tmp chmod 755 frida-server # 赋予执行权限 ./frida-server # 后台运行验证连接新开一个电脑命令行窗口执行frida-ps -U。如果能看到设备上运行的进程列表恭喜你环境搭建成功2.3 辅助工具让分析更高效Jadx/Ghidra静态反编译工具。在动态分析前先用它们看看目标App的Java/Kotlin代码大概结构找到你感兴趣的类和方法名。Jadx速度快适合快速浏览Ghidra更强大能处理复杂的混淆和Native代码。Burp Suite/Charles抓包工具。用于观察App的网络请求和响应直观地看到协议的上层表现。配合Frida可以绕过SSL Pinning证书绑定进行抓包。Objection一个基于Frida的命令行工具封装了很多常用功能如绕过SSL Pinning、内存搜索、执行命令可以作为快速测试的补充。环境搭好了我们就要开始思考面对一个陌生的App从哪里下第一刀。3. 协议分析的核心思路与策略拆解直接上手就Hook很容易在浩瀚的代码海洋里迷失。一个清晰的策略能指引你快速定位到关键代码。3.1 由外而内从网络流量切入这是最经典的入口。先用抓包工具抓到App的某个关键请求比如登录、获取主页数据。你会看到请求的URL、Headers、以及一个可能被加密的Body。关键线索URL和Path可能对应后端某个Controller或API路由。Headers重点关注Authorization、X-Sign、X-Timestamp、User-Agent等自定义字段。这些往往是客户端生成的。Body如果是JSON观察结构如果是二进制或乱码那肯定被加密或编码了。你的第一个分析目标就是找到生成这些Headers中签名Sign和加密Body的代码位置。3.2 关键方法定位的四大技巧字符串搜索在Jadx中直接搜索抓包看到的特征字符串比如URL的一部分、某个固定的Header值如User-Agent里的特定标识。这常常能直接定位到构建请求的代码附近。堆栈回溯法这是Frida动态分析的杀手锏。我们不需要知道确切的类名可以先Hook一些Android系统级的网络相关API。例如Hookokhttp3.OkHttpClient的newCall方法或者更底层的java.net.HttpURLConnection的getOutputStream。当App发起网络请求时我们的Hook脚本能打印出即时的调用堆栈Stack Trace。这个堆栈就像一份“犯罪现场地图”清晰地展示了从你的业务代码一步步如何调用到系统API的完整路径。顺着这条路径往上找很快就能定位到业务层的加密或签名方法。类名/方法名推测根据经验签名或加密类常包含Sign、Encrypt、Crypto、Security、HttpUtil、Network等关键词。利用Jadx的搜索功能全局搜索这些词。主动调用探测如果你通过静态分析怀疑某个类或方法是干这个的可以用Frida写个脚本主动去实例化那个类调用可疑方法传入一些测试数据看输出是否符合你的预期。3.3 面对混淆的应对策略现代App普遍会进行代码混淆类名和方法名都变成了a.a、b.b.c这种无意义字符。不要慌混淆只影响名称不影响逻辑和字符串常量但字符串也可能被加密。关注“不变”的东西系统API调用无论怎么混淆它调用MessageDigest.getInstance(“MD5”)、Cipher.getInstance(“AES/CBC/PKCS5Padding”)的代码是不会变的。你可以Hook这些系统加密类的方法。方法特征一个计算签名的方法其输入参数很可能包含请求的URL、参数Map、时间戳等其返回值很可能是一个String或byte数组。通过参数和返回值类型可以辅助判断。调用链路混淆不改变调用关系。通过堆栈回溯法找到的调用链即使每个节点都是混淆的你也能理清它们的执行顺序。思路清晰了接下来我们就进入真枪实弹的Hook实战环节。4. Frida Hook实战解剖一个签名函数假设我们通过抓包发现每个请求都有一个X-Sign的Header值像是一段Hex字符串。通过字符串搜索或堆栈回溯我们定位到了一个疑似的方法com.example.obfuscated.a.b(String str1, String str2)。4.1 编写基础Hook脚本我们来写一个Frida Python脚本挂钩这个方法打印它的输入和输出。# hook_sign.py import frida import sys # 定义我们的JavaScript Hook代码 jscode Java.perform(function () { // 定义要Hook的类和方法 var targetClass Java.use(com.example.obfuscated.a); // Hook 指定的方法。注意参数类型要和实际匹配。 targetClass.b.overload(java.lang.String, java.lang.String).implementation function (str1, str2) { // 调用原方法前打印入参 console.log([*] com.example.obfuscated.a.b() called!); console.log( str1: str1); console.log( str2: str2); // 调用原方法获取结果 var result this.b(str1, str2); // 打印结果 console.log( result: result); console.log( result (hex): bytesToHex(result.getBytes())); // 返回结果确保App正常运行 return result; }; // 一个将字节数组转为Hex字符串的辅助函数 function bytesToHex(bytes) { var hex []; for (var i 0; i bytes.length; i) { var current bytes[i] 0xff; var hexCode current.toString(16); if (hexCode.length 2) { hexCode 0 hexCode; } hex.push(hexCode); } return hex.join(); } }); def on_message(message, data): if message[type] send: print(f[*] {message[payload]}) else: print(message) # 连接到设备并附加到目标进程 process frida.get_usb_device().attach(目标App包名) # 替换为实际包名如 com.example.app script process.create_script(jscode) script.on(message, on_message) print([*] Hook脚本加载中...) script.load() sys.stdin.read() # 保持脚本运行运行这个脚本python hook_sign.py。然后在手机上操作App触发网络请求。如果Hook成功你会在控制台看到对应的输入输出。4.2 深入分析参数与算法现在你看到了str1和str2的内容。它们可能是str1: 排序后的请求参数字符串。str2: 一个固定的密钥或当前时间戳。result就是计算出的签名。接下来需要分析这个方法内部的逻辑。你可以进一步Hook内部方法在b方法内部它可能调用了MessageDigest.update()、Cipher.doFinal()等。可以继续Hook这些方法看具体的数据流转。查看返回值类型如果结果是String可能是Hex或Base64如果是byte数组可能就是原始的哈希值。模拟计算验证根据打印的输入尝试在Python或JavaScript中用常见的算法MD5, SHA256, HMAC模拟计算看能否得到相同的结果。这是确认你分析是否正确的关键一步。4.3 处理复杂场景Native层加密越来越多的App将核心加密算法放在Native层C/C代码通过JNI调用。如果你发现Java层的签名方法只是一个“外壳”内部调用了native String doEncrypt(String data)那么战场就需要转移到so库。定位so库在Jadx中查看类的静态初始化块static {}通常有System.loadLibrary(“crypto”)这样的语句这就是加载的so库名。在App的lib目录下找到对应的libcrypto.so。使用Frida Hook Native函数这需要一些C和ARM/汇编的基础。你可以使用Frida的Interceptor.attach来Hook so库中的导出函数。关键步骤是找到函数的准确地址这可能需要分析so的导出表。// Hook Native函数示例 var baseAddr Module.findBaseAddress(libcrypto.so); var funcAddr baseAddr.add(0x1234); // 假设的函数偏移地址 Interceptor.attach(funcAddr, { onEnter: function(args) { console.log([*] Native doEncrypt called!); // args[0], args[1]... 对应参数 // 可以打印内存内容 console.log(hexdump(args[0])); }, onLeave: function(retval) { console.log([*] Native doEncrypt returned.); console.log(hexdump(retval)); } });工具辅助使用Ghidra或IDA Pro反编译so库分析doEncrypt函数的逻辑结合Frida的动态打印理解其算法。5. 协议还原与模拟从分析到实现分析的目的不是为了看而是为了用。当我们理解了签名算法和加密流程后下一步就是脱离App用Python或其他语言重新实现这套协议。5.1 重构签名算法根据Hook得到的信息用Python还原算法。例如如果发现是MD5(参数1密钥时间戳)import hashlib import time def generate_sign(param1, secret_key): timestamp str(int(time.time())) raw_string param1 secret_key timestamp # 注意编码通常使用UTF-8 m hashlib.md5() m.update(raw_string.encode(utf-8)) return m.hexdigest() # 使用 sign generate_sign(sorted_params, your_secret_key_from_hook) print(fX-Sign: {sign})5.2 处理加密请求体如果请求体被加密如AES你需要还原出密钥Key和初始向量IV它们可能是固定的也可能是动态生成的如从服务器获取。通过Hook相关函数获取。加密模式通常是AES/CBC/PKCS5Padding。数据格式加密前数据可能是JSON字符串也可能是某种二进制协议如Protobuf。还原后你的Python代码应该能生成和App一模一样的加密数据。5.3 构建完整的请求客户端将还原的签名和加密函数与requests库结合构建一个能模拟App通信的Python客户端。import requests import json from your_crypto_module import generate_sign, encrypt_body class AppClient: def __init__(self, base_url, secret_key): self.base_url base_url self.secret_key secret_key self.session requests.Session() # 可以在这里设置一些固定的Headers如User-Agent self.session.headers.update({ User-Agent: Dalvik/2.1.0 (Linux; U; Android 11; ...), Content-Type: application/json; charsetUTF-8 }) def make_request(self, api_path, params): # 1. 生成签名 sign generate_sign(params, self.secret_key) # 2. 构建Headers headers { X-Sign: sign, X-Timestamp: str(int(time.time())) } # 3. 加密请求体 (如果需要) raw_body json.dumps(params) encrypted_body encrypt_body(raw_body) # 4. 发送请求 url self.base_url api_path # 注意如果body被加密Content-Type可能需要改变 response self.session.post(url, dataencrypted_body, headersheaders) # 5. 解密响应 (如果需要) decrypted_data decrypt_body(response.content) return json.loads(decrypted_data)这个客户端一旦调通就意味着你完全掌握了该App的客户端协议可以用于自动化测试、数据采集或安全审计。6. 进阶挑战与对抗那些年我们踩过的坑实战从来不会一帆风顺。App的开发者们也会采用各种手段来增加分析难度。6.1 反调试与Frida检测这是最常见的对抗手段。App会尝试检测自己是否被调试或注入。常见检测点检查调试器连接android.os.Debug.isDebuggerConnected()检查TracerPid读取/proc/self/status或/proc/self/task/pid/status查看TracerPid字段是否为0。检查Frida特征检测frida-agent.so等文件、特定端口如27042是否开放、特定内存映射等。绕过方法Hook检测函数直接HookisDebuggerConnected()等方法让其返回false。修改内存数据使用Frida的Memory.writeAPI直接修改/proc/self/status在内存中的映射将TracerPid写为0。使用定制版或隐藏Frida修改frida-server和frida-agent的文件名、端口号抹去特征。社区有一些工具可以帮助做这件事。使用“强隐藏”模式一些高级的Frida脚本或工具如objection的android hide命令可以尝试隐藏这些痕迹。6.2 代码混淆与虚拟机壳除了简单的类名混淆还有更高级的保护控制流扁平化打乱代码执行顺序增加静态分析难度。动态分析Frida不太受影响。虚拟机壳VMP将关键代码转换为自定义的字节码在私有虚拟机中执行。这是目前最强的保护之一。对付VMP动态分析几乎是唯一途径但Hook点变得极其难找可能需要深入分析VMP引擎本身。这已经属于高级逆向范畴。6.3 协议动态化与端云协同最头疼的情况是密钥、算法甚至协议结构都不是写死在客户端的。动态密钥每次启动从服务器获取或者根据时间、设备信息动态计算。算法抽离核心算法逻辑以脚本如JS、WASM形式下发在客户端沙盒内执行。端云协同最关键的签名步骤放在服务器端客户端只负责传输原材料。这种情况下客户端协议分析只能走到“收集原材料”这一步无法完全还原。面对这种情况我们的策略需要调整从“完全还原”转向“协议理解与漏洞挖掘”。重点分析客户端收集了哪些信息设备指纹、传感器数据、用户行为作为“原材料”这些信息的收集方式是否存在隐私泄露或伪造的风险客户端与服务器端的信任边界在哪里是否存在逻辑漏洞比如可以重放请求、篡改原材料顺序等7. 实战心得与安全边界最后分享几点干了这么多年总结出来的心得这些在官方文档里可找不到。心态比技术更重要协议分析像解谜90%的时间在寻找和试错10%的时间在收获成功的喜悦。保持耐心善于从失败崩溃、无输出中提取信息错误日志、崩溃地址每一个异常都是线索。记录一切建立一个分析笔记。记录你尝试过的Hook点、观察到的现象、猜测和验证结果。好记性不如烂笔头尤其是当分析中断几天后再回来时详细的笔记能让你快速恢复上下文。由简入繁逐步深入不要一开始就想着Hook整个加密流程。先从最外层的、最明显的网络请求构造器开始Hook打印堆栈一步步往里追。像剥洋葱一样一层层深入。理解业务逻辑最好的逆向工程师也是半个业务专家。尝试理解这个App在做什么这个请求是为了完成什么用户操作。业务逻辑能帮你推测代码逻辑比如“登录”后必然要“保存会话”那么接下来找SessionManager相关的类就很有方向。法律与道德底线这是我们从业者必须时刻绷紧的弦。协议分析技术是一把双刃剑。授权测试仅对你拥有合法测试权限的App进行分析如公司内部产品、你个人开发的App、明确提供漏洞奖励计划的产品。学习与研究以学习移动安全技术、研究防护方案为目的对公开的App进行技术探索是业界通行的做法但绝不能将分析成果用于任何非法、侵权或破坏性活动包括但不限于制作外挂、恶意爬虫、盗取用户数据、攻击服务器等。尊重知识产权分析过程中接触到的代码、算法是开发者的智力成果。我们的目标是理解其原理和实现而非复制窃取。关注数据隐私在分析过程中可能会接触到模拟的用户数据或设备信息。这些数据仅应用于技术验证并应立即销毁不得留存或泄露。技术本身无罪但使用技术的人需要为其后果负责。将你的技能用在正道上比如帮助企业提升App安全性、开发更高效的合法自动化工具或是像我们这样将经验分享出来让更多人了解安全、重视安全这才是这份技术最大的价值所在。