App 数据爬取与协议分析
Web 端的数据在 HTML 或 Ajax 接口里,App 端的数据则全部走接口——没有页面源码可看,唯一的入口是网络流量。因此 App 爬取的第一步永远是抓包,而不是写代码。
⚖️ 合规边界(务必先读)
App 逆向的法律与合规风险显著高于 Web 爬取。以下行为超出正常技术研究范畴:
- 破解加固、脱壳、绕过 SSL Pinning 用于第三方商业 App —— 可能违反《著作权法》对技术保护措施的规定,以及 App 的用户协议
- 批量获取 App 内的用户信息 —— App 内数据多为登录后可见,抓取几乎必然触及《个人信息保护法》
- 规避付费或权限限制
正当场景:分析自己开发或所属组织的 App、有明确书面授权的安全测试、以学习协议机制为目的的原理研究。本篇按技术原理组织,不针对任何特定 App。
一、三条技术路线
| 路线 | 做法 | 效率 | 难度 | 稳定性 |
|---|---|---|---|---|
| 接口抓包 | 抓 HTTP(S) 流量,还原接口后用代码直接请求 | 最高 | 中 | 受签名/加固影响 |
| UI 自动化 | 驱动真机或模拟器操作界面,抓取屏幕内容 | 最低 | 低 | 最高,几乎不会被反制 |
| 协议逆向 | 反编译 App,还原签名/加密算法 | 高 | 最高 | 算法更新即失效 |
决策顺序:先抓包看接口是否可直接复用 → 参数有签名则评估逆向成本 → 逆向成本过高或数据量小则退回 UI 自动化。
二、HTTPS 抓包的原理
2.1 中间人代理的工作方式
抓 HTTP 明文很简单,难点全在 HTTPS。抓包工具的本质是受信任的中间人(MITM):
App ──TLS 握手──> 代理(用自签证书冒充服务端)
│ 代理作为客户端与真实服务端建立第二条 TLS 连接
└──TLS 握手──> 真实服务端代理持有两条独立的加密连接,因此能看到明文。成立的前提是 App 信任代理的自签 CA 证书——整个抓包体系的所有障碍都源于此。
2.2 证书信任的三道门槛
| 门槛 | 表现 | 处理 |
|---|---|---|
| 未安装 CA 证书 | 所有 HTTPS 请求失败 | 安装代理工具的 CA 证书到设备 |
| Android 7+ 用户证书不受信 | 证书装了但仍失败 | 需将证书装入系统证书目录(需 root),或 App 声明了 networkSecurityConfig 才信任用户证书 |
| SSL Pinning(证书固定) | 单个 App 失败,其他 App 正常 | 见 2.4 |
💡 Android 7 是关键分水岭
Android 7.0 起,App 默认只信任系统证书,用户手动安装的证书不再自动生效。这是「证书明明装了却抓不到包」最常见的原因。
可行方案:使用 root 设备将证书写入系统证书区、使用 Android 7 以下的模拟器、或在自有 App 中通过 network_security_config.xml 显式信任用户证书。
2.3 工具对比
| 工具 | 特点 | 适用 |
|---|---|---|
| Charles | 图形界面友好,断点改包方便 | 交互式探索、手动分析 |
| mitmproxy | 命令行 + Python 脚本化,可编程 | 自动化采集,本篇重点 |
| Fiddler | Windows 生态成熟 | Windows 用户 |
| Wireshark | 抓的是链路层原始包,能看非 HTTP 协议 | 私有 TCP 协议分析 |
mitmproxy 的三种运行形态:mitmproxy(终端交互界面)、mitmweb(浏览器界面)、mitmdump(无界面 + 脚本,用于自动化)。
# addon.py:mitmdump 的脚本接口,可直接把流量落库
import json
def response(flow):
"""每个响应都会回调,在这里筛选并处理目标接口。"""
if "/api/list" not in flow.request.pretty_url:
return
data = json.loads(flow.response.get_text())
for item in data.get("items", []):
save_to_db(item) # 边抓边入库,无需还原签名算法
def request(flow):
"""也可以改写请求,例如统一注入调试参数。"""
flow.request.headers["X-Debug"] = "1"mitmdump -s addon.py -p 8080💡 mitmdump 的战术价值
当接口签名逆向成本过高时,mitmdump + UI 自动化 是一个被低估的组合:让自动化脚本驱动 App 正常翻页(签名由 App 自己算),mitmdump 在旁边静默截获所有响应并入库。
完全不需要理解加密算法,代价是速度受限于 App 的真实操作节奏。
2.4 SSL Pinning
App 在代码里内置了服务端证书或公钥的指纹,握手时主动校验对方证书是否与内置值一致,代理的自签证书因此被拒绝。
| 实现层次 | 说明 |
|---|---|
| Java 层 | OkHttp 的 CertificatePinner、TrustManager 自定义校验 |
| Native 层 | 在 .so 里用 OpenSSL/BoringSSL 校验,隐蔽性更高 |
| 双向认证 | 服务端还要校验客户端证书,需从 App 中提取客户端证书 |
技术上的处理方式是用 Frida 在运行时 Hook 校验函数使其恒返回通过,或用 objection 的自动化脚本。这属于绕过 App 的安全机制,仅应在自有 App 或授权测试中使用。
若无授权,遇到 Pinning 的正确响应是改走 UI 自动化路线——它不触碰任何安全机制。
三、UI 自动化
不解析协议,直接驱动界面操作并读取屏幕上的内容。最笨但最稳:不受签名、加密、Pinning 影响,因为你走的就是真实用户路径。
3.1 方案对比
| 方案 | 原理 | 特点 |
|---|---|---|
| Appium | 基于 WebDriver 协议,驱动 UIAutomator2(Android)/XCUITest(iOS) | 跨平台、生态成熟、需元素定位 |
| Airtest + Poco | 图像识别 + UI 层级树 | 对无法定位的元素(游戏、Canvas)有优势 |
| uiautomator2 | 直接对接 Android UIAutomator | 轻量,纯 Android,API 简洁 |
| adb 命令 | input tap / shell | 最底层,适合简单固定操作 |
⚠️ Appium 的 driver 需单独安装
Appium 2.x 起,driver 与 plugin 从主程序中拆分,安装 Appium Server 后必须再单独装驱动(如 appium driver install uiautomator2),否则连接设备时会直接报找不到 driver。网上大量旧教程沿用 1.x 的一体化写法,不能直接照搬。
3.2 核心操作模式
# 以 uiautomator2 为例,展示 UI 自动化的通用范式
import uiautomator2 as u2
d = u2.connect() # 连接设备(adb 已识别)
d(text="搜索").click() # 按文本定位
d(resourceId="com.example:id/input").set_text("关键词")
d.press("enter")
d.xpath('//*[@resource-id="list"]').wait(timeout=10) # 显式等待
# 滚动加载:UI 自动化的数据获取核心是"边滚动边采集"
seen = set()
for _ in range(50):
for el in d.xpath('//*[@resource-id="item_title"]').all():
if el.text not in seen:
seen.add(el.text)
d.swipe_ext("up", scale=0.8) # 上滑加载更多三个必须处理的工程问题:
- 去重:滚动过程中同一元素会被反复读取,必须用业务主键去重。
- 终止判断:连续 N 次滚动都没有新增数据即视为到底,不能靠固定次数。
- 异常恢复:弹窗、广告、网络错误页会打断流程,需要在每轮循环前检查并处理当前界面状态。
3.3 与抓包组合使用
UI 自动化最强的用法不是读屏幕,而是只负责触发请求,数据由抓包层截获:
Airtest/Appium 驱动滚动 → App 自己发起带签名的请求 → mitmdump 截获 JSON → 入库这样既避开了签名逆向,又拿到了结构化的接口数据(而非从屏幕文本还原),是很多场景下性价比最高的方案。
四、协议逆向的技术路线
数据量大到 UI 自动化撑不住、且有正当授权时,才进入这一步。
4.1 工具链
| 阶段 | 工具 | 作用 |
|---|---|---|
| 查看资源与结构 | apktool | 解包 APK,得到资源和 smali |
| 反编译 Java | jadx(开源,首选)、JEB(商业) | 把 dex 还原成近似 Java 源码 |
| 分析 native 层 | IDA Pro、Ghidra(开源) | 反汇编 .so 文件 |
| 动态分析 | Frida、objection | 运行时 Hook 函数、打印参数与返回值 |
| 网络层 | mitmproxy、Wireshark | 确认逆向结果与真实流量一致 |
4.2 静态分析的定位方法
与 JS 逆向 的思路完全一致——先定位,再理解:
- 抓包确定目标参数名(如
sign); - jadx 打开 APK,全局搜索该参数名的字符串;
- 找到拼装请求的位置,回溯签名函数;
- 若签名函数是
native方法(声明带native关键字),说明实现在.so里,需转入 native 分析。
💡 加固会让静态分析直接失效
主流商业 App 普遍使用加固(壳)。表现为 jadx 打开后看不到业务代码,只有壳的加载器。
此时静态分析基本无效,只能转向动态分析——因为代码运行时必然要在内存中还原。这也是 Frida 成为主流工具的原因。
4.3 动态分析:Frida 的核心价值
Frida 在进程运行时注入 JS 引擎,可以 Hook 任意函数,打印其入参、返回值和调用栈。它跳过了「读懂代码」这一步,直接观察实际行为。
// Frida 脚本的通用范式:Hook Java 层方法,观察输入输出
Java.perform(function () {
const Target = Java.use("com.example.security.SignUtil");
Target.sign.overload("java.lang.String").implementation = function (input) {
const result = this.sign(input); // 调用原方法
console.log("[sign] 入参:", input);
console.log("[sign] 返回:", result);
return result;
};
});关键认知:Hook 的目标不一定是「破解」,更多时候是观察——搞清楚签名函数到底接收了什么格式的输入,往往比读懂算法本身更重要。多数逆向失败不是算法错了,而是入参拼接顺序错了。
4.4 RPC 模拟执行:不还原算法也能调用
算法在 native 层且复杂度极高时,还有一条捷径:不还原它,而是让 App 自己算。
Python 爬虫 ──HTTP──> 运行在设备上的 RPC 服务 ──调用──> App 内的签名函数
│
<──────────── 返回签名结果 ────────────────────┘实现方式有两种:Frida-RPC(用 Frida 把 App 内的函数暴露成可远程调用的接口)或 AndServer 类方案(在设备上跑一个 HTTP 服务,内部调用 App 的类方法)。
这与 JS 逆向 里的「Node RPC 服务」是完全相同的思想:脱离环境的成本过高时,就把环境本身变成一台可调用的服务器。
代价:需要设备常驻运行、并发能力受限于设备性能、设备故障即服务中断。
五、路线选择速查
| 场景 | 建议路线 |
|---|---|
| 接口无签名,抓包即可复现 | 直接用 httpx 请求接口 |
| 接口有签名,数据量大且有授权 | 协议逆向 → Python 复现或 RPC |
| 有 SSL Pinning,无授权 | UI 自动化 |
| App 已加固 | 动态分析(Frida),或直接 UI 自动化 |
| 数据量小(几千条以内) | UI 自动化,不值得逆向 |
| 需要结构化数据但不想逆向 | UI 自动化驱动 + mitmdump 截获(推荐组合) |
六、与 Web 爬取的技术对应关系
App 逆向的多数概念在 Web 端都有同构物,理解一边就能迁移另一边:
| App 端 | Web 端 | 共同本质 |
|---|---|---|
| 抓包分析接口 | Ajax 接口分析 | 找到真实数据源 |
| 签名参数逆向 | JS 加密参数逆向 | 还原参数生成算法 |
| Frida Hook | JS Hook(重写原生函数) | 运行时拦截观察 |
| Frida-RPC / AndServer | Node RPC 服务 | 让原环境当计算服务 |
| SSL Pinning | TLS 指纹校验 | 客户端身份验证 |
| UI 自动化(Appium) | 浏览器自动化(Playwright) | 走真实用户路径 |
| App 加固 | JS 混淆 / WASM | 提高静态分析成本 |
📚 模块与官方文档
抓包工具
| 工具 | 安装 | 官方文档 |
|---|---|---|
mitmproxy | pip install mitmproxy | docs.mitmproxy.org |
| mitmproxy Addon API | — | Addon 概览 · 事件钩子 · HTTP 对象 |
| Charles | 官网下载 | charlesproxy.com |
| Fiddler | 官网下载 | docs.telerik.com/fiddler |
| Wireshark | 系统包 | wireshark.org/docs |
UI 自动化
| 模块 | 安装 | 官方文档 |
|---|---|---|
Appium-Python-Client | pip install Appium-Python-Client | GitHub |
| Appium Server | npm i -g appium | appium.io/docs |
| UiAutomator2 Driver | appium driver install uiautomator2 | GitHub |
uiautomator2 | pip install uiautomator2 | GitHub |
airtest | pip install airtest | airtest.doc.io.netease.com |
pocoui | pip install pocoui | poco.readthedocs.io |
facebook-wda(iOS) | pip install facebook-wda | GitHub |
| adb | Android SDK | developer.android.com |
逆向分析
| 工具 | 类型 | 官方文档 |
|---|---|---|
frida / frida-tools | pip install frida-tools | frida.re/docs · JavaScript API |
objection | pip install objection | GitHub |
| jadx | 反编译 dex | GitHub |
| apktool | 解包 APK | apktool.org |
| Ghidra | 反汇编 so | ghidra-sre.org |
| JEB | 商业反编译 | pnfsoftware.com |
Android 平台文档
| 资源 | 说明 |
|---|---|
| 网络安全配置 | 证书信任规则,抓包受阻时必读 |
| Android 7 CA 证书变更 | 用户证书不再默认受信的官方说明 |
| UI Automator | UI 自动化的系统级基础 |
相关笔记
JavaScript 逆向(同构的算法还原与 RPC 思路)· 反爬对抗(代理与登录态)· 请求库详解(拿到接口后的高并发实现)