做过 Web 端和 App 端逆向的人都知道,Web 端的逆向分析工具可以说非常统一 —— Chrome 自带的 DevTools 就够了,既能抓包,又能调试,还能替换资源、进行 Trace。虽然谷歌未必是为了我们逆向分析才做得这么好,但毫无疑问,Web 端的逆向门槛确实低了很多。
对于 Web 端的逆向分析,之前我已经开发了基于 chrome-devtools-mcp 的 js-reverse-mcp,基本上可以完成从抓包到打断点、到执行 JS 的全链路逆向分析,已经相当完善了。
然而 App 端就完全不同了。
App 端的逆向分析工具碎片化严重:从抓包到静态分析到动态调试,从 Java 层到 So 层,每个环节都有自己的工具,而且往往多个工具功能交叉。比如抓包,Fiddler、Charles、Reqable、r0capture、eCapture 其实都能完成,各有各的优势。
这也意味着,全链路 AI 进行移动端逆向分析,很难有一个统一的解决方案。
目前社区在抓包之后的分析环节,已经有了各自的方案:Java 层有 jadx-mcp,So 层有 ida-pro-mcp。但在定位入口这个环节,更多还是凭借抓包工具加上各自的经验。
这其实是全链路逆向分析的最后一块拼图。
补上这块拼图,AI 自己独立分析一个 App 才真正成为可能。这也是我研发抓包 MCP 的初衷。
基于上述背景,我是如何考虑技术选型的呢?
首先排除几个方案:
除此之外,eCapture 还有一个核心问题:
简单说:
基于以上原因,最终选择 Mitmproxy 作为抓包的基础设施。
选好了 Mitmproxy 作为基础设施,接下来就是核心问题:如何让大模型高效地分析抓到的流量?
这里有三个关键问题需要解决:
下面逐一分析。
做过 App 逆向的都知道,随便打开一个 App 操作几分钟,轻松产生几百个请求。
算笔账:
而 Claude 的上下文窗口是有限的,即使是 200K token 的模型,也撑不住这么大的数据量。更别说 token 是要花钱的。
所以,不能把所有请求的完整数据一股脑喂给大模型。
怎么解决?这里可以参考谷歌的思路。
通过分析 Chrome DevTools Protocol 的设计可以发现,谷歌的方案非常巧妙:
这样,大模型先看"目录",定位到感兴趣的请求后,再获取"正文"。
按照这个思路,我们把每个请求封装成一个对象:
大模型调用 traffic_list 时,只返回元数据(ID、URL、状态码等),每条记录只有几百字节。定位到目标请求后,再通过 traffic_get_detail 获取完整信息。
效果:200 个请求的元数据列表,压缩到几十 KB,大模型轻松处理。
解决了数据组织的问题,还有一个更现实的问题:用户根本不知道要找哪个请求。
想想看,用户来问的往往是这样的:
用户知道的是业务需求,不知道的是具体哪个 URL。如果用户知道是哪个 URL,还用得着这个 MCP 吗?
但问题是,一个 App 可能有:
大模型不可能把 200 个请求全看一遍,那样既慢又贵。
那人类是怎么做的?
有经验的逆向工程师拿到抓包数据后,通常是这样操作的:
这就是分层筛选的思路:先粗筛缩小范围,再精筛定位目标。
参考谷歌 CDP 的设计范式,加上我对 App 逆向的经验,最终设计出这套方案:
通过 traffic_list 工具,按元数据快速过滤:
逆向经验:
通过 traffic_search 工具,搜索请求/响应内容:
搜索范围支持:
响应体太大怎么办?分片读取,避免上下文溢出:
用户: "帮我找酷安 App 的搜索接口"
AI 执行:
设计原则:先缩小范围,再精确定位,避免全量读取。
你可能会问:为什么抓包和 MCP 要分成两个进程?合成一个不行吗?
技术上当然可以,但从用户体验考虑,分开更好:
代理进程(用户手动启动):
MCP 进程(Claude 自动调用):
这样分离的好处:
但分成两个进程后,就有个问题:代理抓到的数据,MCP 怎么读取?
几个方案:
所以我们的方案是:SQLite。
为什么不用 Redis?
让用户装 Redis 只为了一个抓包工具?杀鸡用牛刀。SQLite 零配置,开箱即用。
基于以上设计,我们完成了基于 Mitmproxy 的 Android 抓包 MCP:
在这个基础上,构建一个专属于 Android 逆向的 AI Agent 不再是难题。
结合 jadx-mcp(Java 层)、ida-pro-mcp(So 层),全链路 AI 逆向分析的拼图已经完整。
未来,逆向分析可能真的只需要大模型就够了。
项目地址: 0dfK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6*7K9r3W2*7K9s2g2G2k6r3g2E0j5h3!0Q4x3V1k6S2L8X3c8J5L8$3W2V1i4K6g2X3M7s2u0G2P5s2W2Q4y4h3k6E0j5%4l9`.
| 对比维度 |
eCapture |
Mitmproxy |
| 数据格式 |
碎片化的 TCP/TLS 字节流,需要 TShark 重组解析 |
原生 Python 对象,flow.request.json() 直接拿结构化数据 |
| 请求-响应关联 |
很难把请求 A 和响应 B 精准对应(尤其是并发时) |
天生知道哪个响应对应哪个请求 |
| 对 AI 友好度 |
低,需要大量预处理 |
高,零损耗直接喂给 LLM |
@dataclass
class TrafficRecord:
id: str
timestamp: float
method: str
url: str
domain: str
status: int
resource_type: str
size: int
time_ms: float
request_headers: dict
request_body: bytes
response_headers: dict
response_body: bytes
timing: dict
error: str | None
@dataclass
class TrafficRecord:
id: str
timestamp: float
method: str
url: str
domain: str
status: int
resource_type: str
size: int
time_ms: float
request_headers: dict
request_body: bytes
response_headers: dict
response_body: bytes
timing: dict
error: str | None
全量流量 → 粗筛(元数据)→ 精筛(内容搜索)→ 目标请求
全量流量 → 粗筛(元数据)→ 精筛(内容搜索)→ 目标请求
全量流量 → 粗筛(元数据)→ 精筛(内容搜索)→ 目标请求
全量流量 → 粗筛(元数据)→ 精筛(内容搜索)→ 目标请求
| 筛选条件 |
说明 |
示例 |
filter_domain |
域名(支持通配符) |
*api*.coolapk.com |
filter_type |
资源类型 |
XHR(API 通常是 XHR) |
filter_status |
状态码 |
2xx, 4xx, 500-599 |
filter_url |
URL 关键词 |
search, login |
traffic_search(
keyword="搜索词",
search_in=["response_body"],
method="POST",
domain="%coolapk%"
)
traffic_search(
keyword="搜索词",
search_in=["response_body"],
method="POST",
domain="%coolapk%"
)
traffic_read_body(
request_id="req-5",
field="response_body",
offset=0,
length=4000
)
traffic_read_body(
request_id="req-5",
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 3天前
被执着的猫编辑
,原因: