Find what code reads or writes to a specific memory address using hardware breakpoints
Find what code reads or writes to a specific memory address.
address (required): Memory address to monitor (hex)access (optional): "w" for writes only (default), "rw" for reads and writessize (optional): Watch size in bytes: 1, 2, 4, or 8 (default: 4)Set a hardware watchpoint:
probe_hwbp_set address=<addr> access=<w|rw> size=<size>
You get back a breakpoint ID. Hardware breakpoints use debug registers DR0-DR3 (4 slots max).
Wait for the target action -- Tell the user what to do in the application to trigger the write. For example: "Take damage in-game" or "Click the button" or "Send a message".
Read the breakpoint log:
probe_breakpoint_log id=<bp_id>
Each hit gives you a full register snapshot: RIP (the instruction that accessed the address), RAX-R15, RSP, RFLAGS.
Disassemble the writer:
probe_disassemble address=<rip> count=10
Look at the instruction at RIP — it's the exact instruction that wrote (or read) the address.
Get the full function:
probe_disassemble_function address=<function_start>
Walk backwards from RIP to find the function prologue (look for push rbp / sub rsp / push rbx patterns). Or subtract 0x40-0x100 from RIP and disassemble — the prologue is usually within that range.
Identify the caller:
probe_read_pointer address=<rsp_value> reads the return addressGenerate a signature for the function so it can be found again:
probe_generate_signature address=<function_start>
Clean up:
probe_hwbp_remove id=<bp_id>
Always remove hardware breakpoints when done — there are only 4 slots.
probe_watch address=<addr> size=4 interval_ms=100 to catch when the value changes, then narrow down the timing.Direct field write:
mov [rcx+0x354], eax ; rcx = object pointer, 0x354 = field offset, eax = new value
This tells you: the function receives the object in RCX, and offset 0x354 is the field.
Indirect write via pointer chain:
mov rax, [rcx+0x30] ; load sub-object pointer
mov [rax+0x10], edx ; write to sub-object field
This tells you: the struct has a pointer at +0x30 to a sub-struct, and the actual field is at +0x10 in the sub-struct.
Atomic/interlocked write:
lock xchg [rcx+0x40], eax ; thread-safe write
This means the field is accessed from multiple threads — important to know for reading it safely.