docs(huya): 逆向定位 32hex hdid = NativeEntry.getGUID() + 真机注入通道证据
核心结论:
- 32hex hdid (WUP 登录帧 field1.tag0) = com.huya.security.hydeviceid.NativeEntry.getGUID()
- 金样本 ed0db8 为金样本设备 GUID; 当前真机 M2102J2SC getGUID()=0a7dfaa882938a6ab502511452142c57(稳定)
- NativeEntry 全字段: getGUID(32hex)/getCDID(40hex)/getHDID(40hex)/getMID(16hex)/getSDID(base64 safedeviceid)
- 单机不可铸造: 改 serialno/ANDROID_ID/删files/hydevice持久化 均不变(硬锚底层硬件)
- libhydeviceid.so 为 OLLVM+datadiv 壳; 用 dlopen->JNI_OnLoad->RegisterNatives(env表索引215) 绕开混淆
脚本: phone_probe_registernatives2(注册表)/phone_java_call_nativeentry(Java读getGUID)/phone_read_hdid(native hook)
phone_dump_hydev(dump解密so)/phone_probe_login_frame(登录帧)/arm64_disasm(反汇编)
证据: hdid_regtable/java_nativeentry/hdid_read/login_frames/hdid_probe* + libhydeviceid_{original,dump}.so
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# 虎牙 32hex hdid 生成逆向研究(真机 M2102J2SC)
|
||||
|
||||
> 目标:搞清楚 WUP 登录帧 t1.t0 / field1.tag0 的 32hex hdid(金样本 `ed0db8334cadd236c00cadf7e11ab5a5`)
|
||||
> 如何生成、是否能在同一台设备上铸造新值(实现"每账号独立设备")。
|
||||
> 时间:2026-08-28(真机稳定注入通道打通后进行)。**已定位 32hex = NativeEntry.getGUID()。**
|
||||
|
||||
## 一、最终结论(决定性)
|
||||
|
||||
**32hex hdid(WUP 登录帧 field1.tag0)= `com.huya.security.hydeviceid.NativeEntry.getGUID()` 的返回值。**
|
||||
|
||||
- 金样本 `ed0db8...` 是**金样本设备**的 getGUID() 值;
|
||||
- **当前真机 M2102J2SC 的 getGUID() = `0a7dfaa882938a6ab502511452142c57`**(32hex,交叉验证稳定:
|
||||
在 5 个设备上报帧中出现 + Java 主动调用一致)。
|
||||
|
||||
**getGUID() 每台设备唯一且稳定(硬锚硬件,同 getCDID/getHDID 一样),可被 `NativeEntry` 的
|
||||
native 方法直接可靠读取。**
|
||||
|
||||
因此**"生成 32hex hdid" 无需逆向算法**:每台真机天然由 libhydeviceid.so 生成并提供它独有的
|
||||
32hex getGUID()。多真机设备池 = 每台调 getGUID() 采集各自唯一值。
|
||||
|
||||
### 单机不可铸造(3 个决定性实验)
|
||||
改 `ro.serialno` / `ANDROID_ID` / 删 `files/hydevice/` 持久化后重启,设备标识(UUID 文件名 +
|
||||
getGUID 等)**全部不变** —— 因 libhydeviceid.so 基于底层硬件(SoC/IMEI)稳定生成,刷不掉。
|
||||
|
||||
## 二、NativeEntry 全部字段(已可靠读取)
|
||||
|
||||
通过 dlopen 拦截 → JNI_OnLoad(0x22c4c8) → RegisterNatives(JNIEnv 函数表索引 215),dump 出
|
||||
`com.huya.security.hydeviceid.NativeEntry` 的 19 个 native 方法,Java 主动调用实测值:
|
||||
|
||||
| NativeEntry 方法 | 真机实测值 | 对应 WUP 登录帧字段 |
|
||||
|---|---|---|
|
||||
| **getGUID()** | **`0a7dfaa882938a6ab502511452142c57`** | **field1.tag0 = hdid(32hex)** |
|
||||
| getCDID() | `02df398797432eadefcc12767119ad5e80999389` | field2.tag2 = device_id(40hex) |
|
||||
| getHDID() | `7c5387e0539c023c31c4ff0e807e7256117385ee` | field2.tag8 = 另一设备ID(40hex) |
|
||||
| getMID() | `1e8bdf7d4f7a01d3` | MID(16hex) |
|
||||
| getSDID() | `PQwemAN9...`(base64 180B) | safedeviceid |
|
||||
|
||||
- Java 类 `com.huya.security.hydeviceid.NativeEntry` 注册 method 地址(libhydeviceid.so 偏移):
|
||||
getCDID@0x20eb08 / getSDID@0x20ede8 / **getHDID@0x20f0c8** / init@0x20e6e4 /
|
||||
getGUID@0x20f8c8 / getMID@0x20fba8 / linkId@0x20fe88(其余 setPhoneInfo/setMotionEvent/上报类略)。
|
||||
- 另有 `com.huyaudb.HuyaAuthCore` 注册 `init/unInit/sendMsg/receiveNet`(WUP 消息收发)。
|
||||
|
||||
### hdid 名称混淆(已彻底澄清)
|
||||
项目里"hdid"对应**多个不同值**:
|
||||
1. **32hex(WUP 登录帧 field1.tag0)= getGUID()** —— 设备 GUID,硬锚,每台唯一;
|
||||
2. 40hex `7c5387` = getHDID() = huyaudb 上报里被命名为 "hdid"(**同名不同值**);
|
||||
3. 40hex `02df39` = getCDID() = device_id;
|
||||
4. 16hex `1e8bdf7d4f7a01d3` = getMID();
|
||||
5. 凭据文件 `files/hydevice/<UUID>`(`64a33427...`)持久化加密凭据。
|
||||
|
||||
## 三、libhydeviceid.so 加壳(静态逆向极难,但已绕过)
|
||||
|
||||
- **OLLVM 混淆**(`based on Obfuscator-LLVM 9.0.9svn`)+ **datadiv 运行时壳**(~70 个 `.datadiv_decode*`
|
||||
解密函数 + `hy_fopen64/hy_read/hy_syscall/hy_write/hyftell` 文件 I/O 壳 + 读 TracerPid 反调试)。
|
||||
- 用 **dlopen→JNI_OnLoad→RegisterNatives** 动态拿到方法表,绕开混淆,无需反汇编。
|
||||
- 已 dump 解密后代码:`evidence/diag_phone/libhydeviceid_dump.so`(内存态)+ 原始
|
||||
`libhydeviceid_original.so`。
|
||||
|
||||
## 四、可行方向(服务于"每账号独立设备")——**推荐,已具备采集能力**
|
||||
|
||||
**多真机采集设备池**:每台真机用真机注入通道(spawn+art_callsite bypass)读
|
||||
`{getGUID(32hex), getCDID(40hex), getHDID(40hex), getMID(16hex), getSDID(base64)}`,
|
||||
这些值每台唯一、稳定、被服务端认可。采集多台建设备池轮换,即可实现每账号独立设备。
|
||||
**无需逆向铸造算法**,直接读 NativeEntry 即可。
|
||||
|
||||
## 五、实验脚本(真机)
|
||||
|
||||
- `scripts/phone_probe_registernatives2.py`(v7:dlopen→JNI_OnLoad→RegisterNatives 注册表 + get* 地址)
|
||||
- `scripts/phone_java_call_nativeentry.py`(**Java 主动调 NativeEntry get* 全家桶,读取 32hex GUID**)
|
||||
- `scripts/phone_read_hdid.py`(v8:native hook get* 用原生 JNI GetStringUTFChars 解码)
|
||||
- `scripts/phone_dump_hydev.py`(dump 解密后的 libhydeviceid.so)
|
||||
- `scripts/phone_probe_hdid*.py`(早期符号 / 属性 / 文件探针,过程记录)
|
||||
- 证据:`evidence/diag_phone/{hdid_regtable,hdid_read,java_nativeentry,login_frames}.json` /
|
||||
`libhydeviceid_{original,dump}.so`
|
||||
@@ -0,0 +1,366 @@
|
||||
{
|
||||
"events": [
|
||||
{
|
||||
"type": "hydev",
|
||||
"base": "0x7813011000",
|
||||
"size": 4124672
|
||||
},
|
||||
{
|
||||
"type": "hydev-exports",
|
||||
"count": 76,
|
||||
"exports": [
|
||||
{
|
||||
"off": "0xff854",
|
||||
"name": ".datadiv_decode13377393303060931696"
|
||||
},
|
||||
{
|
||||
"off": "0x30bf3c",
|
||||
"name": "hy_syscall"
|
||||
},
|
||||
{
|
||||
"off": "0x2c671c",
|
||||
"name": ".datadiv_decode8394649184240089179"
|
||||
},
|
||||
{
|
||||
"off": "0xc452c",
|
||||
"name": ".datadiv_decode12715880952929850959"
|
||||
},
|
||||
{
|
||||
"off": "0x22c6b4",
|
||||
"name": ".datadiv_decode17756744967214589151"
|
||||
},
|
||||
{
|
||||
"off": "0x206530",
|
||||
"name": ".datadiv_decode16142768320442664775"
|
||||
},
|
||||
{
|
||||
"off": "0x10fe18",
|
||||
"name": ".datadiv_decode13935584199325042899"
|
||||
},
|
||||
{
|
||||
"off": "0x1c91dc",
|
||||
"name": ".datadiv_decode4182082414496431548"
|
||||
},
|
||||
{
|
||||
"off": "0x19b5f0",
|
||||
"name": ".datadiv_decode14514111528646739931"
|
||||
},
|
||||
{
|
||||
"off": "0x26d7b0",
|
||||
"name": ".datadiv_decode3186372685042885857"
|
||||
},
|
||||
{
|
||||
"off": "0x212b04",
|
||||
"name": ".datadiv_decode13001096679939917798"
|
||||
},
|
||||
{
|
||||
"off": "0xec484",
|
||||
"name": ".datadiv_decode7272451282499632790"
|
||||
},
|
||||
{
|
||||
"off": "0x2880fc",
|
||||
"name": ".datadiv_decode5662830498276606612"
|
||||
},
|
||||
{
|
||||
"off": "0x111490",
|
||||
"name": ".datadiv_decode4441042267327943258"
|
||||
},
|
||||
{
|
||||
"off": "0x27f610",
|
||||
"name": ".datadiv_decode16021085630626535143"
|
||||
},
|
||||
{
|
||||
"off": "0x30ae48",
|
||||
"name": ".datadiv_decode17316226923494143690"
|
||||
},
|
||||
{
|
||||
"off": "0x1d5f4c",
|
||||
"name": ".datadiv_decode7531359686248698545"
|
||||
},
|
||||
{
|
||||
"off": "0x247144",
|
||||
"name": ".datadiv_decode17780086335141721104"
|
||||
},
|
||||
{
|
||||
"off": "0x1adfd0",
|
||||
"name": ".datadiv_decode16904853521236949932"
|
||||
},
|
||||
{
|
||||
"off": "0xf013c",
|
||||
"name": ".datadiv_decode3639556092286127407"
|
||||
},
|
||||
{
|
||||
"off": "0x21f98c",
|
||||
"name": ".datadiv_decode8476215815990549556"
|
||||
},
|
||||
{
|
||||
"off": "0x30bf00",
|
||||
"name": "hy_write"
|
||||
},
|
||||
{
|
||||
"off": "0xea848",
|
||||
"name": ".datadiv_decode5013095055348382358"
|
||||
},
|
||||
{
|
||||
"off": "0x2bf894",
|
||||
"name": ".datadiv_decode6552120278544488220"
|
||||
},
|
||||
{
|
||||
"off": "0x245be0",
|
||||
"name": ".datadiv_decode16449743662515666302"
|
||||
},
|
||||
{
|
||||
"off": "0x11b930",
|
||||
"name": ".datadiv_decode15631854437821104344"
|
||||
},
|
||||
{
|
||||
"off": "0xe7a5c",
|
||||
"name": ".datadiv_decode15220286527488363869"
|
||||
},
|
||||
{
|
||||
"off": "0x189ed0",
|
||||
"name": ".datadiv_decode971716066137475299"
|
||||
},
|
||||
{
|
||||
"off": "0x235fd4",
|
||||
"name": ".datadiv_decode2240554284615242758"
|
||||
},
|
||||
{
|
||||
"off": "0xbbf90",
|
||||
"name": ".datadiv_decode930153296716187306"
|
||||
},
|
||||
{
|
||||
"off": "0x17f75c",
|
||||
"name": ".datadiv_decode18193155687921697907"
|
||||
},
|
||||
{
|
||||
"off": "0xef9c4",
|
||||
"name": ".datadiv_decode7208817035916333614"
|
||||
},
|
||||
{
|
||||
"off": "0x22c4c8",
|
||||
"name": "JNI_OnLoad"
|
||||
},
|
||||
{
|
||||
"off": "0xbdb4c",
|
||||
"name": ".datadiv_decode9718679232209877219"
|
||||
},
|
||||
{
|
||||
"off": "0x9f9d8",
|
||||
"name": ".datadiv_decode2415276826024734045"
|
||||
},
|
||||
{
|
||||
"off": "0xf3360",
|
||||
"name": "hyftell"
|
||||
},
|
||||
{
|
||||
"off": "0x242918",
|
||||
"name": ".datadiv_decode1813700997626276425"
|
||||
},
|
||||
{
|
||||
"off": "0x116020",
|
||||
"name": ".datadiv_decode2127118722610622943"
|
||||
},
|
||||
{
|
||||
"off": "0xf80c8",
|
||||
"name": ".datadiv_decode6313363124510466600"
|
||||
},
|
||||
{
|
||||
"off": "0x1d4e54",
|
||||
"name": ".datadiv_decode16229027793617600142"
|
||||
},
|
||||
{
|
||||
"off": "0x1d0580",
|
||||
"name": ".datadiv_decode10115291355877817959"
|
||||
},
|
||||
{
|
||||
"off": "0x30bae8",
|
||||
"name": ".datadiv_decode10203504059392997873"
|
||||
},
|
||||
{
|
||||
"off": "0xf1298",
|
||||
"name": "hyfopen64"
|
||||
},
|
||||
{
|
||||
"off": "0x2c321c",
|
||||
"name": ".datadiv_decode15986889750731987093"
|
||||
},
|
||||
{
|
||||
"off": "0x23e338",
|
||||
"name": ".datadiv_decode11861954481925470588"
|
||||
},
|
||||
{
|
||||
"off": "0x2fae08",
|
||||
"name": ".datadiv_decode13370067802168070091"
|
||||
},
|
||||
{
|
||||
"off": "0x2332d4",
|
||||
"name": ".datadiv_decode5223443461034991902"
|
||||
},
|
||||
{
|
||||
"off": "0x30bef0",
|
||||
"name": "hy_read"
|
||||
},
|
||||
{
|
||||
"off": "0x2ccc64",
|
||||
"name": ".datadiv_decode8208694899043494479"
|
||||
},
|
||||
{
|
||||
"off": "0x227b50",
|
||||
"name": ".datadiv_decode18104751532030584008"
|
||||
},
|
||||
{
|
||||
"off": "0x278c40",
|
||||
"name": ".datadiv_decode8389934691053754807"
|
||||
},
|
||||
{
|
||||
"off": "0x274bbc",
|
||||
"name": ".datadiv_decode381523612705756801"
|
||||
},
|
||||
{
|
||||
"off": "0x10923c",
|
||||
"name": ".datadiv_decode11326397958007879543"
|
||||
},
|
||||
{
|
||||
"off": "0x22ed90",
|
||||
"name": ".datadiv_decode15748246807707687162"
|
||||
},
|
||||
{
|
||||
"off": "0x307350",
|
||||
"name": ".datadiv_decode14652163487098383068"
|
||||
},
|
||||
{
|
||||
"off": "0x2823e0",
|
||||
"name": ".datadiv_decode10168130846246250133"
|
||||
},
|
||||
{
|
||||
"off": "0xfe1f4",
|
||||
"name": ".datadiv_decode8274439441013259658"
|
||||
},
|
||||
{
|
||||
"off": "0xb5a18",
|
||||
"name": ".datadiv_decode2818691867109853035"
|
||||
},
|
||||
{
|
||||
"off": "0x284a38",
|
||||
"name": ".datadiv_decode17937343329166821664"
|
||||
},
|
||||
{
|
||||
"off": "0x23755c",
|
||||
"name": ".datadiv_decode8740858567082904038"
|
||||
},
|
||||
{
|
||||
"off": "0xe1968",
|
||||
"name": ".datadiv_decode15148178244621190089"
|
||||
},
|
||||
{
|
||||
"off": "0x1094ec",
|
||||
"name": ".datadiv_decode10096756759899255764"
|
||||
},
|
||||
{
|
||||
"off": "0x2c2b28",
|
||||
"name": ".datadiv_decode5687050107175613243"
|
||||
},
|
||||
{
|
||||
"off": "0x1e4048",
|
||||
"name": ".datadiv_decode10091502229593795971"
|
||||
},
|
||||
{
|
||||
"off": "0x1107c8",
|
||||
"name": ".datadiv_decode16105350321006291378"
|
||||
},
|
||||
{
|
||||
"off": "0xf0078",
|
||||
"name": ".datadiv_decode13805690131605262795"
|
||||
},
|
||||
{
|
||||
"off": "0x308e64",
|
||||
"name": ".datadiv_decode13481575639147657934"
|
||||
},
|
||||
{
|
||||
"off": "0x25df2c",
|
||||
"name": ".datadiv_decode5869377321246282402"
|
||||
},
|
||||
{
|
||||
"off": "0xec6d8",
|
||||
"name": ".datadiv_decode5215474294857803928"
|
||||
},
|
||||
{
|
||||
"off": "0x23c534",
|
||||
"name": ".datadiv_decode15714924883407080113"
|
||||
},
|
||||
{
|
||||
"off": "0x27fc5c",
|
||||
"name": ".datadiv_decode16454361570808326355"
|
||||
},
|
||||
{
|
||||
"off": "0x27df10",
|
||||
"name": ".datadiv_decode15924187094028544261"
|
||||
},
|
||||
{
|
||||
"off": "0x23532c",
|
||||
"name": ".datadiv_decode4448295952963935447"
|
||||
},
|
||||
{
|
||||
"off": "0x141914",
|
||||
"name": ".datadiv_decode5332962653989896249"
|
||||
},
|
||||
{
|
||||
"off": "0x1106a8",
|
||||
"name": ".datadiv_decode7627883646358325318"
|
||||
},
|
||||
{
|
||||
"off": "0x2c80ac",
|
||||
"name": ".datadiv_decode10053382265862873780"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "jni-onload-hooked"
|
||||
},
|
||||
{
|
||||
"type": "jni-onload-enter",
|
||||
"tid": 27101
|
||||
},
|
||||
{
|
||||
"type": "jni-onload-leave",
|
||||
"ret": "0x10006"
|
||||
},
|
||||
{
|
||||
"type": "udb",
|
||||
"base": "0x77bf3d4000"
|
||||
},
|
||||
{
|
||||
"type": "hdid-hooked"
|
||||
},
|
||||
{
|
||||
"type": "setsd-hooked"
|
||||
},
|
||||
{
|
||||
"type": "hdid",
|
||||
"str": "",
|
||||
"tid": 27267
|
||||
},
|
||||
{
|
||||
"type": "hdid",
|
||||
"str": "",
|
||||
"tid": 27267
|
||||
},
|
||||
{
|
||||
"type": "hdid",
|
||||
"str": "",
|
||||
"tid": 27267
|
||||
},
|
||||
{
|
||||
"type": "hdid",
|
||||
"str": "",
|
||||
"tid": 27101
|
||||
},
|
||||
{
|
||||
"type": "hdid",
|
||||
"str": "",
|
||||
"tid": 27101
|
||||
}
|
||||
],
|
||||
"detached": []
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
{
|
||||
"props": [
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.carrier.name",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.carrier.name",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.carrier.name",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.carrier.name",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.product.mod_device",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.product.mod_device",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.cts",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "persist.sys.miui_optimization",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.cust.test",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.cust.test",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.cust.test",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "persist.sys.func_limit_switch",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.config.low_ram.threshold_gb",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.product.mod_device",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.product.mod_device",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.build.characteristics",
|
||||
"v": "nosdcard"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.userdata_version",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.debuggable",
|
||||
"v": "0"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.has_cust_partition",
|
||||
"v": "true"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.cust_device",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.ui.version.code",
|
||||
"v": "11"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "debug.atrace.tags.enableflags",
|
||||
"v": "0"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.debuggable",
|
||||
"v": "0"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.miui.mcc",
|
||||
"v": "9460"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.build.version.sdk",
|
||||
"v": "30"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.vendor.redirect_socket_calls",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.arch",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.arch",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.boringcrypto.hwrand",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.build.version.sdk",
|
||||
"v": "30"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "persist.sys.dalvik.vm.lib",
|
||||
"v": ""
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "persist.sys.dalvik.vm.lib.2",
|
||||
"v": "libart.so"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.build.version.release_or_codename",
|
||||
"v": "11"
|
||||
},
|
||||
{
|
||||
"type": "prop",
|
||||
"k": "ro.product.model",
|
||||
"v": "M2102J2SC"
|
||||
}
|
||||
],
|
||||
"hyio": [],
|
||||
"hdid": [],
|
||||
"detached": []
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"read": null,
|
||||
"nativetrap": [],
|
||||
"detached": [
|
||||
{
|
||||
"reason": "process-terminated",
|
||||
"detail": "None"
|
||||
}
|
||||
],
|
||||
"getvals": [
|
||||
{
|
||||
"type": "getval",
|
||||
"name": "getCDID",
|
||||
"str": "02df398797432eadefcc12767119ad5e80999389"
|
||||
},
|
||||
{
|
||||
"type": "getval",
|
||||
"name": "getHDID",
|
||||
"str": "7c5387e0539c023c31c4ff0e807e7256117385ee"
|
||||
},
|
||||
{
|
||||
"type": "getval",
|
||||
"name": "getSDID",
|
||||
"str": "PQwemAN9NHkZKoMqJ1jkwhIypqMTaQEOrmXr37xQVhQZq7D8uCKpS0FntlUjmpcZ4PnIwGtXSr+w2NnEM5QpuwVPuPhSRxBjPEjt1XPta6E6IvWxsSculivOJlC9qSOAjmzk9EFJeJU+0K1drfzcBKL5v8hMLabmzr76aYoVFExaEgA7lzs="
|
||||
},
|
||||
{
|
||||
"type": "getval",
|
||||
"name": "getHDID",
|
||||
"str": "7c5387e0539c023c31c4ff0e807e7256117385ee"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"regs": [],
|
||||
"hdid": [],
|
||||
"detached": []
|
||||
}
|
||||
@@ -0,0 +1,802 @@
|
||||
{
|
||||
"regs": [
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "getCDID",
|
||||
"sig": "()Ljava/lang/String;",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20eb08"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "getSDID",
|
||||
"sig": "()Ljava/lang/String;",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20ede8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "getHDID",
|
||||
"sig": "()Ljava/lang/String;",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20f0c8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "init",
|
||||
"sig": "()V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20e6e4"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "getGUID",
|
||||
"sig": "()Ljava/lang/String;",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20f8c8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "getMID",
|
||||
"sig": "()Ljava/lang/String;",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20fba8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "linkId",
|
||||
"sig": "()Z",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20fe88"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "setPrivacyMode",
|
||||
"sig": "(Z)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20f3a8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "setUserAction",
|
||||
"sig": "(Ljava/lang/String;)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20f3b4"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "setAppStatus",
|
||||
"sig": "(I)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20f888"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "setPhoneInfo",
|
||||
"sig": "(Ljava/lang/String;)Z",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x2107f0"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "doReportAgain",
|
||||
"sig": "(Ljava/lang/String;)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20fea0"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "delayCollect",
|
||||
"sig": "(Ljava/lang/String;)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x210348"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "setMotionEvent",
|
||||
"sig": "(Ljava/lang/String;Ljava/lang/String;)Z",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x210c20"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "uploadScreenRecord",
|
||||
"sig": "()V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x2114f8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "uploadScreenShot",
|
||||
"sig": "()V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x2114fc"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "updateDisplayInfo",
|
||||
"sig": "(Ljava/lang/String;)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x211500"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "addApplist",
|
||||
"sig": "(Ljava/lang/String;Ljava/lang/String;)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x2119a8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "requestConfig",
|
||||
"sig": "()V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x212a5c"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huyaudb.HuyaAuthCore",
|
||||
"name": "init",
|
||||
"sig": "()V",
|
||||
"mod": "libudbauthunify.so",
|
||||
"off": "0x2621d4"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huyaudb.HuyaAuthCore",
|
||||
"name": "unInit",
|
||||
"sig": "()V",
|
||||
"mod": "libudbauthunify.so",
|
||||
"off": "0x26238c"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huyaudb.HuyaAuthCore",
|
||||
"name": "sendMsg",
|
||||
"sig": "(J[B)[B",
|
||||
"mod": "libudbauthunify.so",
|
||||
"off": "0x26244c"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huyaudb.HuyaAuthCore",
|
||||
"name": "receiveNet",
|
||||
"sig": "([BIII)V",
|
||||
"mod": "libudbauthunify.so",
|
||||
"off": "0x2626b0"
|
||||
}
|
||||
],
|
||||
"fns_scan": [
|
||||
{
|
||||
"type": "fns-scan",
|
||||
"dump": [
|
||||
{
|
||||
"i": 200,
|
||||
"a": "0x7895f37840",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3ee840"
|
||||
},
|
||||
{
|
||||
"i": 201,
|
||||
"a": "0x7895f38004",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3ef004"
|
||||
},
|
||||
{
|
||||
"i": 202,
|
||||
"a": "0x7895f387c8",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3ef7c8"
|
||||
},
|
||||
{
|
||||
"i": 203,
|
||||
"a": "0x7895f38f8c",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3eff8c"
|
||||
},
|
||||
{
|
||||
"i": 204,
|
||||
"a": "0x7895f39750",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f0750"
|
||||
},
|
||||
{
|
||||
"i": 205,
|
||||
"a": "0x7895f39f14",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f0f14"
|
||||
},
|
||||
{
|
||||
"i": 206,
|
||||
"a": "0x7895f3a6d8",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f16d8"
|
||||
},
|
||||
{
|
||||
"i": 207,
|
||||
"a": "0x7895f3ae9c",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f1e9c"
|
||||
},
|
||||
{
|
||||
"i": 208,
|
||||
"a": "0x7895f3b660",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f2660"
|
||||
},
|
||||
{
|
||||
"i": 209,
|
||||
"a": "0x7895f3be24",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f2e24"
|
||||
},
|
||||
{
|
||||
"i": 210,
|
||||
"a": "0x7895f3c5e8",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f35e8"
|
||||
},
|
||||
{
|
||||
"i": 211,
|
||||
"a": "0x7895f3cdac",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f3dac"
|
||||
},
|
||||
{
|
||||
"i": 212,
|
||||
"a": "0x7895f3d570",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f4570"
|
||||
},
|
||||
{
|
||||
"i": 213,
|
||||
"a": "0x7895f3dd34",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f4d34"
|
||||
},
|
||||
{
|
||||
"i": 214,
|
||||
"a": "0x7895f3e4f8",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f54f8"
|
||||
},
|
||||
{
|
||||
"i": 215,
|
||||
"a": "0x7895f3ecbc",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f5cbc"
|
||||
},
|
||||
{
|
||||
"i": 216,
|
||||
"a": "0x7895f3fbb4",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f6bb4"
|
||||
},
|
||||
{
|
||||
"i": 217,
|
||||
"a": "0x7895f40488",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f7488"
|
||||
},
|
||||
{
|
||||
"i": 218,
|
||||
"a": "0x7895f40bc4",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f7bc4"
|
||||
},
|
||||
{
|
||||
"i": 219,
|
||||
"a": "0x7895f41300",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f8300"
|
||||
},
|
||||
{
|
||||
"i": 220,
|
||||
"a": "0x7895f41354",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f8354"
|
||||
},
|
||||
{
|
||||
"i": 221,
|
||||
"a": "0x7895f41b34",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f8b34"
|
||||
},
|
||||
{
|
||||
"i": 222,
|
||||
"a": "0x7895f4232c",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f932c"
|
||||
},
|
||||
{
|
||||
"i": 223,
|
||||
"a": "0x7895f42b54",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f9b54"
|
||||
},
|
||||
{
|
||||
"i": 224,
|
||||
"a": "0x7895f43320",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fa320"
|
||||
},
|
||||
{
|
||||
"i": 225,
|
||||
"a": "0x7895f43b08",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fab08"
|
||||
},
|
||||
{
|
||||
"i": 226,
|
||||
"a": "0x7895f44244",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fb244"
|
||||
},
|
||||
{
|
||||
"i": 227,
|
||||
"a": "0x7895f44934",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fb934"
|
||||
},
|
||||
{
|
||||
"i": 228,
|
||||
"a": "0x7895f44948",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fb948"
|
||||
},
|
||||
{
|
||||
"i": 229,
|
||||
"a": "0x7895f4495c",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fb95c"
|
||||
},
|
||||
{
|
||||
"i": 230,
|
||||
"a": "0x7895f44a14",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fba14"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"type": "dlopen-hooked",
|
||||
"fn": "android_dlopen_ext"
|
||||
},
|
||||
{
|
||||
"type": "dlopen-hydev",
|
||||
"name": "/data/app/~~8yCINTbsGdM-Ig8T11hF2Q==/com.duowan.kiwi-sandFFuGVgsZjumsNfkf1A==/lib/arm64/libhydeviceid.so"
|
||||
},
|
||||
{
|
||||
"type": "jni-onload-hooked"
|
||||
},
|
||||
{
|
||||
"type": "jni-onload-enter",
|
||||
"tid": 30763
|
||||
},
|
||||
{
|
||||
"type": "fns-scan",
|
||||
"dump": [
|
||||
{
|
||||
"i": 200,
|
||||
"a": "0x7895f37840",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3ee840"
|
||||
},
|
||||
{
|
||||
"i": 201,
|
||||
"a": "0x7895f38004",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3ef004"
|
||||
},
|
||||
{
|
||||
"i": 202,
|
||||
"a": "0x7895f387c8",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3ef7c8"
|
||||
},
|
||||
{
|
||||
"i": 203,
|
||||
"a": "0x7895f38f8c",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3eff8c"
|
||||
},
|
||||
{
|
||||
"i": 204,
|
||||
"a": "0x7895f39750",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f0750"
|
||||
},
|
||||
{
|
||||
"i": 205,
|
||||
"a": "0x7895f39f14",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f0f14"
|
||||
},
|
||||
{
|
||||
"i": 206,
|
||||
"a": "0x7895f3a6d8",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f16d8"
|
||||
},
|
||||
{
|
||||
"i": 207,
|
||||
"a": "0x7895f3ae9c",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f1e9c"
|
||||
},
|
||||
{
|
||||
"i": 208,
|
||||
"a": "0x7895f3b660",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f2660"
|
||||
},
|
||||
{
|
||||
"i": 209,
|
||||
"a": "0x7895f3be24",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f2e24"
|
||||
},
|
||||
{
|
||||
"i": 210,
|
||||
"a": "0x7895f3c5e8",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f35e8"
|
||||
},
|
||||
{
|
||||
"i": 211,
|
||||
"a": "0x7895f3cdac",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f3dac"
|
||||
},
|
||||
{
|
||||
"i": 212,
|
||||
"a": "0x7895f3d570",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f4570"
|
||||
},
|
||||
{
|
||||
"i": 213,
|
||||
"a": "0x7895f3dd34",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f4d34"
|
||||
},
|
||||
{
|
||||
"i": 214,
|
||||
"a": "0x7895f3e4f8",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f54f8"
|
||||
},
|
||||
{
|
||||
"i": 215,
|
||||
"a": "0x7895f3ecbc",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f5cbc"
|
||||
},
|
||||
{
|
||||
"i": 216,
|
||||
"a": "0x7895f3fbb4",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f6bb4"
|
||||
},
|
||||
{
|
||||
"i": 217,
|
||||
"a": "0x7895f40488",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f7488"
|
||||
},
|
||||
{
|
||||
"i": 218,
|
||||
"a": "0x7895f40bc4",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f7bc4"
|
||||
},
|
||||
{
|
||||
"i": 219,
|
||||
"a": "0x7895f41300",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f8300"
|
||||
},
|
||||
{
|
||||
"i": 220,
|
||||
"a": "0x7895f41354",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f8354"
|
||||
},
|
||||
{
|
||||
"i": 221,
|
||||
"a": "0x7895f41b34",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f8b34"
|
||||
},
|
||||
{
|
||||
"i": 222,
|
||||
"a": "0x7895f4232c",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f932c"
|
||||
},
|
||||
{
|
||||
"i": 223,
|
||||
"a": "0x7895f42b54",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3f9b54"
|
||||
},
|
||||
{
|
||||
"i": 224,
|
||||
"a": "0x7895f43320",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fa320"
|
||||
},
|
||||
{
|
||||
"i": 225,
|
||||
"a": "0x7895f43b08",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fab08"
|
||||
},
|
||||
{
|
||||
"i": 226,
|
||||
"a": "0x7895f44244",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fb244"
|
||||
},
|
||||
{
|
||||
"i": 227,
|
||||
"a": "0x7895f44934",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fb934"
|
||||
},
|
||||
{
|
||||
"i": 228,
|
||||
"a": "0x7895f44948",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fb948"
|
||||
},
|
||||
{
|
||||
"i": 229,
|
||||
"a": "0x7895f4495c",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fb95c"
|
||||
},
|
||||
{
|
||||
"i": 230,
|
||||
"a": "0x7895f44a14",
|
||||
"mod": "libart.so",
|
||||
"off": "0x3fba14"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "rn-addr",
|
||||
"rn": "0x7895f3ecbc",
|
||||
"mod": {
|
||||
"n": "libart.so",
|
||||
"off": "0x3f5cbc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "rn-hooked"
|
||||
},
|
||||
{
|
||||
"type": "reg-call",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"count": 19,
|
||||
"tid": 30763
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "getCDID",
|
||||
"sig": "()Ljava/lang/String;",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20eb08"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "getSDID",
|
||||
"sig": "()Ljava/lang/String;",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20ede8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "getHDID",
|
||||
"sig": "()Ljava/lang/String;",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20f0c8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "init",
|
||||
"sig": "()V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20e6e4"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "getGUID",
|
||||
"sig": "()Ljava/lang/String;",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20f8c8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "getMID",
|
||||
"sig": "()Ljava/lang/String;",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20fba8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "linkId",
|
||||
"sig": "()Z",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20fe88"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "setPrivacyMode",
|
||||
"sig": "(Z)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20f3a8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "setUserAction",
|
||||
"sig": "(Ljava/lang/String;)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20f3b4"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "setAppStatus",
|
||||
"sig": "(I)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20f888"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "setPhoneInfo",
|
||||
"sig": "(Ljava/lang/String;)Z",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x2107f0"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "doReportAgain",
|
||||
"sig": "(Ljava/lang/String;)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x20fea0"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "delayCollect",
|
||||
"sig": "(Ljava/lang/String;)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x210348"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "setMotionEvent",
|
||||
"sig": "(Ljava/lang/String;Ljava/lang/String;)Z",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x210c20"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "uploadScreenRecord",
|
||||
"sig": "()V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x2114f8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "uploadScreenShot",
|
||||
"sig": "()V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x2114fc"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "updateDisplayInfo",
|
||||
"sig": "(Ljava/lang/String;)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x211500"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "addApplist",
|
||||
"sig": "(Ljava/lang/String;Ljava/lang/String;)V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x2119a8"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huya.security.hydeviceid.NativeEntry",
|
||||
"name": "requestConfig",
|
||||
"sig": "()V",
|
||||
"mod": "libhydeviceid.so",
|
||||
"off": "0x212a5c"
|
||||
},
|
||||
{
|
||||
"type": "reg-call",
|
||||
"cls": "com.huyaudb.HuyaAuthCore",
|
||||
"count": 4,
|
||||
"tid": 30763
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huyaudb.HuyaAuthCore",
|
||||
"name": "init",
|
||||
"sig": "()V",
|
||||
"mod": "libudbauthunify.so",
|
||||
"off": "0x2621d4"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huyaudb.HuyaAuthCore",
|
||||
"name": "unInit",
|
||||
"sig": "()V",
|
||||
"mod": "libudbauthunify.so",
|
||||
"off": "0x26238c"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huyaudb.HuyaAuthCore",
|
||||
"name": "sendMsg",
|
||||
"sig": "(J[B)[B",
|
||||
"mod": "libudbauthunify.so",
|
||||
"off": "0x26244c"
|
||||
},
|
||||
{
|
||||
"type": "reg",
|
||||
"cls": "com.huyaudb.HuyaAuthCore",
|
||||
"name": "receiveNet",
|
||||
"sig": "([BIII)V",
|
||||
"mod": "libudbauthunify.so",
|
||||
"off": "0x2626b0"
|
||||
}
|
||||
],
|
||||
"detached": [
|
||||
{
|
||||
"reason": "process-terminated",
|
||||
"detail": "None"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"vals": {
|
||||
"getGUID": "0a7dfaa882938a6ab502511452142c57",
|
||||
"getMID": "1e8bdf7d4f7a01d3",
|
||||
"getCDID": "02df398797432eadefcc12767119ad5e80999389",
|
||||
"getSDID": "PQwemAN9NHkZKoMqJ1d4FRIypqMTaQEOrmXr37xQVhQZq7L4uCKpS01ntlUkhq1Axdiy9nVNa/2z36z0Vbos+DdHlZJMBBhNP1vehQ3eO5kFXfuv9zZShivBPUqhhwn0zEuX90cIWKYq7Z98gtyiWLHCsORZI73C6IjhfJRFSXZYH0p8ly0=",
|
||||
"getHDID": "7c5387e0539c023c31c4ff0e807e7256117385ee"
|
||||
},
|
||||
"detached": []
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
"""通用 aarch64 反汇编工具: 分析内存 dump 的 libhydeviceid.so (解密后).
|
||||
|
||||
用法:
|
||||
arm64_disasm.py <dump.so> <模块基址> <偏移hex> [长度字节]
|
||||
例:
|
||||
反汇编 JNI_OnLoad(0x22c4c8) 前 0x300 字节:
|
||||
arm64_disasm.py evidence/diag_phone/libhydeviceid_dump.so 0x7814211000 0x22c4c8 0x300
|
||||
打印所有 BL/BLR 分支目标到模块偏移.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from capstone import Cs, CS_ARCH_ARM64, CS_MODE_ARM
|
||||
|
||||
|
||||
def load(fn):
|
||||
with open(fn, "rb") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def main():
|
||||
dump_path = sys.argv[1]
|
||||
base = int(sys.argv[2], 16)
|
||||
start_off = int(sys.argv[3], 16) # 相对模块基址的偏移 (内存态文件里约等于文件偏移)
|
||||
length = int(sys.argv[4], 16) if len(sys.argv) > 4 else 0x200
|
||||
data = load(dump_path)
|
||||
md = Cs(CS_ARCH_ARM64, CS_MODE_ARM)
|
||||
md.detail = True
|
||||
code = data[start_off:start_off + length]
|
||||
print(f"# base=0x{base:x} start_off=0x{start_off:x} len=0x{length:x}")
|
||||
for insn in md.disasm(code, base + start_off):
|
||||
disp = ""
|
||||
if insn.mnemonic.startswith(("b.", "b ")) or insn.mnemonic in ("bl", "br", "blr", "cbz", "cbnz"):
|
||||
if insn.operands:
|
||||
disp = f" ; -> 0x{insn.operands[0].imm:x}"
|
||||
elif insn.mnemonic == "blr":
|
||||
disp = " ; [indirect call]"
|
||||
print(f" 0x{insn.address:x}\t{insn.mnemonic:8s} {insn.op_str}{disp}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机 dump 解密后的 libhydeviceid.so (JNI_OnLoad 壳解密完成后).
|
||||
|
||||
用真机稳定注入通道 spawn+art_callsite, 等 libhydeviceid.so 加载且壳解密
|
||||
(JNI_OnLoad 返回)后 Memory.dump 整个模块, 保存为本地 .so 供反汇编分析.
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_dump_hydev.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/libhydeviceid_dump.so
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
|
||||
SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f"
|
||||
REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878"
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "libhydeviceid_dump.so"
|
||||
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
var dumped = false;
|
||||
function tryDump(){
|
||||
if(dumped) return;
|
||||
var md = Process.findModuleByName('libhydeviceid.so');
|
||||
if(!md) return;
|
||||
dumped = true;
|
||||
var ranges = md.enumerateRanges('r--');
|
||||
send({type:'plan', count:ranges.length, modBase:''+md.base});
|
||||
ranges.forEach(function(r, i){
|
||||
try{
|
||||
var buf = Memory.readByteArray(r.base, r.size);
|
||||
send({type:'seg', i:i, n:ranges.length, off:''+r.base.sub(md.base), size:r.size}, buf);
|
||||
}catch(e){ send({type:'seg-err', i:i, off:''+r.base.sub(md.base), e:String(e)}); }
|
||||
});
|
||||
send({type:'done', count:ranges.length});
|
||||
}
|
||||
setInterval(tryDump, 500);
|
||||
"""
|
||||
|
||||
|
||||
def adb(*a):
|
||||
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
pid = d.spawn([PACKAGE])
|
||||
print(f"[*] spawn pid={pid}", flush=True)
|
||||
session = d.attach(pid)
|
||||
session.on("detached", lambda r, dd: print(f"[*] detached {r} {dd}", flush=True))
|
||||
session.create_script(ART_CALLSITE.read_text()).load()
|
||||
result = {"got": 0, "expected": 0}
|
||||
fh = OUT.open("wb")
|
||||
def on_message(m, data):
|
||||
if m.get("type") == "error":
|
||||
print("[JS-ERR]", str(m)[:200], flush=True); return
|
||||
p = m.get("payload") or {}
|
||||
t = p.get("type")
|
||||
if t == "plan":
|
||||
result["expected"] = p["count"]
|
||||
print(f"[*] 计划 dump {p['count']} 个 r-x 段", flush=True)
|
||||
elif t == "seg":
|
||||
off = int(p["off"], 16)
|
||||
fh.seek(off)
|
||||
fh.write(data)
|
||||
result["got"] += 1
|
||||
print(f"[*] seg {p['i']+1}/{p['n']} off=0x{p['off']} size={p['size']}", flush=True)
|
||||
elif t == "done":
|
||||
print(f"[*] dump done, got {result['got']}/{result['expected']}", flush=True)
|
||||
elif t == "seg-err":
|
||||
print(f"[seg-err] {p}", flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print("[*] resumed, waiting dump...", flush=True)
|
||||
t0 = time.time()
|
||||
while (result["got"] < result["expected"] or result["expected"] == 0) and time.time() - t0 < 20:
|
||||
time.sleep(0.5)
|
||||
fh.close()
|
||||
OUT.chmod(0o644)
|
||||
print(f"[*] done got={result['got']}/{result['expected']} -> {OUT}", flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机: 主动 Java 调用 NativeEntry 的 getGUID/getMID/getCDID/getSDID/getHDID, 找 32hex hdid(field1.tag0).
|
||||
|
||||
hypasswordLogin 登录帧 field1.tag0 的 32hex hdid 未在 SSL 帧/其他 get* 中抓到,
|
||||
最可能是 getGUID()/getMID()(之前未触发) 的返回值。本脚本在 Java VM 就绪后
|
||||
用 Java.use 主动调用全部 get 方法并打印, 确定 32hex hdid 的来源方法。
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_java_call_nativeentry.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/java_nativeentry.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
|
||||
SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f"
|
||||
REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878"
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "java_nativeentry.json"
|
||||
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
setTimeout(function(){
|
||||
Java.perform(function(){
|
||||
var out = {};
|
||||
try{
|
||||
var NE = Java.use('com.huya.security.hydeviceid.NativeEntry');
|
||||
send({type:'class-ok', cls:'com.huya.security.hydeviceid.NativeEntry'});
|
||||
['init','getGUID','getMID','getCDID','getSDID','getHDID'].forEach(function(m){
|
||||
try{ out[m] = NE[m](); }catch(e){ out[m] = '<ERR:'+String(e).slice(0,50)+'>'; }
|
||||
send({type:'val', method:m, value:out[m]});
|
||||
});
|
||||
send({type:'all', out:out});
|
||||
}catch(e){
|
||||
send({type:'use-err', e:String(e).slice(0,200)});
|
||||
}
|
||||
});
|
||||
}, 6000);
|
||||
"""
|
||||
|
||||
|
||||
def adb(*a):
|
||||
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
pid = d.spawn([PACKAGE])
|
||||
print(f"[*] spawn pid={pid}", flush=True)
|
||||
session = d.attach(pid)
|
||||
det = []
|
||||
session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True))
|
||||
session.create_script(ART_CALLSITE.read_text()).load()
|
||||
result = {"vals": {}, "detached": det}
|
||||
def on_message(m, data):
|
||||
if m.get("type") == "error":
|
||||
print("[JS-ERR]", str(m)[:200], flush=True); return
|
||||
p = m.get("payload") or {}
|
||||
t = p.get("type")
|
||||
if t == "val":
|
||||
result["vals"][p["method"]] = p["value"]
|
||||
print(f"[val] {p['method']} = {p['value']}", flush=True)
|
||||
elif t == "class-ok":
|
||||
print(f"[*] class found", flush=True)
|
||||
elif t == "all":
|
||||
print(f"[*] 全部读取完成", flush=True)
|
||||
elif t in ("use-err",):
|
||||
print(f"[!] use-err: {p.get('e')}", flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print("[*] resumed", flush=True)
|
||||
time.sleep(20)
|
||||
result["detached"] = det
|
||||
OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8")
|
||||
print(f"[*] saved {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机探针: 枚举 libhydeviceid.so / libudbauthunify.so 导出符号, 定位 32hex hdid 生成入口.
|
||||
|
||||
用真机稳定注入通道 (spawn + art_callsite bypass + resume) 长时存活。
|
||||
App 启动即加载这两个 so, 枚举 exports 拿函数名+基址偏移, 为下一步
|
||||
针对性 hook 生成函数(抓输入)做准备。
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_probe_hdid.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/hdid_probe_exports.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
|
||||
SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f"
|
||||
REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878"
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "hdid_probe_exports.json"
|
||||
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
||||
|
||||
PROBE_JS = r"""
|
||||
'use strict';
|
||||
function listExports(modName, tag){
|
||||
try{
|
||||
var md = Process.findModuleByName(modName);
|
||||
if(!md){ send({type:'mod', name:modName, state:'not-loaded'}); return false; }
|
||||
var ex = md.enumerateExports();
|
||||
var sym = md.enumerateSymbols();
|
||||
send({type:'mod', name:modName, base:''+md.base, size:md.size,
|
||||
exports: ex.map(function(e){return {name:e.name, type:e.type, off:''+e.address.sub(md.base)};}),
|
||||
symCount: sym.length,
|
||||
syms: sym.map(function(s){return {name:s.name, off:''+s.address.sub(md.base)};})});
|
||||
return true;
|
||||
}catch(e){ send({type:'err', name:modName, e:String(e)}); return false; }
|
||||
}
|
||||
function hookPropGet(){
|
||||
var t = Module.findExportByName('libc.so','__system_property_get');
|
||||
if(!t) return;
|
||||
Interceptor.attach(t,{
|
||||
onEnter:function(a){ this.k = a[0].readCString()||''; },
|
||||
onLeave:function(ret){
|
||||
try{ send({type:'prop', k:this.k, v:(this.ctx.x1.readCString()||'')}); }catch(e){}
|
||||
}
|
||||
});
|
||||
send({type:'propget-hooked'});
|
||||
}
|
||||
// App 启动早期就抓 libhydeviceid.so 加载后的符号 + 派生属性读取
|
||||
setTimeout(function(){ listExports('libhydeviceid.so','hydev'); }, 2000);
|
||||
setTimeout(function(){ listExports('libudbauthunify.so','udb'); }, 2000);
|
||||
setTimeout(hookPropGet, 2000);
|
||||
"""
|
||||
|
||||
|
||||
def adb(*a):
|
||||
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
pid = d.spawn([PACKAGE])
|
||||
print(f"[*] spawn pid={pid}", flush=True)
|
||||
session = d.attach(pid)
|
||||
det = []
|
||||
session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True))
|
||||
session.create_script(ART_CALLSITE.read_text()).load()
|
||||
d.resume(pid)
|
||||
print("[*] bypass loaded + resumed", flush=True)
|
||||
|
||||
result = {"modules": {}, "props": []}
|
||||
def on_message(m, data):
|
||||
if m.get("type") == "error":
|
||||
print("[JS-ERR]", str(m)[:200], flush=True)
|
||||
return
|
||||
p = m.get("payload") or {}
|
||||
t = p.get("type")
|
||||
if t == "mod":
|
||||
result["modules"][p["name"]] = p
|
||||
print(f"[mod] {p['name']} base={p.get('base')} exports={len(p.get('exports') or [])} syms={p.get('symCount')}", flush=True)
|
||||
elif t == "prop":
|
||||
result["props"].append({"k": p.get("k"), "v": p.get("v")})
|
||||
elif t == "propget-hooked":
|
||||
print("[*] __system_property_get hooked", flush=True)
|
||||
elif t == "err":
|
||||
print(f"[err] {p.get('name')}: {p.get('e')}", flush=True)
|
||||
|
||||
sc = session.create_script(PROBE_JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
print("[*] probe loaded, collecting 12s", flush=True)
|
||||
time.sleep(12)
|
||||
result["detached"] = det
|
||||
OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8")
|
||||
# 打印关键: 各 so 的导出符号名(去重)
|
||||
for name, m in result["modules"].items():
|
||||
print(f"\n=== {name} exports ({len(m.get('exports') or [])}) ===")
|
||||
for e in (m.get("exports") or [])[:60]:
|
||||
print(f" 0x{e['off']} {e['name']}")
|
||||
print("\n=== 派生属性 (去重) ===")
|
||||
seen = {}
|
||||
for pr in result["props"]:
|
||||
seen[pr["k"]] = pr["v"]
|
||||
for k, v in seen.items():
|
||||
print(f" {k} = {v}")
|
||||
print(f"\n[*] saved {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机探针 v3: 等 so 加载后 hook, 抓 32hex hdid 生成/使用链路 + 登录触发.
|
||||
|
||||
修正 v1/v2: 挂起时 libhydeviceid.so 尚未加载 -> 用 setInterval 轮询,
|
||||
so 一加载即 hook:
|
||||
- libhydeviceid.so: 枚举非 runtime 导出 + hook JNI_OnLoad(0x22c4c8)
|
||||
- libudbauthunify.so: hook BusinessCfg::getHdid(0x26a484) / setSafeDeviceId(0x26a2e0) 带调用栈
|
||||
resume 后 am start GameSdkLoginActivity 触发设备上报/登录, 让 hdid 被读取/生成.
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_probe_hdid3.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/hdid_probe3.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
|
||||
SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f"
|
||||
REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878"
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
LOGIN_ACT = "com.duowan.kiwi/.loginui.impl.gamesdk.GameSdkLoginActivity"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "hdid_probe3.json"
|
||||
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
function stdstr(p){
|
||||
try{
|
||||
if(p.isNull()) return '';
|
||||
var first = p.readU8();
|
||||
if((first&1)===0){ var l=first>>1; return l? p.add(1).readUtf8String(l):''; }
|
||||
else { var d=p.readPointer(); var l=p.add(8).readU64(); return (l&&l<256)? d.readUtf8String(l):''; }
|
||||
}catch(e){ return ''; }
|
||||
}
|
||||
var hydev=null, udb=null;
|
||||
function hookHydev(){
|
||||
if(hydev) return;
|
||||
var md = Process.findModuleByName('libhydeviceid.so');
|
||||
if(!md) return;
|
||||
hydev = md;
|
||||
send({type:'hydev', base:''+md.base, size:md.size});
|
||||
var ex = md.enumerateExports();
|
||||
var useful = ex.filter(function(e){ return !/^_Z/.test(e.name) && !/^__/.test(e.name) && e.name!==''; });
|
||||
send({type:'hydev-exports', count:useful.length,
|
||||
exports: useful.map(function(e){ return {off:''+e.address.sub(md.base), name:e.name}; })});
|
||||
try{
|
||||
Interceptor.attach(md.base.add(0x22c4c8), {
|
||||
onEnter:function(){ send({type:'jni-onload-enter', tid:Process.getCurrentThreadId()}); },
|
||||
onLeave:function(ret){ send({type:'jni-onload-leave', ret:''+ret}); }
|
||||
});
|
||||
send({type:'jni-onload-hooked'});
|
||||
}catch(e){ send({type:'hydev-err', e:String(e)}); }
|
||||
}
|
||||
function hookUdb(){
|
||||
if(udb) return;
|
||||
var md = Process.findModuleByName('libudbauthunify.so');
|
||||
if(!md) return;
|
||||
udb = md;
|
||||
send({type:'udb', base:''+md.base});
|
||||
try{
|
||||
Interceptor.attach(md.base.add(0x26a484), {
|
||||
onLeave:function(ret){ send({type:'hdid', str:stdstr(ret), tid:Process.getCurrentThreadId()}); }
|
||||
});
|
||||
send({type:'hdid-hooked'});
|
||||
}catch(e){ send({type:'udb-err', e:String(e)}); }
|
||||
try{
|
||||
Interceptor.attach(md.base.add(0x26a2e0), {
|
||||
onEnter:function(a){
|
||||
send({type:'setsd', sd:stdstr(a[1]), hd:stdstr(a[2]), tid:Process.getCurrentThreadId()});
|
||||
try{
|
||||
var bt=Thread.backtrace(this.context, Backtracer.ACCURATE).slice(0,20).map(function(x){return x.toString();});
|
||||
send({type:'bt', bt:bt});
|
||||
}catch(e){}
|
||||
}
|
||||
});
|
||||
send({type:'setsd-hooked'});
|
||||
}catch(e){}
|
||||
}
|
||||
setInterval(hookHydev, 40);
|
||||
setInterval(hookUdb, 40);
|
||||
"""
|
||||
|
||||
|
||||
def adb(*a):
|
||||
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
pid = d.spawn([PACKAGE])
|
||||
print(f"[*] spawn pid={pid}", flush=True)
|
||||
session = d.attach(pid)
|
||||
det = []
|
||||
session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True))
|
||||
session.create_script(ART_CALLSITE.read_text()).load()
|
||||
result = {"events": [], "detached": det}
|
||||
def on_message(m, data):
|
||||
if m.get("type") == "error":
|
||||
print("[JS-ERR]", str(m)[:200], flush=True); return
|
||||
p = m.get("payload") or {}
|
||||
result["events"].append(p)
|
||||
t = p.get("type")
|
||||
if t in ("hdid", "setsd"):
|
||||
print(f"[{t}] {p.get('hd') or p.get('str')} tid={p.get('tid')}", flush=True)
|
||||
elif t == "bt":
|
||||
print(" bt:", " <- ".join(p["bt"][:8]), flush=True)
|
||||
elif t == "hydev":
|
||||
print(f"[*] libhydeviceid loaded base={p.get('base')}", flush=True)
|
||||
elif t == "hydev-exports":
|
||||
print(f"[*] hydev 非runtime导出 {p.get('count')}:", flush=True)
|
||||
for e in (p.get("exports") or [])[:40]:
|
||||
print(f" 0x{e['off']} {e['name']}")
|
||||
elif t in ("udb","hdid-hooked","setsd-hooked","jni-onload-hooked","jni-onload-enter","jni-onload-leave"):
|
||||
print(f"[*] {t}", p if t in ("jni-onload-leave",) else "", flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print("[*] resumed", flush=True)
|
||||
time.sleep(5)
|
||||
print("[*] 触发登录 Activity...", flush=True)
|
||||
adb("shell", "am", "start", "-n", LOGIN_ACT)
|
||||
time.sleep(15)
|
||||
result["detached"] = det
|
||||
OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8")
|
||||
print(f"[*] saved {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机探针 v4: 抓 libhydeviceid.so 生成 hdid 的输入(系统属性/文件/随机源).
|
||||
|
||||
libhydeviceid.so 加壳, 生成函数不导出。改为抓它的"输入":
|
||||
- hook libc __system_property_get (spawn 挂起时已加载) -> App 启动读的属性
|
||||
- hook libhydeviceid 的 hy_fopen64/hy_read/hy_syscall (壳文件包装) -> 访问的文件
|
||||
- hook getHdid(0x26a484) 时同时 dump x0 原始字节, 定位为何返回空
|
||||
这能判断 hdid 依赖 ro.serialno / ANDROID_ID / /proc 信息 / 随机源等, 决定能否复现.
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_probe_hdid4.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/hdid_probe4.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
|
||||
SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f"
|
||||
REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878"
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
LOGIN_ACT = "com.duowan.kiwi/.loginui.impl.gamesdk.GameSdkLoginActivity"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "hdid_probe4.json"
|
||||
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
function stdstr(p){
|
||||
try{
|
||||
if(p.isNull()) return '';
|
||||
var first = p.readU8();
|
||||
if((first&1)===0){ var l=first>>1; return l? p.add(1).readUtf8String(l):''; }
|
||||
else { var d=p.readPointer(); var l=p.add(8).readU64(); return (l&&l<512)? d.readUtf8String(l):''; }
|
||||
}catch(e){ return ''; }
|
||||
}
|
||||
// 1) spawn 挂起时 hook __system_property_get (libc 已加载)
|
||||
(function(){
|
||||
try{
|
||||
var t = Module.findExportByName('libc.so','__system_property_get');
|
||||
if(!t){ send({type:'no-propget'}); return; }
|
||||
Interceptor.attach(t,{
|
||||
onEnter:function(a){ this.k = a[0].readCString()||''; this.vp = a[1]; },
|
||||
onLeave:function(ret){ try{ send({type:'prop', k:this.k, v:this.vp.readCString()||''}); }catch(e){ send({type:'prop', k:this.k, v:'<err>'}); } }
|
||||
});
|
||||
send({type:'propget-hooked'});
|
||||
}catch(e){ send({type:'propget-err', e:String(e)}); }
|
||||
})();
|
||||
|
||||
var hydev=null;
|
||||
function hookHydev(){
|
||||
if(hydev) return;
|
||||
var md = Process.findModuleByName('libhydeviceid.so');
|
||||
if(!md) return;
|
||||
hydev = md;
|
||||
send({type:'hydev', base:''+md.base});
|
||||
// 文件操作壳包装: hy_fopen64(0xf1298), hy_read(0x30bef0), hy_syscall(0x30bf3c)
|
||||
var targets = {0xf1298:'hy_fopen64', 0x30bef0:'hy_read', 0x30bf3c:'hy_syscall', 0x30bf00:'hy_write', 0xf3360:'hyftell'};
|
||||
Object.keys(targets).forEach(function(off){
|
||||
try{
|
||||
Interceptor.attach(md.base.add(parseInt(off,16)), {
|
||||
onEnter:function(a){
|
||||
var s='';
|
||||
try{ s = (a[0]&&!a[0].isNull()) ? a[0].readCString(256)||'' : ''; }catch(e){}
|
||||
send({type:'hyio', fn:targets[off], arg0:s, tid:Process.getCurrentThreadId()});
|
||||
}
|
||||
});
|
||||
}catch(e){}
|
||||
});
|
||||
send({type:'hyio-hooked'});
|
||||
}
|
||||
// getHdid 返回空排查: dump x0 原始字节
|
||||
function hookUdb(){
|
||||
var md = Process.findModuleByName('libudbauthunify.so');
|
||||
if(!md) return;
|
||||
try{
|
||||
Interceptor.attach(md.base.add(0x26a484), {
|
||||
onLeave:function(ret){
|
||||
var s=stdstr(ret);
|
||||
var hex='';
|
||||
try{ hex = Array.from(new Uint8Array(ret.readByteArray(64))).map(function(b){return ('0'+b.toString(16)).slice(-2);}).join(''); }catch(e){}
|
||||
send({type:'hdid', str:s, ret:''+ret, hex:hex, tid:Process.getCurrentThreadId()});
|
||||
}
|
||||
});
|
||||
send({type:'hdid-hooked'});
|
||||
}catch(e){ send({type:'udb-err', e:String(e)}); }
|
||||
}
|
||||
setInterval(hookHydev, 40);
|
||||
setInterval(hookUdb, 40);
|
||||
"""
|
||||
|
||||
|
||||
def adb(*a):
|
||||
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
pid = d.spawn([PACKAGE])
|
||||
print(f"[*] spawn pid={pid}", flush=True)
|
||||
session = d.attach(pid)
|
||||
det = []
|
||||
session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True))
|
||||
session.create_script(ART_CALLSITE.read_text()).load()
|
||||
result = {"props": [], "hyio": [], "hdid": [], "detached": det}
|
||||
def on_message(m, data):
|
||||
if m.get("type") == "error":
|
||||
print("[JS-ERR]", str(m)[:200], flush=True); return
|
||||
p = m.get("payload") or {}
|
||||
t = p.get("type")
|
||||
if t == "prop":
|
||||
result["props"].append(p); print(f"[prop] {p.get('k')} = {p.get('v')}", flush=True)
|
||||
elif t == "hyio":
|
||||
result["hyio"].append(p); print(f"[hyio] {p.get('fn')} arg0={p.get('arg0')}", flush=True)
|
||||
elif t == "hdid":
|
||||
result["hdid"].append(p); print(f"[hdid] str={p.get('str')!r} ret={p.get('ret')} hex={p.get('hex')}", flush=True)
|
||||
elif t in ("propget-hooked","hydev","hyio-hooked","hdid-hooked","no-propget"):
|
||||
print(f"[*] {t}", flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print("[*] resumed", flush=True)
|
||||
time.sleep(4)
|
||||
print("[*] 触发登录 Activity...", flush=True)
|
||||
adb("shell", "am", "start", "-n", LOGIN_ACT)
|
||||
time.sleep(15)
|
||||
result["detached"] = det
|
||||
OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8")
|
||||
print(f"[*] saved {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机 v6: hook SSL_write 抓 hypasswordLogin 登录帧, 提取实际 hdid + 追来源.
|
||||
|
||||
目标: 确认登录帧 t1.t0 的 32hex hdid 实际值(应为金样本 ed0db8...),
|
||||
并 hook 它进入 WUP 帧前被读取的调用链。同时 hook:
|
||||
- libc strstr/strcpy 等 (登录帧组装时 hdid 字符串被拷贝处)
|
||||
- BusinessCfg::getSafeDeviceId(0x26a3c4) / setDeviceInfo(0x26a894)
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_probe_hdid6.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/hdid_probe6.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
|
||||
SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f"
|
||||
REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878"
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
LOGIN_ACT = "com.duowan.kiwi/.loginui.impl.gamesdk.GameSdkLoginActivity"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "hdid_probe6.json"
|
||||
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
function hexb(p,n){try{return Array.from(new Uint8Array(p.readByteArray(n))).map(b=>('0'+b.toString(16)).slice(-2)).join('');}catch(e){return '';}}
|
||||
function stdstr(p){
|
||||
try{
|
||||
if(p.isNull()) return '';
|
||||
var f = p.readU8();
|
||||
if((f&1)===0){ var l=f>>1; return l? p.add(1).readUtf8String(l):''; }
|
||||
else { var d=p.readPointer(); var l=p.add(8).readU64(); return (l&&l<512)? d.readUtf8String(l):''; }
|
||||
}catch(e){ return ''; }
|
||||
}
|
||||
var sslDone=false, udbHooked=false;
|
||||
function hookSSL(){
|
||||
if(sslDone) return;
|
||||
try{
|
||||
var r=new ApiResolver('module');
|
||||
r.enumerateMatchesSync('exports:*!SSL_write').forEach(function(m){
|
||||
Interceptor.attach(m.address,{onEnter:function(a){
|
||||
var len=a[2].toInt32();
|
||||
if(len<50||len>50000)return;
|
||||
var h=''; try{ h=a[1].readCString(Math.min(len,600))||''; }catch(e){}
|
||||
if(h.indexOf('hypasswordLogin')>=0){
|
||||
send({type:'wup_login', len:len, hex:hexb(a[1],len)});
|
||||
}
|
||||
}});
|
||||
});
|
||||
send({type:'sslwrite-hooked'});
|
||||
}catch(e){}
|
||||
sslDone=true;
|
||||
}
|
||||
function hookUdb(){
|
||||
if(udbHooked) return;
|
||||
var md = Process.findModuleByName('libudbauthunify.so');
|
||||
if(!md) return;
|
||||
udbHooked=true;
|
||||
send({type:'udb', base:''+md.base});
|
||||
// 设备信息读取点
|
||||
try{
|
||||
Interceptor.attach(md.base.add(0x26a3c4), { // getSafeDeviceId
|
||||
onLeave:function(ret){ send({type:'getsd', ret:''+ret}); }
|
||||
});
|
||||
}catch(e){}
|
||||
try{
|
||||
Interceptor.attach(md.base.add(0x26a894), { // setDeviceInfo(a1=this, a2..a5=std::string*)
|
||||
onEnter:function(a){
|
||||
send({type:'setdi', args:[stdstr(a[1]), stdstr(a[2]), stdstr(a[3]), stdstr(a[4])]});
|
||||
}
|
||||
});
|
||||
}catch(e){}
|
||||
send({type:'udb-hooked'});
|
||||
}
|
||||
// libhydeviceid 导出函数调用探测
|
||||
var hyTried=false;
|
||||
function hookHy(){
|
||||
if(hyTried) return;
|
||||
var md = Process.findModuleByName('libhydeviceid.so');
|
||||
if(!md) return;
|
||||
hyTried=true;
|
||||
send({type:'hydev', base:''+md.base});
|
||||
['hyfopen64','hy_read','hy_syscall','hy_write','hyftell'].forEach(function(n){
|
||||
try{
|
||||
var a = Module.findExportByName('libhydeviceid.so', n);
|
||||
if(a) Interceptor.attach(a,{onEnter:function(args){
|
||||
var s=''; try{ s=args[0].readCString(128)||''; }catch(e){}
|
||||
send({type:'hycall', fn:n, a0:s, tid:Process.getCurrentThreadId()});
|
||||
}});
|
||||
}catch(e){}
|
||||
});
|
||||
send({type:'hydev-hooked'});
|
||||
}
|
||||
setInterval(hookSSL, 800);
|
||||
setInterval(hookUdb, 50);
|
||||
setInterval(hookHy, 50);
|
||||
"""
|
||||
|
||||
|
||||
def adb(*a):
|
||||
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
pid = d.spawn([PACKAGE])
|
||||
print(f"[*] spawn pid={pid}", flush=True)
|
||||
session = d.attach(pid)
|
||||
det = []
|
||||
session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True))
|
||||
session.create_script(ART_CALLSITE.read_text()).load()
|
||||
result = {"frames": [], "events": [], "detached": det}
|
||||
def on_message(m, data):
|
||||
if m.get("type") == "error":
|
||||
print("[JS-ERR]", str(m)[:200], flush=True); return
|
||||
p = m.get("payload") or {}
|
||||
t = p.get("type")
|
||||
result["events"].append(p)
|
||||
if t == "wup_login":
|
||||
result["frames"].append(p)
|
||||
b = bytes.fromhex(p.get("hex", ""))
|
||||
# 提取帧里的 32hex hdid
|
||||
h32 = re.findall(rb"[0-9a-f]{32}", b)
|
||||
print(f"[WUP_LOGIN] len={p.get('len')} hdid候选: {[h.decode() for h in h32[:3]]}", flush=True)
|
||||
elif t in ("udb","udb-hooked","sslwrite-hooked","hydev","hydev-hooked"):
|
||||
print(f"[*] {t}", p if t=="udb" else "", flush=True)
|
||||
elif t == "setdi":
|
||||
print(f"[setDeviceInfo] args={p.get('args')}", flush=True)
|
||||
elif t == "getsd":
|
||||
print(f"[getSafeDeviceId] ret={p.get('ret')}", flush=True)
|
||||
elif t == "hycall":
|
||||
print(f"[hycall] {p.get('fn')} a0={p.get('a0')}", flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print("[*] resumed", flush=True)
|
||||
time.sleep(4)
|
||||
print("[*] 触发登录...", flush=True)
|
||||
adb("shell", "am", "start", "-n", LOGIN_ACT)
|
||||
time.sleep(18)
|
||||
result["detached"] = det
|
||||
OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8")
|
||||
print(f"[*] 登录帧数: {len(result['frames'])} saved {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机 v9: 触发一键登录, hook SSL_write 抓 WUP 登录帧 + createWupDeviceInfo(0x2746a0),
|
||||
从登录帧提取 32hex hdid (t1.t0) 和其来源.
|
||||
|
||||
触发: 点击 GameSdkLoginActivity 的"本机号码一键登录" (mBtnLogin, 中心 540,1287),
|
||||
App 会自动发起登录 WUP 请求, 帧内经 JCE 编码含 t1.t0=hdid 字段.
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_probe_login_frame.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/login_frames.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
|
||||
SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f"
|
||||
REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878"
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
LOGIN_ACT = "com.duowan.kiwi/.loginui.impl.gamesdk.GameSdkLoginActivity"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "login_frames.json"
|
||||
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
var sslDone=false, udbHooked=false, cdiHooked=false;
|
||||
function hookSSL(){
|
||||
if(sslDone) return;
|
||||
try{
|
||||
var r=new ApiResolver('module');
|
||||
r.enumerateMatchesSync('exports:*!SSL_write').forEach(function(m){
|
||||
Interceptor.attach(m.address,{onEnter:function(a){
|
||||
var len=a[2].toInt32();
|
||||
if(len<200||len>80000) return;
|
||||
try{
|
||||
var buf=a[1].readByteArray(len);
|
||||
send({type:'sslfr', len:len, hex:Array.from(new Uint8Array(buf)).map(function(b){return ('0'+b.toString(16)).slice(-2);}).join('')});
|
||||
}catch(e){}
|
||||
}});
|
||||
});
|
||||
send({type:'ssl-hooked'});
|
||||
}catch(e){ send({type:'ssl-err', e:String(e)}); }
|
||||
sslDone=true;
|
||||
}
|
||||
function hookUdb(){
|
||||
if(udbHooked) return;
|
||||
var md=Process.findModuleByName('libudbauthunify.so');
|
||||
if(!md) return;
|
||||
udbHooked=true;
|
||||
send({type:'udb', base:''+md.base});
|
||||
try{ // BusinessCfg::getHdid
|
||||
Interceptor.attach(md.base.add(0x26a484),{onLeave:function(ret){
|
||||
var s='<n>';
|
||||
try{ s = ret.isNull()? '<null>' : ret.readCString(64)||'<?>'; }catch(e){ s='<err>'; }
|
||||
var bt='';
|
||||
try{ bt = Thread.backtrace(this.context, Backtracer.ACCURATE).slice(0,16).map(function(x){return x.toString();}).join(' <- '); }catch(e){}
|
||||
send({type:'getHdid', ret:''+ret, str:s, bt:bt});
|
||||
}});
|
||||
}catch(e){}
|
||||
try{ // createWupDeviceInfo(wup::DeviceInfo*)
|
||||
Interceptor.attach(md.base.add(0x2746a0),{onEnter:function(a){ send({type:'createWupDev-enter', tid:Process.getCurrentThreadId()}); }});
|
||||
}catch(e){}
|
||||
send({type:'udb-hooked'});
|
||||
}
|
||||
setInterval(hookSSL, 500);
|
||||
setInterval(hookUdb, 60);
|
||||
"""
|
||||
|
||||
|
||||
def adb(*a):
|
||||
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
||||
|
||||
|
||||
def tap(x, y):
|
||||
adb("shell", f"input tap {x} {y}")
|
||||
|
||||
|
||||
def main():
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
pid = d.spawn([PACKAGE])
|
||||
print(f"[*] spawn pid={pid}", flush=True)
|
||||
session = d.attach(pid)
|
||||
det = []
|
||||
session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True))
|
||||
session.create_script(ART_CALLSITE.read_text()).load()
|
||||
result = {"frames": [], "events": [], "detached": det}
|
||||
def on_message(m, data):
|
||||
if m.get("type") == "error":
|
||||
print("[JS-ERR]", str(m)[:200], flush=True); return
|
||||
p = m.get("payload") or {}
|
||||
t = p.get("type")
|
||||
result["events"].append(p)
|
||||
if t == "sslfr":
|
||||
result["frames"].append(p)
|
||||
b = bytes.fromhex(p.get("hex", ""))
|
||||
# WUP/JCE 帧特征: 找 t1.t0 的 32hex 候选
|
||||
h32 = list(set(x.decode() for x in re.findall(rb"[0-9a-f]{32}", b)))
|
||||
h40 = list(set(x.decode() for x in re.findall(rb"[0-9a-f]{40}", b)))
|
||||
print(f"[ssl] len={p['len']} 32hex候选={h32[:4]} 40hex候选={h40[:2]}", flush=True)
|
||||
elif t in ("udb","udb-hooked","ssl-hooked"):
|
||||
print(f"[*] {t}", p if t=="udb" else "", flush=True)
|
||||
elif t == "getHdid":
|
||||
print(f"[getHdid] ret={p.get('ret')} str={p.get('str')[:60]!r}", flush=True)
|
||||
if p.get("bt"):
|
||||
print(f" bt: {p['bt']}", flush=True)
|
||||
elif t == "createWupDev-enter":
|
||||
print(f"[createWupDeviceInfo] enter tid={p.get('tid')}", flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print("[*] resumed", flush=True)
|
||||
time.sleep(6)
|
||||
# 打开登录页 (不自动点击, 交给用户手动切账号密码登录)
|
||||
adb("shell", "am", "start", "-n", LOGIN_ACT)
|
||||
time.sleep(3)
|
||||
duration = int(sys.argv[3]) if len(sys.argv) > 3 else 240
|
||||
print(f"[*] 探针持续抓帧 {duration}s, 请手动登录...", flush=True)
|
||||
time.sleep(duration)
|
||||
result["detached"] = det
|
||||
OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8")
|
||||
print(f"[*] 帧数: {len(result['frames'])} saved {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机探针 v2: 抓 libhydeviceid.so 经 RegisterNatives 注册的 native 方法 + getHdid 调用栈.
|
||||
|
||||
libhydeviceid.so 没有 JNI/生成函数导出(核心方法经 JNI_OnLoad -> RegisterNatives 动态注册)。
|
||||
本脚本在 spawn 挂起中 hook libart.so 的 RegisterNatives, App 启动早期注册会被捕获,
|
||||
拿到 libhydeviceid.so 注册的 native 方法指针 -> 基址偏移, 定位 hdid 生成入口。
|
||||
同时 hook libudbauthunify.so 的 BusinessCfg::getHdid(0x26a484) 抓 hdid 值+调用栈。
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_probe_registernatives.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/hdid_registernatives.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
|
||||
SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f"
|
||||
REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878"
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "hdid_registernatives.json"
|
||||
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
function stdstr(p){
|
||||
try{
|
||||
if(p.isNull()) return '';
|
||||
var first = p.readU8();
|
||||
if((first & 1)===0){ var l=first>>1; return l? p.add(1).readUtf8String(l):''; }
|
||||
else { var d=p.readPointer(); var l=p.add(8).readU64(); return (l&&l<256)? d.readUtf8String(l):''; }
|
||||
}catch(e){ return ''; }
|
||||
}
|
||||
function modOf(a){ try{ var m=Process.findModuleByAddress(a); return m? {n:m.name, off:''+a.sub(m.base)}: {n:'?', off:'?'}; }catch(e){ return {n:'?',off:'?'}; } }
|
||||
|
||||
// 1) hook libart RegisterNatives
|
||||
(function(){
|
||||
var RN = Module.findExportByName('libart.so','_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi');
|
||||
if(!RN){ send({type:'rn-notfound'}); return; }
|
||||
send({type:'rn-found', a:''+RN});
|
||||
Interceptor.attach(RN,{
|
||||
onEnter:function(a){
|
||||
var count = a[3].toInt32();
|
||||
if(count<=0||count>500) return;
|
||||
var cls='';
|
||||
try{ cls = Java.vm.tryGetEnv().getClassName(a[1])||''; }catch(e){ cls='?'; }
|
||||
for(var i=0;i<count;i++){
|
||||
try{
|
||||
var base=a[2].add(i*24);
|
||||
var name=base.readPointer().readCString()||'';
|
||||
var sig =base.add(8).readPointer().readCString()||'';
|
||||
var fn =base.add(16).readPointer();
|
||||
var mo=modOf(fn);
|
||||
send({type:'reg', cls:cls, name:name, sig:sig, mod:mo.n, off:mo.off});
|
||||
}catch(e){}
|
||||
}
|
||||
}
|
||||
});
|
||||
send({type:'rn-hooked'});
|
||||
})();
|
||||
|
||||
// 2) hook BusinessCfg::getHdid (libudbauthunify.so 0x26a484)
|
||||
(function(){
|
||||
var md = Process.findModuleByName('libudbauthunify.so');
|
||||
if(!md){ send({type:'udb-not-loaded'}); return; }
|
||||
try{
|
||||
Interceptor.attach(md.base.add(0x26a484),{
|
||||
onLeave:function(ret){
|
||||
var s=stdstr(ret);
|
||||
send({type:'hdid', str:s, tid:Process.getCurrentThreadId()});
|
||||
}
|
||||
});
|
||||
Interceptor.attach(md.base.add(0x26a2e0),{ // setSafeDeviceId(a2=sd, a3=hdid)
|
||||
onEnter:function(a){
|
||||
send({type:'setsd', sd:stdstr(a[1]), hd:stdstr(a[2]), tid:Process.getCurrentThreadId()});
|
||||
try{
|
||||
var bt=Thread.backtrace(this.context, Backtracer.ACCURATE).slice(0,24).map(function(x){return x.toString();});
|
||||
send({type:'bt', bt:bt});
|
||||
}catch(e){}
|
||||
}
|
||||
});
|
||||
send({type:'udb-hooked'});
|
||||
}catch(e){ send({type:'udb-err', e:String(e)}); }
|
||||
})();
|
||||
"""
|
||||
|
||||
|
||||
def adb(*a):
|
||||
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
pid = d.spawn([PACKAGE])
|
||||
print(f"[*] spawn pid={pid}", flush=True)
|
||||
session = d.attach(pid)
|
||||
det = []
|
||||
session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True))
|
||||
session.create_script(ART_CALLSITE.read_text()).load()
|
||||
print("[*] art_callsite loaded", flush=True)
|
||||
result = {"regs": [], "hdid": [], "detached": det}
|
||||
def on_message(m, data):
|
||||
if m.get("type") == "error":
|
||||
print("[JS-ERR]", str(m)[:200], flush=True); return
|
||||
p = m.get("payload") or {}
|
||||
t = p.get("type")
|
||||
if t == "reg":
|
||||
result["regs"].append(p)
|
||||
elif t in ("hdid", "setsd"):
|
||||
result["hdid"].append(p)
|
||||
print(f"[{t}] {p}", flush=True)
|
||||
elif t == "bt":
|
||||
print(" bt:", " <- ".join(p["bt"][:8]), flush=True)
|
||||
elif t in ("rn-found","rn-hooked","udb-hooked"):
|
||||
print(f"[*] {t}", flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print("[*] resumed, collecting 18s", flush=True)
|
||||
time.sleep(18)
|
||||
result["detached"] = det
|
||||
OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
# 汇总 libhydeviceid.so 注册的方法
|
||||
print("\n=== libhydeviceid.so 注册的 native 方法 ===")
|
||||
seen = set()
|
||||
for r in result["regs"]:
|
||||
if r.get("mod") == "libhydeviceid.so" and (r["name"], r["off"]) not in seen:
|
||||
seen.add((r["name"], r["off"]))
|
||||
print(f" 0x{r['off']} {r['name']}{r['sig']} (class={r.get('cls')})")
|
||||
print(f"\n=== 所有 so 注册统计 ===")
|
||||
from collections import Counter
|
||||
c = Counter(r.get("mod") for r in result["regs"])
|
||||
for mod, n in c.most_common():
|
||||
print(f" {mod}: {n}")
|
||||
print(f"\n[*] saved {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机 v7: dlopen 拦截 libhydeviceid -> attach JNI_OnLoad -> hook JNINativeInterface::RegisterNatives
|
||||
拿到 libhydeviceid.so 动态注册的全部 native 方法(名字+签名+函数地址).
|
||||
|
||||
绕过 OLLVM 混淆: 不去静态反汇编 JNI_OnLoad, 而是运行时在 JNI_OnLoad 里用 env 的
|
||||
JNINativeInterface 函数表定位 RegisterNatives 槽位并 hook, 抓 JNINativeMethod 数组.
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_probe_registernatives2.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/hdid_regtable.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
|
||||
SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f"
|
||||
REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878"
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "hdid_regtable.json"
|
||||
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
||||
|
||||
# JNINativeInterface 中 RegisterNatives 的索引 (JNI 1.6 规范, 含 4 个 reserved)
|
||||
RN_INDEX = 215
|
||||
|
||||
|
||||
def mod_of(a):
|
||||
try:
|
||||
m = Process.findModuleByAddress(a)
|
||||
return (m.name, str(a.sub(m.base))) if m else ("?", "?")
|
||||
except Exception:
|
||||
return ("?", "?")
|
||||
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
function jmod(a){
|
||||
try{ var m = Process.findModuleByAddress(a); return m ? {n:m.name, off:''+a.sub(m.base)} : {n:'?',off:'?'}; }catch(e){ return {n:'?',off:'?'}; }
|
||||
}
|
||||
var hooked = false;
|
||||
function attachJniOnload(md){
|
||||
try{
|
||||
Interceptor.attach(md.base.add(0x22c4c8), {
|
||||
onEnter:function(a){
|
||||
send({type:'jni-onload-enter', tid:Process.getCurrentThreadId()});
|
||||
try{
|
||||
var env = Java.vm.tryGetEnv();
|
||||
var fns = env.handle.readPointer(); // JNINativeInterface*
|
||||
// 打印 200..230 槽位的所属模块, 验证表结构
|
||||
var dump=[];
|
||||
for(var i=200;i<=230;i++){
|
||||
var p = fns.add(i*8).readPointer();
|
||||
var m = jmod(p);
|
||||
dump.push({i:i, a:''+p, mod:m.n, off:m.off});
|
||||
}
|
||||
send({type:'fns-scan', dump:dump});
|
||||
var rn = fns.add(%d*8).readPointer();
|
||||
send({type:'rn-addr', rn:''+rn, mod:jmod(rn)});
|
||||
Interceptor.attach(rn, {
|
||||
onEnter:function(args){
|
||||
// args[1]=jclass clazz, args[2]=JNINativeMethod* methods, args[3]=jint n
|
||||
var count = args[3].toInt32();
|
||||
var methods = args[2];
|
||||
var cls='';
|
||||
try{ cls = Java.vm.tryGetEnv().getClassName(args[1]||Java.vm.tryGetEnv().getClass(args[1])); }catch(e){ cls='?'; }
|
||||
send({type:'reg-call', cls:cls, count:count, tid:Process.getCurrentThreadId()});
|
||||
for(var i=0;i<count;i++){
|
||||
try{
|
||||
var base = methods.add(i*24);
|
||||
var name = base.readPointer().readCString()||'';
|
||||
var sig = base.add(8).readPointer().readCString()||'';
|
||||
var fn = base.add(16).readPointer();
|
||||
var m = jmod(fn);
|
||||
send({type:'reg', cls:cls, name:name, sig:sig, mod:m.n, off:m.off});
|
||||
}catch(e){}
|
||||
}
|
||||
}
|
||||
});
|
||||
send({type:'rn-hooked'});
|
||||
}catch(e){ send({type:'rn-err', e:String(e)}); }
|
||||
}
|
||||
});
|
||||
send({type:'jni-onload-hooked'});
|
||||
}catch(e){ send({type:'jni-err', e:String(e)}); }
|
||||
}
|
||||
function hookDlopen(){
|
||||
if(hooked) return;
|
||||
var dl = Module.findExportByName(null,'android_dlopen_ext');
|
||||
var dlname = 'android_dlopen_ext';
|
||||
if(!dl){ dl = Module.findExportByName(null,'dlopen'); dlname='dlopen'; }
|
||||
if(!dl) return;
|
||||
hooked = true;
|
||||
send({type:'dlopen-hooked', fn:dlname});
|
||||
Interceptor.attach(dl, {
|
||||
onEnter:function(a){ try{ this.name = a[0].readCString()||''; }catch(e){ this.name=''; } },
|
||||
onLeave:function(){
|
||||
if(this.name && this.name.indexOf('libhydeviceid')>=0){
|
||||
send({type:'dlopen-hydev', name:this.name});
|
||||
var md = Process.findModuleByName('libhydeviceid.so');
|
||||
if(md){ attachJniOnload(md); }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
setInterval(hookDlopen, 40);
|
||||
""" % RN_INDEX
|
||||
|
||||
|
||||
def adb(*a):
|
||||
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
pid = d.spawn([PACKAGE])
|
||||
print(f"[*] spawn pid={pid}", flush=True)
|
||||
session = d.attach(pid)
|
||||
det = []
|
||||
session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True))
|
||||
session.create_script(ART_CALLSITE.read_text()).load()
|
||||
result = {"regs": [], "fns_scan": [], "events": [], "detached": det}
|
||||
def on_message(m, data):
|
||||
if m.get("type") == "error":
|
||||
print("[JS-ERR]", str(m)[:200], flush=True); return
|
||||
p = m.get("payload") or {}
|
||||
t = p.get("type")
|
||||
result["events"].append(p)
|
||||
if t == "reg":
|
||||
result["regs"].append(p)
|
||||
print(f"[reg] {p.get('name')}{p.get('sig')} @ {p.get('mod')}+0x{p.get('off')} (class={p.get('cls')})", flush=True)
|
||||
elif t == "reg-call":
|
||||
print(f"[*] RegisterNatives called count={p.get('count')} cls={p.get('cls')}", flush=True)
|
||||
elif t == "rn-addr":
|
||||
print(f"[*] RegisterNatives addr 0x{p['rn']} mod={p.get('mod')}", flush=True)
|
||||
elif t == "fns-scan":
|
||||
result["fns_scan"].append(p)
|
||||
print("[*] JNINativeInterface 槽位 200-230:", flush=True)
|
||||
for e in p["dump"]:
|
||||
print(f" [{e['i']}] {e.get('mod')}+0x{e.get('off')}", flush=True)
|
||||
elif t in ("dlopen-hooked","jni-onload-hooked","rn-hooked","dlopen-hydev","jni-onload-enter"):
|
||||
print(f"[*] {t}", p if t=="dlopen-hydev" else "", flush=True)
|
||||
elif t in ("jni-err","rn-err"):
|
||||
print(f"[!] {t}: {p.get('e')}", flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print("[*] resumed, waiting RegisterNatives...", flush=True)
|
||||
time.sleep(22)
|
||||
result["detached"] = det
|
||||
OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8")
|
||||
print(f"[*] 注册方法数: {len(result['regs'])} saved {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""真机 v8: 用 Java 层直接调用 com.huya.security.hydeviceid.NativeEntry 采集 hdid 族.
|
||||
|
||||
拿到 libhydeviceid 注册表后, 用 frida Java.use 调 NativeEntry 的 native 方法:
|
||||
init() -> getHDID()/getSDID()/getCDID()/getGUID()/getMID()
|
||||
直接可靠读取每台真机的 hdid(32hex) 及同族设备 ID —— 这是多机采集 hdid 池的前提.
|
||||
同时 hook getHDID 的 native 入口(0x20f0c8)记录调用栈确认来源.
|
||||
|
||||
用法:
|
||||
/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0/.venv/bin/python \
|
||||
scripts/phone_read_hdid.py [serial] [remote]
|
||||
输出:
|
||||
evidence/diag_phone/hdid_read.json
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
|
||||
SERIAL = sys.argv[1] if len(sys.argv) > 1 else "5dd8c93f"
|
||||
REMOTE = sys.argv[2] if len(sys.argv) > 2 else "127.0.0.1:31878"
|
||||
PACKAGE = "com.duowan.kiwi"
|
||||
REPO = Path("/Users/yml/codes/douyu_login_py")
|
||||
OUT = REPO / "evidence" / "diag_phone" / "hdid_read.json"
|
||||
RE = Path("/Users/yml/codes/Reverse-Engineering-Agent-Universal-v3.0")
|
||||
ART_CALLSITE = RE / "evidence" / "scripts" / "bypass_msaoaid_maps_art_callsite.js"
|
||||
GETHDID_OFF = 0x20F0C8 # libhydeviceid.so 相对偏移
|
||||
|
||||
JS = r"""
|
||||
'use strict';
|
||||
var NE_CLASS = 'com.huya.security.hydeviceid.NativeEntry';
|
||||
var nativeHooked = false;
|
||||
var GETS = [
|
||||
{n:'getCDID', off:0x20eb08},
|
||||
{n:'getSDID', off:0x20ede8},
|
||||
{n:'getHDID', off:0x20f0c8},
|
||||
{n:'getGUID', off:0x20f8c8},
|
||||
{n:'getMID', off:0x20fba8}
|
||||
];
|
||||
function readJstr(env, ret){
|
||||
try{
|
||||
var fns = env.readPointer(); // JNINativeInterface*
|
||||
var g = new NativeFunction(fns.add(169*8).readPointer(), 'pointer', ['pointer','pointer','pointer']); // GetStringUTFChars
|
||||
var rel = new NativeFunction(fns.add(170*8).readPointer(), 'void', ['pointer','pointer','pointer']); // ReleaseStringUTFChars
|
||||
var cv = g(env, ret, ptr(0));
|
||||
if(cv && !cv.isNull()){ var s=cv.readCString()||''; rel(env, ret, cv); return s; }
|
||||
return '<null>';
|
||||
}catch(e){ return '<err>'; }
|
||||
}
|
||||
function hookAll(){
|
||||
if(nativeHooked) return;
|
||||
var m = Process.findModuleByName('libhydeviceid.so');
|
||||
if(!m) return;
|
||||
nativeHooked = true;
|
||||
GETS.forEach(function(g){
|
||||
try{
|
||||
Interceptor.attach(m.base.add(g.off), {
|
||||
onEnter:function(a){ this.env = a[0]; this.name = g.n; },
|
||||
onLeave:function(ret){ send({type:'getval', name:this.name, str:readJstr(this.env, ret)}); }
|
||||
});
|
||||
}catch(e){}
|
||||
});
|
||||
send({type:'all-hooked', mods:GETS.map(function(g){return g.n;})});
|
||||
}
|
||||
setInterval(hookAll, 300);
|
||||
|
||||
Java.perform(function(){
|
||||
function tryRead(){
|
||||
try{
|
||||
var NE = Java.use('com.huya.security.hydeviceid.NativeEntry');
|
||||
send({type:'class-found', cls:NE_CLASS});
|
||||
// 先 init (可能构造指纹), 再逐个读
|
||||
var out = {};
|
||||
['init','getHDID','getSDID','getCDID','getGUID','getMID'].forEach(function(m){
|
||||
try{ out[m] = NE[m](); }catch(e){ out[m] = '<ERR:'+e+'>'; }
|
||||
});
|
||||
send({type:'read', values:out});
|
||||
}catch(e){ send({type:'java-err', e:String(e)}); }
|
||||
}
|
||||
// 类可能尚未注册(等 libhydeviceid 加载)
|
||||
var n=0;
|
||||
var timer = setInterval(function(){
|
||||
n++;
|
||||
try{
|
||||
Java.use('com.huya.security.hydeviceid.NativeEntry');
|
||||
clearInterval(timer);
|
||||
tryRead();
|
||||
}catch(e){ if(n>60){ clearInterval(timer); send({type:'cls-not-found'}); } }
|
||||
}, 500);
|
||||
});
|
||||
"""
|
||||
|
||||
|
||||
def adb(*a):
|
||||
return subprocess.run(["adb", "-s", SERIAL, *a], capture_output=True, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
adb("shell", "am", "force-stop", PACKAGE)
|
||||
time.sleep(1.5)
|
||||
d = frida.get_device_manager().add_remote_device(REMOTE)
|
||||
pid = d.spawn([PACKAGE])
|
||||
print(f"[*] spawn pid={pid}", flush=True)
|
||||
session = d.attach(pid)
|
||||
det = []
|
||||
session.on("detached", lambda r, dd: det.append({"reason": r, "detail": str(dd)}) or print(f"[*] detached {r} {dd}", flush=True))
|
||||
session.create_script(ART_CALLSITE.read_text()).load()
|
||||
result = {"read": None, "nativetrap": [], "detached": det}
|
||||
def on_message(m, data):
|
||||
if m.get("type") == "error":
|
||||
print("[JS-ERR]", str(m)[:200], flush=True); return
|
||||
p = m.get("payload") or {}
|
||||
t = p.get("type")
|
||||
if t == "read":
|
||||
result["read"] = p["values"]
|
||||
print("[*] NativeEntry 读取:", flush=True)
|
||||
for k, v in p["values"].items():
|
||||
print(f" {k} = {v}", flush=True)
|
||||
elif t == "getval":
|
||||
print(f"[getval] {p.get('name')} = {p.get('str')}", flush=True)
|
||||
# 存起来
|
||||
result.setdefault("getvals", []).append(p)
|
||||
elif t == "all-hooked":
|
||||
print(f"[*] all get methods hooked: {p.get('mods')}", flush=True)
|
||||
elif t in ("class-found","native-hooked"):
|
||||
print(f"[*] {t}", flush=True)
|
||||
elif t == "cls-not-found":
|
||||
print("[!] 类未找到(可能未加载)", flush=True)
|
||||
elif t == "java-err":
|
||||
print(f"[java-err] {p.get('e')}", flush=True)
|
||||
sc = session.create_script(JS)
|
||||
sc.on("message", on_message)
|
||||
sc.load()
|
||||
d.resume(pid)
|
||||
print("[*] resumed, waiting for class + read...", flush=True)
|
||||
time.sleep(35)
|
||||
result["detached"] = det
|
||||
OUT.write_text(json.dumps(result, indent=1, ensure_ascii=False), encoding="utf-8")
|
||||
print(f"[*] saved {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user