移动端协议逆向:从抓包到还原加密通信的全流程
一、加密通信:App 的最后一道防线,也是逆向的起点
现代移动 App 几乎不再用明文 HTTP。登录、支付、关键业务接口,都套了自定义加密层。请求体 AES 加密,签名用 HMAC-SHA256,部分参数还做了 RSA 公钥加密。普通的 HTTPS 抓包,看到的全是密文。
这种"应用层加密"是 App 的最后一道防线。即使客户端被反编译,攻击者仍需还原密钥与算法,才能伪造合法请求。但实战中,这道防线常常撑不过几小时。
逆向的入口往往是抓包。开发者以为加了 SSL Pinning 就高枕无忧,但只要设备 root 或越狱,Frida 几行脚本就能绕过证书校验。绕过 Pinning 之后,明文 HTTP 流量重新可见。但应用层加密仍然挡在前面。
接下来是定位加密函数。静态分析可以找到加密 SDK 的入口,但混淆、加壳、Native 化让纯静态寸步难行。Frida 动态 Hook 是更高效的选择:直接 hook 加密 API,拿到明文与密钥,反推算法。
最后一环是算法还原。把抓到的密钥、IV、模式、填充、签名顺序整理清楚,就能用 Python 复现整个协议。这一步是逆向的终点,也是漏洞验证、协议重放、自动化测试的起点。
整个过程看似线性,实则反复横跳。抓包没看到完整字段,回过头 hook;hook 拿不到密钥,回过头静态分析;算法还原对不上,再回去抓更多样本。逆向从来不是一条直线,是不断收敛的螺旋。
二、从流量到算法的四个阶段
把协议逆向拆成阶段,每阶段有明确产出。
抓包——看到流量。定位——找到加密函数。Hook——提取密钥与参数。还原——用代码复现协议。每阶段产出要可固化,避免下次重做时从零开始。
这里有个容易被忽略的事:样本采集。光抓一两条请求,不足以反推算法。必须采集多组明文-密文对,覆盖不同输入长度、不同字符集。这样才能区分 AES-CBC 与 AES-ECB,才能识别填充模式,才能验证签名顺序。少一组样本,算法还原就可能差一个细节。
三、全流程实现:抓包、Hook 与算法还原
下面是一段组合 Frida Hook 与 Python 还原的实战脚本。它把抓包、Hook、还原三步串起来:
import asyncio import hashlib import json import time from pathlib import Path # Frida Hook 脚本:hook javax.crypto.Cipher 拿到明文与密钥 FRIDA_SCRIPT = """ Java.perform(function() { var Cipher = Java.use("javax.crypto.Cipher"); // hook init,捕获模式与密钥 Cipher.init.overload( "int", "java.security.Key" ).implementation = function(mode, key) { var modeStr = mode === 1 ? "ENCRYPT" : "DECRYPT"; var keyBytes = key.getEncoded(); send({ type: "init", mode: modeStr, key: Java.array(keyBytes).toString() }); return this.init(mode, key); }; // hook doFinal,捕获明文与密文 Cipher.doFinal.overload("[B").implementation = function(input) { var result = this.doFinal(input); send({ type: "doFinal", input: Java.array(input).toString(), output: Java.array(result).toString() }); return result; }; }); """ # 样本存储目录 SAMPLE_DIR = Path("./samples") SAMPLE_DIR.mkdir(exist_ok=True) def save_sample(sample: dict) -> str: # 样本以 JSON 落盘,便于后续批量分析 digest = hashlib.sha256( json.dumps(sample, sort_keys=True).encode() ).hexdigest()[:12] path = SAMPLE_DIR / f"sample_{digest}.json" path.write_text( json.dumps(sample, ensure_ascii=False, indent=2), encoding="utf-8" ) return digest async def capture_samples(duration: float = 30.0) -> list[dict]: # 占位:真实环境通过 Frida RPC 接收样本 # 这里模拟几组明文-密文对,结构对齐真实输出 samples = [] for i in range(5): sample = { "time": time.time_ns(), "mode": "ENCRYPT", "key": bytes([0x10 + i] * 16).hex(), "input": f"test_payload_{i}".encode().hex(), "output": bytes([0x20 + i] * 32).hex(), } digest = save_sample(sample) sample["digest"] = digest samples.append(sample) await asyncio.sleep(0.05) return samples def identify_mode(samples: list[dict]) -> str: # 通过样本判断加密模式:相同明文是否产生相同密文 # ECB:相同明文块产生相同密文块;CBC:不同 # 这里只做最简判定,实战需更细致 if not samples: return "unknown" return "CBC" if len({s["output"] for s in samples}) > 1 else "ECB" def reproduce_protocol(samples: list[dict]) -> dict: # 用还原出的参数复现协议,验证样本一致性 try: from Crypto.Cipher import AES # pycryptodome from Crypto.Util.Padding import pad except ImportError: return {"error": "pycryptodome 未安装", "verified": False} mode = identify_mode(samples) if not samples or mode != "CBC": return {"mode": mode, "verified": False, "reason": "样本不足或模式不符"} s = samples[0] key = bytes.fromhex(s["key"]) iv = key # 实战需单独 hook IV,此处仅作占位 cipher = AES.new(key, AES.MODE_CBC, iv) # 用样本明文加密,对比是否与样本密文一致 expected = bytes.fromhex(s["output"]) actual = cipher.encrypt(pad(bytes.fromhex(s["input"]), 16)) return { "mode": mode, "key_len": len(key) * 8, "verified": actual == expected, } async def demo(): samples = await capture_samples() print(f"采集 {len(samples)} 组样本") result = reproduce_protocol(samples) print(json.dumps(result, ensure_ascii=False, indent=2))Frida 脚本 hook 加密 API,拿到密钥与明文;样本多组采集后落盘,便于批量分析;算法识别通过样本对比而非猜;还原后用样本验证一致性,验证不通过则继续 Hook。整个流程可重复执行,每次迭代都让算法更接近真实。
四、逆向的边界:反调试、合规与版本迭代
移动端逆向有三条硬边界,越线即风险。
反调试是技术边界。商业 App 普遍集成反调试、反 Frida 检测。检测到调试器或注入框架就闪退或上报。绕过这些检测需要持续对抗,且不可一劳永逸。每次 App 版本更新都可能引入新的检测点,逆向工程必须跟着版本演进。说实话,这是个体力活。
合规是法律边界。逆向他人 App 用于破解、外挂、协议重放,在国内多数情况下违法。合法的逆向场景仅限:自有 App 的安全自测、授权渗透、学术研究。任何逆向都要先确认授权范围与法律依据,否则技术上能做不等于可以做。这个坑踩进去,代价不是技术层面的。
版本迭代是工程边界。今天还原出的协议,下个版本可能就失效。密钥换了,算法换了,签名顺序换了。逆向产物必须随版本管理,每份样本标注对应 App 版本。否则旧的逆向结论被用到新版本上,会直接构造出非法请求,触发风控或被识别为攻击。
还有一条常被忽视:还原后的协议不要直接用于生产重放。验证完算法即可,重放只在测试环境做。用还原协议去打真实服务器,即使初衷是测试,也可能触发法律与风控红线。逆向的产出是"理解"而非"利用"。
最后是数据留存。逆向过程中拿到的密钥、token、用户样本,都是高敏感数据。交付后必须清理,只保留算法描述与脱敏样本。这些中间产物若长期留存,本身就是新的泄露面。
五、总结
移动端协议逆向,四步走:绕过 SSL Pinning、定位加密函数、Frida Hook 拿密钥、Python 还原算法。听起来简单,实操中每步都可能卡住。抓包看不到完整字段、hook 拿不到密钥、算法还原对不上——这些是常态,不是意外。
反调试、合规、版本管理,三条线不能碰。逆向的技术产出是"理解加密设计",不是为了"利用协议"。最后记着把中间产物清干净,密钥和 token 留着就是给自己埋雷。逆向的价值,在于让 App 在被反向审视后,加密设计仍然能站住脚。