feat(huya): unidbg 金测试打通 - 采集器加密可复现(androidId=PDSuHx..)/GUID=0a7dfa..一致
- 输入图定案: 库内零硬件文件读, 种子=ANDROID_ID+服务端hydeviceid_config+MID, 属性层无关(X1/X2) - 真机全段解密 dump(phone_dump_hydev_full) + merge_decrypted 合入原文件 → unidbg 可直接加载 - unidbg(JDK21+patch) harness: JNI 桩(SharedPreferences日志化/Settings.Secure/NativeBridge) 金测试: androidId 加密态与 getGUID 全部与真机一致 - 遗留: NativeBridge.b(100) 原生归属库未定(非hydeviceid/udb/device-util), 当前桩替顶 - 修正结论: '单机不可铸造'判断错误, GUID=b(100)=f(androidId密, config, MID) 输入全可控
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
"""rebase v4: 直白版 - 原地改写 dump 的重定位槽, 无嵌套函数.
|
||||
用法: python3 rebase_v4.py <original.so> <dump_full.so> <runtime_base> <out.so>
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import struct, sys
|
||||
|
||||
def main():
|
||||
original, dump, base_s, out = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]
|
||||
base = int(base_s, 16)
|
||||
od = open(original, 'rb').read()
|
||||
dd = bytearray(open(dump, 'rb').read())
|
||||
|
||||
# 原文件段表
|
||||
e_shoff = struct.unpack_from('<Q', od, 0x28)[0]
|
||||
e_shentsize = struct.unpack_from('<H', od, 0x3A)[0]
|
||||
e_shnum = struct.unpack_from('<H', od, 0x3C)[0]
|
||||
e_shstrndx = struct.unpack_from('<H', od, 0x3E)[0]
|
||||
shdrs = []
|
||||
for i in range(e_shnum):
|
||||
sh = struct.unpack_from('<IIQQQQIIQQ', od, e_shoff + i * e_shentsize)
|
||||
shdrs.append([sh[0], sh[3], sh[4], sh[5]]) # name, vaddr, offset, size
|
||||
shstr = shdrs[e_shstrndx]
|
||||
shstrtab = od[shstr[2]:shstr[2] + shstr[3]]
|
||||
def nm(idx):
|
||||
e = shstrtab.find(b'\0', idx)
|
||||
return shstrtab[idx:e].decode(errors='replace')
|
||||
|
||||
reb, zer, low, oob = 0, 0, 0, 0
|
||||
for s in shdrs:
|
||||
n = nm(s[0])
|
||||
if n not in ('.rela.dyn', '.rela.plt', '.init_array', '.fini_array'):
|
||||
continue
|
||||
if n in ('.rela.dyn', '.rela.plt'):
|
||||
for i in range(0, s[3], 24):
|
||||
r_off, r_info, _ = struct.unpack_from('<QQq', od, s[2] + i)
|
||||
rt = r_info & 0xffffffff
|
||||
if r_off + 8 > len(dd):
|
||||
oob += 1
|
||||
continue
|
||||
v = struct.unpack_from('<Q', dd, r_off)[0]
|
||||
if rt == 1027 or rt == 257: # RELATIVE / ABS64
|
||||
if v == 0 or v < 0x4000000000:
|
||||
low += 1
|
||||
continue
|
||||
if base <= v < base + 0x600000:
|
||||
struct.pack_into('<Q', dd, r_off, v - base)
|
||||
reb += 1
|
||||
else:
|
||||
struct.pack_into('<Q', dd, r_off, 0)
|
||||
zer += 1
|
||||
elif rt == 1025 or rt == 1026: # GLOB_DAT / JUMP_SLOT
|
||||
struct.pack_into('<Q', dd, r_off, 0)
|
||||
zer += 1
|
||||
else: # init/fini arrays
|
||||
for i in range(0, s[3], 8):
|
||||
off = s[1] + i # vaddr 轴
|
||||
if off + 8 > len(dd):
|
||||
oob += 1
|
||||
continue
|
||||
v = struct.unpack_from('<Q', dd, off)[0]
|
||||
if v == 0 or v < 0x4000000000:
|
||||
low += 1
|
||||
continue
|
||||
if base <= v < base + 0x600000:
|
||||
struct.pack_into('<Q', dd, off, v - base)
|
||||
reb += 1
|
||||
else:
|
||||
struct.pack_into('<Q', dd, off, 0)
|
||||
zer += 1
|
||||
|
||||
# 段名表修复: dump 的 shstrtab 为空, 复制原文件段头表 + shstrtab (布局一致)
|
||||
import struct as _s
|
||||
e_shoff = _s.unpack_from('<Q', od, 0x28)[0]
|
||||
e_shentsize = _s.unpack_from('<H', od, 0x3A)[0]
|
||||
e_shnum = _s.unpack_from('<H', od, 0x3C)[0]
|
||||
e_shstrndx = _s.unpack_from('<H', od, 0x3E)[0]
|
||||
# 原文件 shstrtab 段内容
|
||||
sh = _s.unpack_from('<IIQQQQIIQQ', od, e_shoff + e_shstrndx * e_shentsize)
|
||||
shstr_off, shstr_size = sh[4], sh[5]
|
||||
if e_shoff + e_shnum * e_shentsize <= len(dd) and shstr_off + shstr_size <= len(dd):
|
||||
dd[e_shoff:e_shoff + e_shnum * e_shentsize] = od[e_shoff:e_shoff + e_shnum * e_shentsize]
|
||||
dd[shstr_off:shstr_off + shstr_size] = od[shstr_off:shstr_off + shstr_size]
|
||||
# 重定位文件为全 vaddr 轴(offset==vaddr): 逐段修正 sh_offset = sh_addr
|
||||
for si in range(e_shnum):
|
||||
basee = e_shoff + si * e_shentsize
|
||||
sh = list(_s.unpack_from('<IIQQQQIIQQ', dd, basee))
|
||||
sh[4] = sh[3] # offset := addr
|
||||
_s.pack_into('<IIQQQQIIQQ', dd, basee, *sh)
|
||||
print(f"[*] section header table + shstrtab copied & offset:=addr (shoff={e_shoff:#x})")
|
||||
else:
|
||||
print("[!] WARN: section table copy skipped (out of bounds)")
|
||||
|
||||
open(out, 'wb').write(dd)
|
||||
print(f"[*] rebase={reb} zero={zer} low={low} oob={oob}")
|
||||
# 验证
|
||||
import subprocess
|
||||
v1 = struct.unpack_from('<Q', dd, 0x3b21c0)[0]
|
||||
v2 = struct.unpack_from('<Q', dd, 0x3b9b68)[0] if 0x3b9b68 + 8 <= len(dd) else -1
|
||||
print(f"[*] check init_array[0]={v1:#x} (.got@0x3b9b68)={v2:#x}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user