439 lines
11 KiB
Markdown
439 lines
11 KiB
Markdown
# Android eCapture 抓包 runbook
|
||
|
||
本文记录本项目中已验证可复现的 Android eCapture 抓包流程。核心结论:
|
||
|
||
- 不需要代理、不需要安装 CA、不需要 Hook APP。
|
||
- 依赖设备 root、eBPF/uprobe 和 eCapture hook BoringSSL/Conscrypt。
|
||
- 当前设备普通 `adb shell` 看不到 `su`,但可通过 `bin.mt.termex` 的 root 授权间接执行。
|
||
|
||
## 1. 已验证环境
|
||
|
||
- 目标包名:`com.ct.client`
|
||
- 目标 UID:`10409`
|
||
- root 方案:SukiSU Ultra
|
||
- 已授权 root 的 APP:`bin.mt.termex`
|
||
- eCapture 版本:`v2.5.2`
|
||
- 设备 eCapture 路径:`/data/local/tmp/ecapture`
|
||
- 设备 text 启动脚本:`/data/local/tmp/run_ecapture_dx.sh`
|
||
- 本地 text 启动脚本:`tools/ecapture/run_ecapture_dx.sh`
|
||
- 设备 pcap 启动脚本:`/data/local/tmp/run_ecapture_dx_pcap.sh`
|
||
- 本地 pcap 启动脚本:`tools/ecapture/run_ecapture_dx_pcap.sh`
|
||
- eCapture 目标 TLS 库:
|
||
- `/apex/com.android.conscrypt/lib64/libssl.so`
|
||
|
||
## 2. root 权限获取方式
|
||
|
||
普通 `adb shell` 下 `su` 不可见,这是本设备的正常现象:
|
||
|
||
```sh
|
||
adb shell id
|
||
adb shell su -c id
|
||
```
|
||
|
||
预期现象:
|
||
|
||
```text
|
||
uid=2000(shell)
|
||
su: inaccessible or not found
|
||
```
|
||
|
||
正确方式是通过已授权 root 的 `bin.mt.termex` 进入同一 root 授权上下文:
|
||
|
||
```sh
|
||
adb shell 'run-as bin.mt.termex id'
|
||
adb shell 'run-as bin.mt.termex /system/bin/su -c id'
|
||
```
|
||
|
||
成功时第二条应返回类似:
|
||
|
||
```text
|
||
uid=0(root) gid=0(root) ... context=u:r:ksu:s0
|
||
```
|
||
|
||
如果这里失败,优先检查:
|
||
|
||
1. SukiSU Ultra 是否仍启用。
|
||
2. `bin.mt.termex` 是否仍有 root 授权。
|
||
3. 命令必须走 `/system/bin/su`,不要依赖普通 shell PATH。
|
||
|
||
## 3. 一次性部署 eCapture
|
||
|
||
确认设备在线:
|
||
|
||
```sh
|
||
adb devices
|
||
```
|
||
|
||
确认目标 UID:
|
||
|
||
```sh
|
||
adb shell 'cmd package list packages -U | grep com.ct.client'
|
||
```
|
||
|
||
部署 eCapture 二进制和启动脚本:
|
||
|
||
```sh
|
||
adb push tools/ecapture/ecapture-v2.5.2-android-arm64/ecapture /data/local/tmp/ecapture
|
||
adb shell 'chmod 755 /data/local/tmp/ecapture'
|
||
|
||
adb push tools/ecapture/run_ecapture_dx.sh /data/local/tmp/run_ecapture_dx.sh
|
||
adb shell 'chmod 755 /data/local/tmp/run_ecapture_dx.sh'
|
||
|
||
adb push tools/ecapture/run_ecapture_dx_pcap.sh /data/local/tmp/run_ecapture_dx_pcap.sh
|
||
adb shell 'chmod 755 /data/local/tmp/run_ecapture_dx_pcap.sh'
|
||
```
|
||
|
||
可选:检查设备是否支持 eBPF/uprobe:
|
||
|
||
```sh
|
||
adb shell 'run-as bin.mt.termex /system/bin/su -c "mount | grep -E \"tracefs|bpf\"; zcat /proc/config.gz | grep -E \"CONFIG_BPF=|CONFIG_UPROBES=|CONFIG_DEBUG_INFO_BTF=\" 2>/dev/null"'
|
||
```
|
||
|
||
## 4. 启动抓包
|
||
|
||
### 4.1 text 模式:日常抽明文 body
|
||
|
||
直接运行项目脚本:
|
||
|
||
```sh
|
||
adb shell 'run-as bin.mt.termex /system/bin/su -c /data/local/tmp/run_ecapture_dx.sh'
|
||
```
|
||
|
||
成功时输出:
|
||
|
||
```text
|
||
ecapture_pid=<pid>
|
||
```
|
||
|
||
脚本实际执行内容:
|
||
|
||
```sh
|
||
nohup /data/local/tmp/ecapture tls \
|
||
-m text \
|
||
--uid=10409 \
|
||
--libssl=/apex/com.android.conscrypt/lib64/libssl.so \
|
||
--ssl_version="boringssl 1.1.1" \
|
||
> /data/local/tmp/ecapture_dx.log \
|
||
2> /data/local/tmp/ecapture_dx.err &
|
||
```
|
||
|
||
检查进程:
|
||
|
||
```sh
|
||
adb shell 'ps -A | grep ecapture || true'
|
||
```
|
||
|
||
### 4.2 pcapng 模式:还原 HTTP/2 path/header
|
||
|
||
`text` 模式足够抽 JSON body,但 HTTP/2 的 `:authority/:path`
|
||
依赖 HPACK 状态,文本日志不适合可靠还原。
|
||
|
||
需要闭合真实业务 URL 时,用 pcapng 模式:
|
||
|
||
```sh
|
||
adb shell 'run-as bin.mt.termex /system/bin/su -c /data/local/tmp/run_ecapture_dx_pcap.sh'
|
||
```
|
||
|
||
默认网卡是 `wlan0`。如果需要指定接口:
|
||
|
||
```sh
|
||
adb shell 'run-as bin.mt.termex /system/bin/su -c "/data/local/tmp/run_ecapture_dx_pcap.sh wlan0"'
|
||
```
|
||
|
||
可先用 root 查看设备接口:
|
||
|
||
```sh
|
||
adb shell 'run-as bin.mt.termex /system/bin/su -c "ip -o link show"'
|
||
```
|
||
|
||
pcap 脚本实际执行:
|
||
|
||
```sh
|
||
nohup /data/local/tmp/ecapture tls \
|
||
-m pcap \
|
||
-i wlan0 \
|
||
-w /data/local/tmp/ecapture_dx.pcapng \
|
||
--uid=10409 \
|
||
--libssl=/apex/com.android.conscrypt/lib64/libssl.so \
|
||
--ssl_version="boringssl 1.1.1" \
|
||
tcp port 443 \
|
||
> /data/local/tmp/ecapture_dx_pcap.log \
|
||
2> /data/local/tmp/ecapture_dx_pcap.err &
|
||
```
|
||
|
||
注意:pcap 脚本启动时会覆盖
|
||
`/data/local/tmp/ecapture_dx.pcapng`。如果要保留上一轮,先拉回本地。
|
||
|
||
## 5. 停止并拉取日志
|
||
|
||
PowerShell 模板:
|
||
|
||
```powershell
|
||
$stamp=Get-Date -Format 'yyyyMMdd_HHmmss'
|
||
$out="out\ecapture_case_$stamp"
|
||
New-Item -ItemType Directory -Force -Path $out | Out-Null
|
||
|
||
adb shell 'run-as bin.mt.termex /system/bin/su -c "pkill -f /data/local/tmp/ecapture || true; chmod 644 /data/local/tmp/ecapture_dx.log /data/local/tmp/ecapture_dx.err 2>/dev/null || true"'
|
||
|
||
adb pull /data/local/tmp/ecapture_dx.log "$out\ecapture_dx.log"
|
||
adb pull /data/local/tmp/ecapture_dx.err "$out\ecapture_dx.err"
|
||
adb pull /data/local/tmp/ecapture_dx.pcapng "$out\ecapture_dx.pcapng" 2>$null
|
||
adb pull /data/local/tmp/ecapture_dx_pcap.log "$out\ecapture_dx_pcap.log" 2>$null
|
||
adb pull /data/local/tmp/ecapture_dx_pcap.err "$out\ecapture_dx_pcap.err" 2>$null
|
||
|
||
adb shell 'uiautomator dump /sdcard/window_after_case.xml >/dev/null 2>&1'
|
||
adb pull /sdcard/window_after_case.xml "$out\window.xml"
|
||
|
||
adb shell screencap -p /sdcard/screen_after_case.png
|
||
adb pull /sdcard/screen_after_case.png "$out\screen.png"
|
||
|
||
adb shell 'dumpsys activity activities | grep -E "topResumedActivity|mResumedActivity" | head -n 20' |
|
||
Set-Content -LiteralPath "$out\activity.txt"
|
||
|
||
adb shell 'logcat -d -v time' | Set-Content -LiteralPath "$out\logcat.txt"
|
||
```
|
||
|
||
说明:
|
||
|
||
- `pkill` 只停止本次 eCapture 进程。
|
||
- 启动脚本会清空 `/data/local/tmp/ecapture_dx.log` 和 `.err`,所以每次启动前先确认上轮日志已拉回。
|
||
- 不要在用户点击流程中途停止 eCapture;确认登录等动作必须在 eCapture 正在运行时执行。
|
||
|
||
## 6. 解析日志
|
||
|
||
本项目提供文本解析脚本:
|
||
|
||
```powershell
|
||
powershell -ExecutionPolicy Bypass -File tools\ecapture\parse_ecapture_text.ps1 `
|
||
-Log out\ecapture_case_xxx\ecapture_dx.log `
|
||
-OutJson out\ecapture_case_xxx\summary.json |
|
||
Set-Content -LiteralPath out\ecapture_case_xxx\summary.txt
|
||
```
|
||
|
||
解析输出会汇总:
|
||
|
||
- HTTP 请求:method、host、path
|
||
- 302 跳转链:`Location`
|
||
- Cookie:`Set-Cookie`
|
||
- 表单体:如 `appId/pk/ps/sign`
|
||
- JSON 业务 code:如 `oneKeyLogin`
|
||
|
||
注意:APP 大量接口走 HTTP/2。`-m text` 模式下,HTTP/2 头压缩帧和 JSON body 可能混在一行,看到二进制乱码是正常现象;直接按 JSON 关键字抽取即可。
|
||
|
||
如果本轮采集了 pcapng,用 Wireshark/tshark 抽 HTTP/2 头:
|
||
|
||
```powershell
|
||
powershell -ExecutionPolicy Bypass -File tools\ecapture\extract_http2_from_pcap.ps1 `
|
||
-Pcap out\ecapture_case_xxx\ecapture_dx.pcapng `
|
||
-OutTsv out\ecapture_case_xxx\http2.tsv
|
||
```
|
||
|
||
重点看:
|
||
|
||
- `http2.headers.authority`
|
||
- `http2.headers.path`
|
||
- `http2.headers.method`
|
||
- `http2.data.data`
|
||
|
||
业务登录已通过 pcapng 闭合以下 endpoint:
|
||
|
||
```text
|
||
POST https://appgologinsz.189.cn/login/client/userLoginNormal
|
||
POST https://appgologinsz.189.cn/login/client/getAccessCodeDaily
|
||
POST https://appgologinsz.189.cn/login/client/oneKeyLogin
|
||
POST https://appgologinsz.189.cn/login/custIdInfo
|
||
```
|
||
|
||
## 7. 已验证抓包流程
|
||
|
||
### 7.1 无 Hook 启动 APP
|
||
|
||
```sh
|
||
adb shell 'run-as bin.mt.termex /system/bin/su -c /data/local/tmp/run_ecapture_dx.sh'
|
||
adb shell 'monkey -p com.ct.client -c android.intent.category.LAUNCHER 1'
|
||
```
|
||
|
||
已验证 APP 能进入:
|
||
|
||
```text
|
||
com.ct.client/.activity.MainActivity
|
||
```
|
||
|
||
代表性产物:
|
||
|
||
- `out/ecapture_nohook_20260720_081836`
|
||
|
||
### 7.2 进入“我”页
|
||
|
||
底部“我”tab 坐标约:
|
||
|
||
```text
|
||
x=972 y=2253
|
||
```
|
||
|
||
命令:
|
||
|
||
```sh
|
||
adb shell 'input tap 972 2253'
|
||
```
|
||
|
||
代表性产物:
|
||
|
||
- `out/ecapture_mytab_20260720_082333`
|
||
|
||
### 7.3 点击“一键登录”
|
||
|
||
“一键登录”按钮坐标约:
|
||
|
||
```text
|
||
x=174 y=411
|
||
```
|
||
|
||
命令:
|
||
|
||
```sh
|
||
adb shell 'input tap 174 411'
|
||
```
|
||
|
||
已抓到运营商预认证链:
|
||
|
||
```text
|
||
POST id6.me /auth/presdk.do
|
||
302 -> yw.wosms.cn /unicomAuth/openapi/qc
|
||
302 -> nisportal.10010.com:9001 /api
|
||
302 -> enrichgw.10010.com /d93222629f52ec79/api
|
||
302 -> yw.wosms.cn /unicomAuth/openapi/callback
|
||
302 -> ne189.21cn.com /openapi/networkauth/nm/spcallback/...
|
||
Set-Cookie: gw_auth=<COOKIE>
|
||
```
|
||
|
||
APP 侧业务上报:
|
||
|
||
```text
|
||
getAccessCodeDaily
|
||
```
|
||
|
||
代表性产物:
|
||
|
||
- `out/ecapture_login_tap_20260720_082514`
|
||
|
||
### 7.4 点击“确认登录”
|
||
|
||
确认登录前要保持 eCapture 正在运行。登录页有 `FLAG_SECURE`,截图可能是黑屏,但 `uiautomator` 仍能看到控件。
|
||
|
||
登录页控件状态示例:
|
||
|
||
```text
|
||
Activity: com.ct.client/.login.activity.SwitchUserActivity
|
||
tab: 本机登录
|
||
button: 确认登录
|
||
checkbox: 我已阅读并同意...
|
||
```
|
||
|
||
已抓到最终确认登录链:
|
||
|
||
```text
|
||
userLoginNormal
|
||
getAccessCodeDaily
|
||
oneKeyLogin
|
||
custIdInfo
|
||
loginNetworkReport
|
||
```
|
||
|
||
其中 `oneKeyLogin` 请求的字段结构为:
|
||
|
||
```json
|
||
{
|
||
"headerInfos": {
|
||
"code": "oneKeyLogin",
|
||
"clientType": "#13.3.0#channel45#OnePlus PJZ110#",
|
||
"source": "110003",
|
||
"sourcePassword": "Sid98s",
|
||
"timestamp": "yyyyMMddHHmmss",
|
||
"token": "",
|
||
"userLoginName": ""
|
||
},
|
||
"content": {
|
||
"attach": "test",
|
||
"fieldData": {
|
||
"pswType": "04",
|
||
"accessCode": "nm...",
|
||
"gwAuth": "<COOKIE_VALUE>",
|
||
"accountType": "c2000004",
|
||
"operatorType": "CU",
|
||
"loginAuthCipherAsymmertric": "<LONG_SECRET>",
|
||
"deviceUid": "",
|
||
"shopId": "20002",
|
||
"source": "110003",
|
||
"systemVersion": "16",
|
||
"androidId": "<ANDROID_ID>"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
登录成功后,后续业务接口会带登录态字段:
|
||
|
||
```text
|
||
provinceCode=600101
|
||
token=<TOKEN>
|
||
userLoginName=<USER>
|
||
account=<ACCOUNT>
|
||
userId=<USERID>
|
||
```
|
||
|
||
代表性产物:
|
||
|
||
- `out/ecapture_confirm_login_20260720_082947`
|
||
|
||
## 8. 常见问题
|
||
|
||
### 普通 adb shell 里找不到 su
|
||
|
||
本设备就是这种状态。不要卡在 `adb shell su`,直接使用:
|
||
|
||
```sh
|
||
adb shell 'run-as bin.mt.termex /system/bin/su -c id'
|
||
```
|
||
|
||
### eCapture 没有输出
|
||
|
||
按顺序检查:
|
||
|
||
1. `bin.mt.termex` 是否仍有 root 授权。
|
||
2. `run-as bin.mt.termex /system/bin/su -c id` 是否返回 root。
|
||
3. 目标 UID 是否仍是 `10409`。
|
||
4. 目标是否使用 `/apex/com.android.conscrypt/lib64/libssl.so`。
|
||
5. `/data/local/tmp/ecapture_dx.err` 是否有错误。
|
||
|
||
### 截图黑屏
|
||
|
||
登录页设置了安全窗口。黑屏不代表 APP 崩溃:
|
||
|
||
```sh
|
||
adb shell 'uiautomator dump /sdcard/window.xml'
|
||
```
|
||
|
||
用 `window.xml` 判断真实 UI。
|
||
|
||
### 为什么不用代理
|
||
|
||
这个 APP 在 Hook/代理场景容易卡启动或被壳逻辑干扰。eCapture 是内核侧 uprobes 抓 BoringSSL 明文,绕过代理、CA、证书锁定和 APP 内部网络栈差异。
|
||
|
||
### HTTP/2 日志有乱码
|
||
|
||
正常。`-m text` 能拿到明文,但 HTTP/2 帧、HPACK 头和 JSON body 会混杂。现阶段优先用:
|
||
|
||
```powershell
|
||
Select-String -LiteralPath ecapture_dx.log -Pattern 'oneKeyLogin|userLoginNormal|ticket|token'
|
||
```
|
||
|
||
如果后续需要 Wireshark 级别解析,切换到 pcapng 模式:
|
||
|
||
```sh
|
||
adb shell 'run-as bin.mt.termex /system/bin/su -c /data/local/tmp/run_ecapture_dx_pcap.sh'
|
||
```
|
||
|
||
再拉取 `/data/local/tmp/ecapture_dx.pcapng`,用
|
||
`tools/ecapture/extract_http2_from_pcap.ps1` 抽 `:authority/:path`。
|