Cheat Engine and Memory Scanning
How to find and modify a running game’s values in memory, and how to turn that into a persistent, patch-resistant trainer — the dynamic-analysis counterpart to the static-analysis workflow in x86 Reverse Engineering for Games. This is usually the actual entry point people take into game RE, because it needs no disassembly literacy at all to get a first result (freezing a health value), and naturally motivates learning to read x86 the moment you want to know why a value changed rather than just that it did.
Scope note: this is written for single-player/offline use — modding your own save, practicing on a game you own, or just poking at how a game represents its state. Doing this against an online multiplayer game to affect other players is a ToS violation on essentially every platform and, depending on the anti-cheat involved, can trigger bans or worse; see the scope note in x86 Reverse Engineering for Games for the fuller version of this caveat, which applies equally here.
Process memory basics
A running process’s memory is organized into a virtual address space private to that process — the addresses you see are not physical RAM addresses, and (with ASLR, the default since Windows Vista / most modern Linux) the base address a module loads at is randomized per run, so an absolute address found in one session will not be valid in the next unless it’s expressed relative to something stable. This single fact drives most of the workflow below: a “trainer” that just hardcodes 0x7FF6A1234567 breaks the moment the game restarts, which is why pointer chains (below) exist at all.
Memory regions within that space are tagged with permissions (read/write/execute) and belong to either the main executable module, a loaded DLL, or dynamically allocated memory (the heap, stacks, memory-mapped files) — Cheat Engine’s scanner lets you restrict a scan to particular region types, which matters because most gameplay values (health, position, ammo) live in heap-allocated objects, not static/global memory.
Cheat Engine scanning workflow
The core loop: narrow down millions of candidate addresses to the one holding the value you care about, by repeatedly telling Cheat Engine what you know changed.
- Attach Cheat Engine to the game’s process (the icon-with-magnifying-glass button, pick the process from the list).
- First Scan, value type set to match what you’re hunting (
4 Bytes/Float/Doubleare the common ones — health/ammo counters are usually a 4-byte int or a float; screen/world coordinates are almost always float). If you don’t know the exact current value, use Unknown initial value instead and rely entirely on step 3’s “changed”/“decreased”/“increased” scans. - Change the value in-game (take damage, pick up ammo) and run Next Scan with the matching condition (Decreased value, Increased value, or an exact new value if you know it). Repeat — each round eliminates addresses whose value didn’t change the way you specified, typically narrowing thousands of hits down to single digits within 3–5 rounds.
- Once narrowed to a handful of addresses, confirm by testing each candidate individually: double-click to add it to the address list, then edit the value directly and check in-game that it actually changed the right thing.
- To make an effect persistent, right-click the address in the list and enable Freeze (locks the value, re-writing it continuously so the game’s own writes are overridden) — the simplest possible “infinite health,” and functionally identical to what a compiled trainer does with a timer-based write loop.
Float scanning specifically: floating point comparisons are exact-bit by default, so a value like 100.0 that the game internally stores as 99.99997 will not match a 100 scan — Cheat Engine’s float/double scan type does an epsilon-tolerant comparison by default for exactly this reason; if a scan that should be matching finds nothing, check that the value type (float vs. double vs. int) actually matches what the game uses, which itself is diagnostic information about the game’s internal representation.
Pointer scanning: surviving a restart
An address found by scanning is only valid for the current run — the whole point of a pointer chain is to express that address as [[module_base + static_offset] + offset1] + offset2 ..., i.e. relative to something that doesn’t move (the module’s base address, fixed at load time relative to ASLR’s randomization, which Cheat Engine reports as modulename.exe+0xNNNN), rather than as a raw address.
Workflow:
- Find a dynamic address the normal scanning way (above).
- Right-click it → Find out what accesses this address (sets a hardware breakpoint that logs every instruction touching it) or Pointer scan for this address (Cheat Engine walks backward from the value through memory looking for other addresses that point at it, recursively, up to a configurable number of levels/offsets).
- Find out what accesses this address is usually the faster, more instructive route: trigger the value to change in-game, and Cheat Engine shows you the exact instruction that wrote it, with the full disassembly context — this is the direct bridge into the static-analysis skills from Reading Compiled Code Patterns, since that instruction’s addressing mode (
mov [rbx+0xC], eax, say) tells you both the offset within the struct and the register holding the base pointer at that moment. - From there, right-click that instruction → Find out what accesses this address again, one level further back, or use it directly to seed a Pointer scan, which produces a static path of the form
game.exe+0x1A2B3C → +0x18 → +0x40 → +0xCthat resolves to the live health address on any run, any restart, independent of ASLR — because each hop starts by re-reading a pointer from a location whose offset from the module base stays fixed even though the base itself moves. - Static globals (a genuinely fixed, non-relocating value — rare, but does happen for e.g. some singleton managers) show up in Cheat Engine simply as
game.exe+0xNNNNNNwith zero further offsets needed.
A multi-level pointer chain (base → player manager → local player → entity → health) is completely normal for anything beyond the simplest indie game — each hop corresponds to one dereference in the game’s own object graph, mirroring the struct/field-access patterns from Reading Compiled Code Patterns one level at a time.
Worked example: trainer-style memory edit
Putting the above together as a minimal, concrete trainer for a hypothetical single-player game’s health value.
1. Find the value. First Scan 4 Bytes, value 100 (assume the game showed 100 HP on screen). Take damage down to, say, 80; Next Scan value 80. Repeat with one or two more damage instances until the list narrows to one address, e.g. 0x1F3A45678.
2. Confirm it’s the right one. Double-click to track it, edit it to 9999 in Cheat Engine, and confirm the in-game HUD updates. If it doesn’t, or if the game overwrites your edit back to the real value within a frame (common for networked or server-authoritative values — a sign this isn’t a value the client fully controls), that’s diagnostic on its own.
3. Make it survive a restart. Right-click → Find out what accesses this address, trigger more damage in-game, and read off the logged instruction, e.g.:
sub dword [rbx+0xC], eax ; game.exe+0x2A1F40This tells you two things at once: the struct offset (0xC, matching the Entity::health layout guessed in Reading Compiled Code Patterns), and that rbx at that instruction holds the live entity pointer. Right-click that logged instruction’s address itself (not the value) → Pointer scan, or manually walk it: find what points at the address currently in rbx the same way, repeating until you reach an address expressed as game.exe+offset with no further dynamic hops — that’s your stable base.
4. Write it as a persistent script. Cheat Engine’s built-in scripting (Lua, via the Auto Assemble window) can encode the pointer chain and either freeze the value or inject a code change directly. A simple AOB-based “infinite health” injection, illustrating the signature-scanning technique from x86 Reverse Engineering for Games applied here instead of to a file on disk:
[ENABLE]
aobscan(healthSub, 29 43 0C 8B 4D ??) // scan for the sub instruction's bytes, wildcarding what varies
alloc(newmem, 256, healthSub)
label(returnhere)
newmem:
cmp [rbx+0xC], 1 // don't let the subtraction bring health below 1
jle skipdamage
sub [rbx+0xC], eax
skipdamage:
jmp returnhere
healthSub:
jmp newmem
nop
returnhere:
[DISABLE]
healthSub:
db 29 43 0C
unregistersymbol(healthSub)
dealloc(newmem)aobscan finds the instruction by its byte pattern (surviving small game updates that shift the address but not the surrounding code, the same principle as the file-patching AOB signatures in x86 Reverse Engineering for Games); alloc reserves a code cave; the [ENABLE] block overwrites the original instruction with a jmp into that cave, runs a guarded version of the same subtraction (skip it once health would drop to zero), then jumps back; [DISABLE] restores the original 3 bytes exactly, which is why the disabled block records db 29 43 0C — the original raw bytes captured before patching. This is a direct, hands-on instance of the JMP-redirect/code-cave technique described more generally in x86 Reverse Engineering for Games, scoped specifically to live process memory instead of a file on disk.
5. Attach a UI, optionally. Cheat Engine can export this as a standalone trainer (.CETRAINER, or a compiled .exe via its trainer generator) with a checkbox/hotkey bound to the script’s [ENABLE]/[DISABLE] blocks — the mechanical difference between “a Cheat Engine table” and “a standalone trainer.exe” is packaging, not technique; both are the same pointer chain plus the same code injection underneath.
See also
- x86 Reverse Engineering for Games — the static-analysis and patching counterpart to this article, including the crackme walkthrough and the code-cave/AOB-signature techniques used above.
- Reading Compiled Code Patterns — recognizing struct/field layouts (like the
healthoffset found here) from static disassembly instead of by scanning. - x86 Assembly Fundamentals, x86 Instructions and the Stack — the instruction-level knowledge needed to read what “Find out what accesses this address” reports.
- All Articles