Skip to content
Lesson 1.9Computer, Game, and Code Foundations

What a Memory Scan Really Finds

Find, test, and change one gold value in Wesnoth, and see what each scan does and does not prove.

Lesson 1.9
Read or listenPrint chapter or book
About 18 minutes

Keyboard shortcuts

?
Open this guide
Alt + B
Show or hide the lesson list
Alt + O
Show or fold the lesson outline
Alt + N
Open or close notes
Alt + H
Toggle semantic colours
Alt + D
Toggle page brightness
Alt + C
Toggle code brightness
← →
Move through a focused code trace
Home End
First or final step in a focused trace
Escape
Close this guide or an open panel

Reader shortcuts pause while you type in a text field.

Cheatsheet for Lesson 1.9Key ideas, reference and questions

A one-page summary of “What a Memory Scan Really Finds” you can keep beside your notes. Plain Markdown.

Lesson 1.9 — What a Memory Scan Really Finds: cheatsheet

  • Remember: Each Next Scan only re-checks the old candidates and keeps the ones that now match, so the list shrinks every time.
  • Also useful: Think of a guessing game with yes or no questions: each new gold value removes addresses that could not be the answer.

Reading map

  • Prepare the target
  • Perform the first value scan
  • Change the game once
  • Test one candidate reversibly
  • Why several copies may all be related
  • What the scan established

Formulas and patterns

  • new_candidates = old_candidates ∩ addresses_currently_equal_to_85

Review

  • Q: A first scan finds many addresses containing 100. You recruit a unit and the visible gold changes to 85. Which addresses should the next scan inspect? A: Only the first scan's candidates, keeping those now holding 85
  • Q: What does attaching a memory scanner to a running process accomplish? A: It selects which process's memory the scanner reads
Download cheatsheet (.md)

An address names a location, while a type tells a program what its bytes mean. We will use a memory scanner on Wesnoth: observe a gold value, search for matching bytes, make one in-game change, and filter the results.

The goal is not merely to reach a final address. The goal is to understand why each scan removes some candidates and why one test still does not prove that a candidate is the main game-play value.

Use the course build of Wesnoth 1.14.9 in the Windows VM. Start a local match and pause before recruiting a unit.

A Wesnoth match showing the gold value

The paused Wesnoth match. The gold number on screen is what the scan will look for.

Suppose the screen shows 100 gold and the unit you plan to recruit costs 15. After recruiting, the visible value should become 100 − 15 = 85.

Open Cheat Engine, choose the process button, and select the running Wesnoth process.

Choosing the Wesnoth process

Cheat Engine's process list, with the running Wesnoth chosen.

Attaching tells Cheat Engine which process address space to read. It does not yet tell the scanner where gold is or how it is stored.

Windows VM

Enter the visible gold amount. Use:

  • Value Type: 4 Bytes;
  • Scan Type: Exact Value;
  • Value: the current gold amount.

Start the first scan.

The first scan for gold

Straight after the first scan, many addresses hold the same number.

The scan will probably find many addresses. That is expected. The number 100 can appear in timers, object fields, capacities, interface data, and unrelated allocations.

At this point, each result proves only this:

These four bytes were readable as the integer 100 when the scanner checked them.

It does not prove that the address controls gold.

Return to Wesnoth and perform the planned action. Recruit the unit and read the new gold value off the screen.

Gold after recruiting a unit

After recruiting, the new gold amount is the next value to search for.

If the value changed from 100 to 85, return to Cheat Engine, enter 85, and choose Next Scan.

The scanner does not search the whole process from the beginning. It checks the previous candidate addresses and keeps only those that now contain 85. In set notation:

new_candidates = old_candidates ∩ addresses_currently_equal_to_85

You do not need the symbol memorized. It means “keep items that are in both groups.”

What Next Scan does with each candidate. This step-by-step trace needs JavaScript. Here is the code and where the run ends.

candidates = [A, B, C]  # every address that held 100
# you recruit a unit; the screen now shows new_gold
kept = []
for addr in candidates:
    now = read_4_bytes(addr)
    if now == new_gold:
        kept.append(addr)  # keep it
  # otherwise it is dropped
candidates = kept

Next Scan leaves B and C. Both followed the gold, so one may be the field and the other a copy for the screen. Repeat with a different change to tell them apart.

Final values: candidates = B, C; new_gold = 85; kept = B, C; addr = C; now = 85.

Read every step of the lesson’s example
  1. Step 1, line 1. The first scan for 100 left three candidates. Each one only proved that its four bytes read as 100 at that moment.

    candidates = A, B, C.

    Memory: address A = 100; address B = 100; address C = 100.

  2. Step 2, line 2. The screen now shows 85. B and C changed with it, but A, which only happened to be 100, did not.

    candidates = A, B, C; new_gold = 85.

    Memory: address A = 100; address B = 85; address C = 85.

  3. Step 3, line 3. Start an empty list for the addresses that survive.

    candidates = A, B, C; new_gold = 85; kept = (empty).

    Memory: address A = 100; address B = 85; address C = 85.

  4. Step 4, line 4. Take the next candidate: A.

    candidates = A, B, C; new_gold = 85; kept = (empty); addr = A.

    Memory: address A = 100 (reading now); address B = 85; address C = 85.

  5. Step 5, line 5. Read the four bytes at A right now. They read as 100.

    candidates = A, B, C; new_gold = 85; kept = (empty); addr = A; now = 100.

    Memory: address A = 100 (reading now); address B = 85; address C = 85.

  6. Step 6, line 8. 100 does not equal 85, so A is dropped. Its value did not follow the gold.

    candidates = A, B, C; new_gold = 85; kept = (empty); addr = A; now = 100.

    Memory: address A = 100 (reading now); address B = 85; address C = 85.

  7. Step 7, line 4. Take the next candidate: B.

    candidates = A, B, C; new_gold = 85; kept = (empty); addr = B; now = 100.

    Memory: address A = 100; address B = 85 (reading now); address C = 85.

  8. Step 8, line 5. Read the four bytes at B right now. They read as 85.

    candidates = A, B, C; new_gold = 85; kept = (empty); addr = B; now = 85.

    Memory: address A = 100; address B = 85 (reading now); address C = 85.

  9. Step 9, line 7. 85 equals 85, so B stays in the list.

    candidates = A, B, C; new_gold = 85; kept = B; addr = B; now = 85.

    Memory: address A = 100; address B = 85 (reading now); address C = 85.

  10. Step 10, line 4. Take the next candidate: C.

    candidates = A, B, C; new_gold = 85; kept = B; addr = C; now = 85.

    Memory: address A = 100; address B = 85; address C = 85 (reading now).

  11. Step 11, line 5. Read the four bytes at C right now. They read as 85.

    candidates = A, B, C; new_gold = 85; kept = B; addr = C; now = 85.

    Memory: address A = 100; address B = 85; address C = 85 (reading now).

  12. Step 12, line 7. 85 equals 85, so C stays in the list.

    candidates = A, B, C; new_gold = 85; kept = B, C; addr = C; now = 85.

    Memory: address A = 100; address B = 85; address C = 85 (reading now).

  13. Step 13, line 9. Next Scan leaves B and C. Both followed the gold, so one may be the field and the other a copy for the screen. Repeat with a different change to tell them apart.

    candidates = B, C; new_gold = 85; kept = B, C; addr = C; now = 85.

    Memory: address A = 100; address B = 85; address C = 85.

Make another controlled change with a different result. For example, recruit a unit with another cost or end a turn if the scenario changes gold in a known way. Enter the new gold value and perform another Next Scan.

A narrowed list of candidate addresses

After Next Scan the candidate list is much shorter.

Using different values is important. If you repeat only 100 → 85 → 100, an unrelated value that happens to follow the same pattern can remain in the list.

Continue until you have a small number of candidates. Do not assume the first remaining address is correct.

Add one candidate to the address list. Before changing it:

  1. copy its current value so you can put it back;
  2. confirm the game still shows the expected gold;
  3. choose a small replacement value;
  4. write the replacement once;
  5. observe the screen and one real purchase;
  6. restore the original value.

The edited gold value in Wesnoth

Wesnoth after one candidate was edited: the display shows the new value.

Different results support different explanations:

What happens Likely meaning
the amount changes and the next purchase uses that amount strong candidate for game-play state
only the text changes likely a display copy
the value changes back on the next update another system recomputes or owns it
nothing visible changes unrelated value or a copy used elsewhere
nearby behavior breaks wrong address, type, or field width may have been changed

A single run can mislead, because an unrelated value might change at the same moment by coincidence. Getting the same result again from the same starting state makes that explanation less likely.

The game can legitimately keep more than one gold-related value:

  • the current simulation value;
  • a previous value used by an animation;
  • text prepared for the interface;
  • a value in a replay, event, or network message;
  • a temporary calculation.

That is why value scanning is usually the start of reverse engineering. A write breakpoint later shows which instruction changes the strongest candidate. The surrounding instructions can reveal the object base and other fields.

The address you found belongs to this run of the game. After a restart, gold may live somewhere else, because the process layout and the heap can change between runs. A later lesson on building and loading a program explains why some distances stay fixed within one build even when absolute addresses move. First, a debugger lets us follow the candidate value to the instruction that changes it.

The first scan returned readable candidates that matched while it ran; it did not identify “the gold address.” Each controlled game change filtered that candidate set. Changing one survivor and then making a real purchase showed whether it affected gameplay, only the display, or neither. Restoring the original value leaves the game in a known state for another run.

The candidate address tells us where to look, but not why the value changes. When a purchase subtracts gold, which instruction performs the subtraction, and what decides whether the player can afford it? What a Debugger Can Reveal begins Chapter 2 with that question. We will pause the program at the change, then learn how source code becomes the instructions the debugger shows.

Knowledge check

Use a controlled change

A first scan finds many addresses containing 100. You recruit a unit and the visible gold changes to 85. Which addresses should the next scan inspect?

Finished Lesson 1.9?

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.

Chapter 1: 10 lessons

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.