re: auto-label all opcode dispatch handlers from FUN_00413860
Extract op->real-handler map (handler(op)=ctx[0x26c93+op]) from the registration routine's override stores; ghidra_handler_map.py + build/op-handler-map.json (420 overrides). Cross-check vs opcodes.toml found 0 real drift. One-shot Ghidra pass then labeled the /v2 image: 281 raw FUN_/LAB_ handlers -> op_0xNN_handler, 107 bare VAs -> functions, 31 hand-named preserved, opcode plate comment on every handler. Includes the Task A spec + plan and the two-program (/v2 vs SMM) gotcha. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
61
tools/test_ghidra_handler_map.py
Normal file
61
tools/test_ghidra_handler_map.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""Unit tests for the FUN_00413860 dispatch-table parser (tools/ghidra_handler_map.py).
|
||||
|
||||
Run: py -3.11 -X utf8 tools/test_ghidra_handler_map.py (plain runner, no pytest dependency).
|
||||
"""
|
||||
import sys
|
||||
from ghidra_handler_map import parse_overrides
|
||||
|
||||
FAILS = []
|
||||
|
||||
|
||||
def check(cond, msg):
|
||||
if not cond:
|
||||
FAILS.append(msg)
|
||||
print("FAIL:", msg)
|
||||
else:
|
||||
print("ok:", msg)
|
||||
|
||||
|
||||
def test_parses_known_override_anchors():
|
||||
# Lines in the exact shape captured from disassemble_function(0x413860).
|
||||
# disp = 0x9b24c + op*4 => 0x9b8fc = 0x9b24c + 0x1ac*4 (op 0x1ac), 0x9b56c = op 0xc8.
|
||||
lines = [
|
||||
"0041476a: MOV dword ptr [ESI + 0x9b8fc],0x427fb0", # op 0x1ac
|
||||
"004148e6: MOV dword ptr [ESI + 0x9b56c],0x420ec0", # op 0xc8
|
||||
"00414738: MOV dword ptr [ESI + 0x9b8d4],0x42d360", # op 0x1a2
|
||||
"00414d1e: MOV dword ptr [ESI + 0x9baa0],0x42a0b0", # op 0x215
|
||||
]
|
||||
m = parse_overrides(lines)
|
||||
check(m.get(0x1ac) == 0x427fb0, "anchor op 0x1ac -> 0x427fb0")
|
||||
check(m.get(0xc8) == 0x420ec0, "anchor op 0xc8 -> 0x420ec0")
|
||||
check(m.get(0x1a2) == 0x42d360, "anchor op 0x1a2 -> 0x42d360")
|
||||
check(m.get(0x215) == 0x42a0b0, "anchor op 0x215 -> 0x42a0b0")
|
||||
|
||||
|
||||
def test_excludes_default_handler_and_out_of_window():
|
||||
lines = [
|
||||
"MOV EAX,0x4162b0", # STOSD seed, not a store -> ignored
|
||||
"MOV dword ptr [ESI + 0x9b24c],0x4162b0", # op 0x0 default handler -> excluded
|
||||
"MOV dword ptr [ESI + 0x5f2e8],0x570410", # disp below table window -> ignored
|
||||
"MOV dword ptr [ESI + 0x992c0],EAX", # register source, no immediate -> ignored
|
||||
"MOV dword ptr [ESI],0x570fec", # no displacement -> ignored
|
||||
]
|
||||
check(parse_overrides(lines) == {}, "default handler + out-of-window + non-immediate all excluded")
|
||||
|
||||
|
||||
def test_ignores_misaligned_displacement():
|
||||
# A store into the table window but not on a 4-byte boundary is not a real slot.
|
||||
lines = ["MOV dword ptr [ESI + 0x9b24e],0x401234"] # 0x9b24e-0x9b24c = 2, not %4
|
||||
check(parse_overrides(lines) == {}, "misaligned displacement in window excluded")
|
||||
|
||||
|
||||
def main():
|
||||
test_parses_known_override_anchors()
|
||||
test_excludes_default_handler_and_out_of_window()
|
||||
test_ignores_misaligned_displacement()
|
||||
print("FAILURES:", len(FAILS))
|
||||
return 1 if FAILS else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user