#!/usr/bin/env python3 """Static verification of patch v2 (capstone). Checks: 1. callback registration at 0x4efe28/2c now points to cave 0x41b8b0 2. cave disassembles without obvious corruption; entry guard present 3. key globals/strings land in the expected .data zero region 4. diff vs stock confined to the cave + registration bytes """ import sys, struct from capstone import Cs, CS_ARCH_MIPS, CS_MODE_MIPS32, CS_MODE_LITTLE_ENDIAN STOCK = "hiby_player_stock" PATCHED = "hiby_player_patched_v2" def rd(fn, off, n): with open(fn, "rb") as f: f.seek(off) return f.read(n) def disasm(data, vaddr): md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_LITTLE_ENDIAN) return list(md.disasm(data, vaddr)) ok = True def check(cond, msg): global ok print(("PASS " if cond else "FAIL ") + msg) if not cond: ok = False # 1. callback registration pat = rd(PATCHED, 0x4EFE28 - 0x400000, 8) lui = struct.unpack_from("= 4, f"syscall instructions present ({len(syscalls)})") # find addiu $v0,$zero,0xN patterns for fork/execve/waitpid (capstone prints hex) def find_li_v0(val): s = hex(val) for i in range(len(insns) - 1): a, b = insns[i], insns[i + 1] if a.mnemonic == "addiu" and "$v0" in a.op_str and s in a.op_str: return True return False check(find_li_v0(4002), "fork syscall number 4002 (0xfa2) present") check(find_li_v0(4011), "execve syscall number 4011 (0xfab) present") check(find_li_v0(4007), "waitpid syscall number 4007 (0xfa7) present") # key control-flow references def has_call(target): for i in insns: if i.mnemonic == "jal" and hex(target) in i.op_str: return True return False check(has_call(0x49BE00), "calls 0x49be00 (create view)") check(has_call(0x46C800), "calls 0x46c800 (activate view)") check(has_call(0x46C2C0), "calls 0x46c2c0 (set callback)") check(has_call(0x8E6C00), "calls opendir (0x8e6c00)") check(has_call(0x8E6240), "calls access (0x8e6240)") # v2.2: watchdog integration import re as _re def find_li_v0x(val): s = hex(val) for i in range(len(insns) - 1): a, b = insns[i], insns[i + 1] if a.mnemonic == "addiu" and "$v0" in a.op_str and s in a.op_str: return True return False check(find_li_v0x(4037), "kill syscall 4037 present (watchdog cleanup)") check(find_li_v0x(4002) and find_li_v0x(4011), "double fork (app+watchdog) syscalls present") wds = rd(PATCHED, 0x41C8B0 - 0x400000, 400) check(b"/usr/data/meow_watchdog\x00" in wds, "watchdog path string present") # itoa: divu + mflo/mfhi present divu = [i for i in insns if i.mnemonic == "divu"] mflo = [i for i in insns if i.mnemonic == "mflo"] check(len(divu) >= 1 and len(mflo) >= 1, f"itoa divu/mflo present ({len(divu)}/{len(mflo)})") check(has_call(0x8E65B0), "calls readdir (0x8e65b0)") # 3. string area s = rd(PATCHED, 0x41C8B0 - 0x400000, 300) # STR_BASE = CAVE+0x1000 = 0x41c8b0 check(b"hiby_apps_main\x00" in s, "string 'hiby_apps_main' present") check(b"/usr/data/mnt/sd_0/MeowAPPs\x00" in s, "string MeowAPPs dir present") check(b"/proc/mounts\x00" in s, "string /proc/mounts present") check("未检测到存储卡".encode() in s, "utf8 no-card hint present") check("无可用应用".encode() in s, "utf8 no-app hint present") check(b'"name":\x00' in s and b'"exec":\x00' in s, "json key strings present") # 4. diff vs stock st = open(STOCK, "rb").read() pt = open(PATCHED, "rb").read() assert len(st) == len(pt) diffs = [i for i in range(len(st)) if st[i] != pt[i]] regions = [] for d in diffs: if regions and d - regions[-1][1] <= 4: regions[-1][1] = d else: regions.append([d, d]) print(f"\ndiff bytes vs stock: {len(diffs)}, regions:") for r in regions: print(f" file {r[0]:#x}-{r[1]:#x} ({r[1]-r[0]+1} B)") # must be exactly 2 regions: registration (0xE28/0xE2c) + cave (0x1B8B0..) # cave code + strings extend from CAVE to CAVE+0x1000+~0x120 (strings) CAVE_END = CAVE + 0x1000 + 0x160 ok_regions = all( (0x4EFE28 - 0x400000 <= r[0] <= 0x4EFE30 - 0x400000) or (0x47B820 - 0x400000 <= r[0] <= 0x47B824 - 0x400000) or (0x436FC0 - 0x400000 <= r[0] <= 0x436FC4 - 0x400000) or (CAVE - 0x400000 <= r[0] and r[1] <= CAVE_END - 0x400000) for r in regions ) check(ok_regions, "diff confined to registration + cave region") print("\nRESULT:", "OK" if ok else "FAILED") sys.exit(0 if ok else 1)