"""Hardware-only trace of the engine-local overwrite wrapper entry. Breaks before the prologue of FUN_147ce47e0, where [rsp] is the exact direct caller return address and R8D is the team ID later written to the final match record. This closes the one frame Wine PE unwinding could not recover. No INT3/software breakpoints. No client memory writes. """ from __future__ import annotations import json import os import struct import time import traceback import gdb WRAPPER_VA = 0x147CE47E0 _STATE = None def _reg(name: str) -> int: return int(gdb.parse_and_eval(f"${name}")) def _read(address: int, size: int) -> bytes | None: if not address or address < 0: return None try: return bytes(gdb.selected_inferior().read_memory(address, size)) except gdb.error: return None def _u64(address: int) -> int | None: data = _read(address, 8) return struct.unpack(" dict: thread = gdb.selected_thread() if thread is None: return {} return {"name": thread.name, "ptid": list(thread.ptid), "global_num": thread.global_num} def _registers() -> dict: names = ( "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "rsp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "rip", ) return {name: _reg(name) for name in names} class State: def __init__(self, path: str): self.path = path self.index = 0 def log(self, kind: str, **payload): self.index += 1 thread = _thread() event = { "event": kind, "event_index": self.index, "time_unix": time.time(), "thread": thread, **payload, } with open(self.path, "a", encoding="utf-8") as handle: handle.write(json.dumps(event, sort_keys=True) + "\n") handle.flush() os.fsync(handle.fileno()) class WrapperBreakpoint(gdb.Breakpoint): def __init__(self, state: State): self.state = state super().__init__( f"*0x{WRAPPER_VA:x}", type=gdb.BP_HARDWARE_BREAKPOINT, internal=False ) self.silent = True def stop(self): try: stack = _reg("rsp") caller_return = _u64(stack) self.state.log( "engine_overwrite_wrapper_entry", wrapper_va=WRAPPER_VA, caller_return_address=caller_return, source_team_id=_reg("r8") & 0xFFFFFFFF, side_argument=_reg("rdx") & 0xFFFFFFFF, registers=_registers(), caller_disassembly=( gdb.execute(f"x/12i 0x{caller_return - 32:x}", to_string=True) if caller_return else None ), backtrace=gdb.execute("bt 32", to_string=True), ) except Exception as exc: self.state.log( "trace_error", where="engine_overwrite_wrapper", error=str(exc), traceback=traceback.format_exc() ) return False def _on_exit(event): if _STATE is not None: _STATE.log("inferior_exited", detail=str(event)) def start_trace(log_path: str, _cards_base: int): global _STATE open(log_path, "w", encoding="utf-8").close() _STATE = State(log_path) breakpoint = WrapperBreakpoint(_STATE) gdb.events.exited.connect(_on_exit) _STATE.log( "trace_armed", breakpoints={"engine_overwrite_wrapper": {"number": breakpoint.number, "va": WRAPPER_VA}}, hardware_only=True, client_memory_writes=False, )