chore: 备份 TNG 注册/captcha 逆向与 MariBank SG bypass 进展
TngRootBypassHook 增强 captcha 诊断、TigerTally/JNIC 分层与 HWUI 策略;新增逆向脚本、Frida 工具与 UI dump;同步 MariBank SG hook 与 tng_exit_guard 更新。
This commit is contained in:
167
reverse/scripts/dump_tng_runtime_kill.py
Normal file
167
reverse/scripts/dump_tng_runtime_kill.py
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Race-window: dump TNG executable maps and hunt kill+SVC after launch."""
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
ADB = r"C:\Users\Administrator\AppData\Local\Android\Sdk\platform-tools\adb.exe"
|
||||
PKG = "my.com.tngdigital.ewallet"
|
||||
OUT = Path(__file__).resolve().parents[1] / "dumps" / "tng_runtime"
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# aarch64 movz x8/w8,#129/130/131 + svc #0
|
||||
KILL_IMMS = [
|
||||
bytes.fromhex("281080d2"),
|
||||
bytes.fromhex("28108052"),
|
||||
bytes.fromhex("481080d2"),
|
||||
bytes.fromhex("48108052"),
|
||||
bytes.fromhex("681080d2"),
|
||||
bytes.fromhex("68108052"),
|
||||
]
|
||||
SVC0 = bytes.fromhex("010000d4")
|
||||
EXIT_IMM = bytes.fromhex("c80b80d2") # movz x8,#94 exit_group
|
||||
EXIT2 = bytes.fromhex("ba0b80d2") # movz x8,#93 exit
|
||||
|
||||
|
||||
def adb(*args, check=False):
|
||||
r = subprocess.run([ADB, *args], capture_output=True)
|
||||
out = (r.stdout or b"") + (r.stderr or b"")
|
||||
if check and r.returncode != 0:
|
||||
raise RuntimeError(out.decode("utf-8", "ignore"))
|
||||
return r.returncode, out.decode("utf-8", "replace")
|
||||
|
||||
|
||||
def su(cmd):
|
||||
return adb("shell", f"su -c '{cmd}'")
|
||||
|
||||
|
||||
def main():
|
||||
adb("shell", "am", "force-stop", PKG)
|
||||
time.sleep(0.5)
|
||||
adb("shell", "monkey", "-p", PKG, "-c", "android.intent.category.LAUNCHER", "1")
|
||||
|
||||
pid = None
|
||||
for _ in range(40):
|
||||
time.sleep(0.25)
|
||||
_, out = adb("shell", "pidof", PKG)
|
||||
toks = out.strip().split()
|
||||
if toks:
|
||||
pid = toks[0]
|
||||
break
|
||||
if not pid:
|
||||
print("FAIL: no pid")
|
||||
return 1
|
||||
print(f"pid={pid}")
|
||||
|
||||
# pull maps
|
||||
_, maps = su(f"cat /proc/{pid}/maps")
|
||||
maps_path = OUT / f"maps_{pid}.txt"
|
||||
maps_path.write_text(maps, encoding="utf-8", errors="replace")
|
||||
print(f"maps -> {maps_path} lines={len(maps.splitlines())}")
|
||||
|
||||
targets = []
|
||||
for line in maps.splitlines():
|
||||
if "r-xp" not in line and "r-x" not in line:
|
||||
# also rw-p with execute rarely; keep x
|
||||
if "x" not in line.split()[1] if len(line.split()) > 1 else "":
|
||||
continue
|
||||
if "tngdigital" in line or "libnative" in line or "ewallet" in line.lower():
|
||||
parts = line.split()
|
||||
rng = parts[0]
|
||||
start_s, end_s = rng.split("-")
|
||||
start, end = int(start_s, 16), int(end_s, 16)
|
||||
path = parts[-1] if len(parts) >= 6 else ""
|
||||
targets.append((start, end, path, line))
|
||||
|
||||
print(f"target segments={len(targets)}")
|
||||
for start, end, path, line in targets[:12]:
|
||||
print(f" {hex(start)}-{hex(end)} {path}")
|
||||
|
||||
# dump via dd from /proc/pid/mem
|
||||
remote = f"/data/local/tmp/tng_rt_{pid}.bin"
|
||||
su(f"rm -f {remote}")
|
||||
total = 0
|
||||
for i, (start, end, path, _) in enumerate(targets):
|
||||
size = end - start
|
||||
if size <= 0 or size > 32 * 1024 * 1024:
|
||||
continue
|
||||
# append dump
|
||||
cmd = (
|
||||
f"dd if=/proc/{pid}/mem bs=4096 skip={start // 4096} "
|
||||
f"count={(size + 4095) // 4096} 2>/dev/null >> {remote}"
|
||||
)
|
||||
# dd skip is in blocks from file start — wrong for /proc/pid/mem!
|
||||
# Use busybox dd with seek on output and skip via python on device instead.
|
||||
cmd = (
|
||||
f"toybox dd if=/proc/{pid}/mem of={remote}.p{i} "
|
||||
f"bs=1 skip={start} count={size} 2>/dev/null"
|
||||
)
|
||||
code, _ = su(cmd)
|
||||
if code == 0:
|
||||
total += size
|
||||
print(f" dumped p{i} size={size} from {path}")
|
||||
else:
|
||||
# fallback: python on device
|
||||
py = (
|
||||
f"python3 -c \"import sys;f=open('/proc/{pid}/mem','rb');"
|
||||
f"f.seek({start});d=f.read({size});open('{remote}.p{i}','wb').write(d)\""
|
||||
)
|
||||
code2, out2 = su(py)
|
||||
if code2 == 0:
|
||||
total += size
|
||||
print(f" dumped p{i} via python size={size}")
|
||||
else:
|
||||
print(f" FAIL dump p{i}: {out2[:120]}")
|
||||
|
||||
# pull pieces and scan
|
||||
local_dir = OUT / f"mem_{pid}"
|
||||
local_dir.mkdir(exist_ok=True)
|
||||
kill_hits = 0
|
||||
exit_hits = 0
|
||||
for i, (start, end, path, _) in enumerate(targets):
|
||||
rem = f"{remote}.p{i}"
|
||||
loc = local_dir / f"seg_{i}_{start:x}.bin"
|
||||
code, _ = adb("shell", f"su -c 'test -f {rem} && echo OK'")
|
||||
if "OK" not in _:
|
||||
continue
|
||||
adb("pull", rem, str(loc))
|
||||
if not loc.exists():
|
||||
continue
|
||||
data = loc.read_bytes()
|
||||
for imm in KILL_IMMS:
|
||||
pos = 0
|
||||
while True:
|
||||
j = data.find(imm, pos)
|
||||
if j < 0:
|
||||
break
|
||||
win = data[j : j + 36]
|
||||
if SVC0 in win:
|
||||
kill_hits += 1
|
||||
delta = win.find(SVC0)
|
||||
print(f"KILL+SVC seg{i} file+0x{j:x} va=0x{start+j:x} delta={delta} path={path}")
|
||||
pos = j + 4
|
||||
for imm in (EXIT_IMM, EXIT2):
|
||||
pos = 0
|
||||
while True:
|
||||
j = data.find(imm, pos)
|
||||
if j < 0:
|
||||
break
|
||||
win = data[j : j + 36]
|
||||
if SVC0 in win:
|
||||
exit_hits += 1
|
||||
if exit_hits <= 15:
|
||||
print(f"EXIT+SVC seg{i} file+0x{j:x} va=0x{start+j:x} path={path}")
|
||||
pos = j + 4
|
||||
# also count raw svc
|
||||
print(f" seg{i} svc0={data.count(SVC0)} size={len(data)}")
|
||||
|
||||
print(f"DONE kill+svc={kill_hits} exit+svc={exit_hits} dumped_bytes~={total}")
|
||||
_, alive = adb("shell", "pidof", PKG)
|
||||
print(f"still alive? {alive.strip() or 'NO'}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user