From a Game Value to the Code That Changes It
Follow a known gold address backward to the exact instruction that changes it.
Cheatsheet for Lesson 2.5
A one-page summary of “From a Game Value to the Code That Changes It” you can keep beside your notes. Plain Markdown.
Lesson 2.5 — From a Game Value to the Code That Changes It: cheatsheet
- Remember: Watching a data address finds every writer of a value. A code breakpoint tells you when one particular instruction runs.
- Also useful: A register name is a width clue. Treat an 8-bit, 16-bit, and 32-bit write as different state changes even when they belong to one register family.
Key terms
al:eax,ax,ah, andalare views of the same register family, not four independent containers.eax: In the course's 32-bit target,eaxis the full-width view.esi: For example, ifesiholds0x0A3C1200, the destination is0x0A3C1200 + 0x30 = 0x0A3C1230.
Formulas and patterns
address = register value + offset;old = 32-bit value read from address;new = old - low 32 bits of eax;store new at address;update arithmetic flags
Review
- Q: Why use a hardware write breakpoint on the gold address? A: It can reveal any instruction that writes the watched bytes
- Q: Your match shows 100 gold and the recruited unit costs 15. What should you expect at the paused subtraction? A: The destination is the gold address and the price register holds 15
Find the instruction that changes gold
Section titled “Find the instruction that changes gold”In the previous memory lab, we found the current gold. Now we want to know:
Which instruction subtracts the price of a recruited unit?
Use the same offline Wesnoth 1.14.9 target and restore your clean VM snapshot before starting. For a concrete run, suppose you start at 100 gold and recruit a unit costing 15. The expected result is 85. Use the actual price and gold shown in your match when checking the paused instruction.
Attach x64dbg
Section titled “Attach x64dbg”Launch Wesnoth, begin a local match, and find the gold address again. Then attach x64dbg to the running process.


The CPU view contains four useful areas:
- disassembly—the instructions near the current position;
- registers—the CPU’s current scratch values;
- dump—raw memory;
- stack—return addresses and temporary data.

Attaching may pause in Windows system code. That is normal. Resume the process before triggering your experiment.
Go to the gold address
Section titled “Go to the gold address”Open the dump view’s “go to expression” command and enter the address found by the memory scanner.


The bytes in the dump are little-endian. If the gold is 100, the 32-bit value 0x00000064 appears as:
64 00 00 00The least-significant byte comes first.
Break on writes
Section titled “Break on writes”Set a hardware breakpoint on write, covering four bytes at the gold address. The lesson uses x64dbg, but any debugger that can watch an address will do:
x64dbg
Select the four bytes in the dump, right-click, and choose Breakpoint, then Hardware, Write, then Dword.
WinDbg
Type ba w4 followed by the address, for example ba w4 0A3C1230, to break on a
four-byte write. Then type g to let the game run.
Cheat Engine
Right-click the gold entry in the address list and choose Find out what writes to this address. Cheat Engine lists each instruction that writes it, with a count.
A hardware memory breakpoint does not replace the game’s instruction bytes. The debugger programs special CPU debug registers with an address, access type, and width. When an instruction writes across that watched range, the CPU raises a debug exception and Windows reports it to the debugger.
The width matters. Gold is believed to be a four-byte value, so watch all four bytes. Watching one byte can still trigger, but it hides whether the game wrote the complete integer or only part of it.
This breakpoint watches a data address, not one fixed instruction. If three different functions write gold, the same watch can reveal all three. A software breakpoint placed on a code address answers the opposite question: “When does this exact instruction execute?”

Resume the game and recruit one unit. The debugger should pause close to an instruction that resembles:
sub dword ptr [reg+offset], eaxDo not copy the exact register or offset from this page. Read the instruction in your own debugger. A hardware data breakpoint can report an access after the instruction has run, so the highlighted next instruction may not be the one that wrote gold. Use the breakpoint’s access information and nearby disassembly to identify the triggering write, then confirm that its calculated destination is your gold address. Builds and runs can differ.

Ask the five assembly questions
Section titled “Ask the five assembly questions”Apply the five questions to the paused instruction:
- What is read? The current gold and the price in a register.
- What is written? Four bytes at the gold field.
- How many bytes?
dword ptrmeans 32 bits. - Signed or unsigned? Test nearby comparisons before deciding.
- Where next? The following instruction unless a branch changes the path.
Now compare the price register with the unit’s price. In the example
run, it should be 15 and the destination should be the gold address
that changed from 100 to 85. Matching both is stronger evidence than
recognizing sub alone.
Read an instruction as state transition
Section titled “Read an instruction as state transition”For sub dword ptr [reg+offset], eax, the complete before-and-after effect is:
address = register value + offsetold = 32-bit value read from addressnew = old - low 32 bits of eaxstore new at addressupdate arithmetic flagsThat last line matters. Later branches may inspect zero, sign, carry, or overflow flags. Replacing the subtraction changes both the stored gold and those flags. Before a temporary patch, step forward and check whether nearby conditional jumps consume them.
The effective address calculation does not dereference reg and then add an offset to the loaded value. It adds the offset to the register first, then reads memory at the resulting address. Keep “calculate an address” separate from “read the value at that address.”
Register names can select overlapping bits
Section titled “Register names can select overlapping bits”eax, ax, ah, and al are views of the same register family, not four
independent containers. eax selects 32 bits, ax its low 16, ah bits 15–8,
and al bits 7–0. In the course’s 32-bit target, eax is the full-width view.
The diagram also shows the wider rax available in 64-bit mode.
Byte number, 7 on the left (most significant) to 0 on the right (least significant)
Before: RAX = 0x1122334455667788
- 11
- 22
- 33
- 44
- 55
- 66
- 77
- 88
After mov eax, 0xAABBCCDD: RAX = 0x00000000AABBCCDD
- 00zero
- 00zero
- 00zero
- 00zero
- AAset
- BBset
- CCset
- DDset
- set written by the instruction
- zero cleared by the 32-bit rule
- same left alone
mov eax, 0xAABBCCDD changes RAX from 0x1122334455667788 to 0x00000000AABBCCDD. Bytes 3 to 0 were written. Bytes 7 to 4 were cleared by the zero-extension rule: 11 22 33 44 became 00 00 00 00.
- RAX (bits 63 to 0, 8 bytes)
- Replaces all eight bytes.
- EAX (bits 31 to 0, 4 bytes)
- Replaces bytes 3 to 0 and clears bytes 7 to 4 to zero. This is a real x86-64 rule: a 32-bit write always zero-extends into the full register.
- AX (bits 15 to 0, 2 bytes)
- Replaces bytes 1 and 0. Bytes 7 to 2 keep their old values.
- AH (bits 15 to 8, 1 byte)
- Replaces byte 1 only. Every other byte keeps its old value.
- AL (bits 7 to 0, 1 byte)
- Replaces byte 0 only. Every other byte keeps its old value.
The same starting value, five different writes
Start with RAX = 0x1122334455667788, whose bytes from byte 7 down to byte 0 are 11 22 33 44 55 66 77 88. Each row below starts again from that value.
| Instruction | RAX afterwards | What happened to the bytes |
|---|---|---|
mov al, 0xEE | 0x11223344556677EE | Byte 0 was written. Bytes 7 to 1 were not touched. |
mov ah, 0xEE | 0x112233445566EE88 | Byte 1 was written. Bytes 7 to 2 and byte 0 were not touched. |
mov ax, 0xEEFF | 0x112233445566EEFF | Bytes 1 to 0 were written. Bytes 7 to 2 were not touched. |
mov eax, 0xAABBCCDD | 0x00000000AABBCCDD | Bytes 3 to 0 were written. Bytes 7 to 4 were cleared by the zero-extension rule: 11 22 33 44 became 00 00 00 00. |
mov rax, 0x8899AABBCCDDEEFF | 0x8899AABBCCDDEEFF | Bytes 7 to 0 were written. |
The AH and AL names exist because the 8086 had 8-bit halves; the 32-bit and 64-bit names were added later as the register grew. The zero-extension rule for EAX applies in 64-bit mode. In 32-bit mode (this course's target programs) EAX is the whole register and there is no RAX. Microsoft documents the x64 rules in its x64 architecture reference.
In 64-bit mode, a write to eax also clears rax bits 63–32. Writes to
ax, ah, or al preserve bits outside the selected part. Microsoft’s
x64 register reference
documents this distinction. For the gold instruction, the operand width
still tells us that exactly four memory bytes are changed.
Read the brackets as an address calculation
Section titled “Read the brackets as an address calculation”For an instruction such as:
sub dword ptr [esi+30h], eaxread it from the inside outward:
- take the address currently stored in
esi; - add hexadecimal offset
0x30; - treat the four bytes at that final address as the destination;
- subtract the lower 32 bits of
eaxfrom that destination; - store the result back to the same four bytes;
- update CPU flags for later branches.
The bracketed expression accesses memory. Without brackets, esi+30h would be
an address calculation rather than a read or write of the bytes at that
address.
Read the live value of esi in the register panel and add 0x30 yourself. For
example, if esi holds 0x0A3C1200, the destination is
0x0A3C1200 + 0x30 = 0x0A3C1230. If the sum equals the candidate gold address,
this instruction writes the gold you found.
One instruction, step by step: sub dword ptr [esi+30h], eax. This step-by-step trace needs JavaScript. Here is the code and where the run ends.
; sub dword ptr [esi+30h], eax
address = esi + 0x30
old = read_u32(address)
new = old - eax ; low 32 bits
write_u32(address, new)
flags = ZF, SF, CF, OF from this resultThe flags are set from the result: ZF (zero) 0, SF (top bit) 0, CF (borrow) 0, OF (signed overflow) 0. Later conditional jumps read these flags, not the stored gold, so a patch that removes this instruction removes them too.
Final values: esi = 0x0A3C1200; eax = 15; address = 0x0A3C1230; old = 100; new = 85; flags = ZF=0 SF=0 CF=0 OF=0.
Read every step of the lesson’s example
Step 1, line 1. Before it runs: esi holds 0x0A3C1200 and eax holds the price, 15. The four bytes at 0x0A3C1230 hold the gold, 100, stored lowest byte first as 64 00 00 00.
esi = 0x0A3C1200; eax = 15.
Memory: …1230 = 64; …1231 = 00; …1232 = 00; …1233 = 00.
Step 2, line 2. Brackets mean an address calculation first: 0x0A3C1200 + 0x30 = 0x0A3C1230. Nothing is read yet.
esi = 0x0A3C1200; eax = 15; address = 0x0A3C1230.
Memory: …1230 = 64; …1231 = 00; …1232 = 00; …1233 = 00.
Step 3, line 3. Now read the four bytes at 0x0A3C1230 as one number: 0x64 = 100.
esi = 0x0A3C1200; eax = 15; address = 0x0A3C1230; old = 100.
Memory: …1230 = 64; …1231 = 00; …1232 = 00; …1233 = 00.
Step 4, line 4. Subtract the price: 100 - 15 = 85. The result is held in the CPU and has not touched memory yet.
esi = 0x0A3C1200; eax = 15; address = 0x0A3C1230; old = 100; new = 85.
Memory: …1230 = 64; …1231 = 00; …1232 = 00; …1233 = 00.
Step 5, line 5. Store the result back to the same four bytes. They now read 55 00 00 00.
esi = 0x0A3C1200; eax = 15; address = 0x0A3C1230; old = 100; new = 85.
Memory: …1230 = 55; …1231 = 00; …1232 = 00; …1233 = 00.
Step 6, line 6. The flags are set from the result: ZF (zero) 0, SF (top bit) 0, CF (borrow) 0, OF (signed overflow) 0. Later conditional jumps read these flags, not the stored gold, so a patch that removes this instruction removes them too.
esi = 0x0A3C1200; eax = 15; address = 0x0A3C1230; old = 100; new = 85; flags = ZF=0 SF=0 CF=0 OF=0.
Memory: …1230 = 55; …1231 = 00; …1232 = 00; …1233 = 00.
Test with a reversible patch
Section titled “Test with a reversible patch”Copy the original instruction bytes first, so you can write them back. Then temporarily replace the entire subtraction instruction with the same number of nop bytes.

Resume and recruit another unit. If gold no longer decreases, this instruction is the one that subtracts the price. Restore the original bytes immediately afterward.

Turn one discovery into a repeatable patch
Section titled “Turn one discovery into a repeatable patch”Our tools will not hardcode “whatever address worked once.” They will:
- open the controlled process;
- locate a module or scan a known pattern;
- confirm the expected original bytes;
- apply a temporary patch;
- restore the bytes on exit.
Checking the original bytes is crucial. Refusing to patch an unknown version is safer than writing to the wrong place.
Clean up
Section titled “Clean up”Remove the breakpoint through x64dbg’s Breakpoints tab and confirm the original instruction is restored.

The scan gave us a candidate value; the write breakpoint gave us a specific instruction and the register values from one recruitment. The reversible patch checked its effect, but the instruction may also run for other gold-changing actions. Lesson 2.6 follows the call path to identify which event, caller, and affordability check selected this gold write.
Watch the data, not one function
Why use a hardware write breakpoint on the gold address?
A data breakpoint watches an address range. Different code paths can trigger it, which makes it useful when you know the changing data before you know the responsible instruction.
Finished Lesson 2.5?
Mark it done and the lesson list shows a beside it. A chapter whose lessons are all done is marked done too. Progress is saved in this browser only.
Plain Markdown, saved in this browser.
This book grew out of the original Game Hacking Academy by attilathedud and has been rewritten as a beginner-first course. Like the original repository, its source is published under the Apache License 2.0.