Releases: wleeaf/cheat-engine-linux
Release list
v0.8.0: CE parity sweep, global symbols, code pages, Lua breadth
A broad Cheat Engine parity pass across the cheat table, Memory Viewer, structure
dissector and Lua API. This release also carries the v0.7.0 work, which was written
into the changelog but never tagged or published.
Cheat table
- "Add to new group" wraps the selected records under a new header.
- Deleting a group header now removes its whole subtree instead of orphaning children.
- "Convert to module+offset" rewrites a raw scanned address as
module+0x…, so a saved
table survives PIE/ASLR rebasing on the next launch. - Drag-and-drop reordering, group collapse/expand, and recursive "Set value" on a group.
Scanning
- "Remove selected addresses from the found list", pruning the result set properly so a
later Next Scan stays consistent (undo restores them). - String results now decode from the scan's code page. A CP1252 hit like "café" no longer
renders as mojibake.
Symbols
- User-defined labels are global. A label set in the Memory Viewer resolves as a record
address, in Lua, and in every open view.
Memory Viewer and debugger
- "Add this address to the list" from the disassembler, wildcard AOB search, Find previous,
match highlighting, and stepping driven from the Memory Viewer. - Debugger shows R8-R15, all flags, predicts conditional jumps, and gets Run to cursor (F4).
Structure dissector
- Set a field's type from the context menu, so a hand-built struct round-trips through
save/load and the value column.
Lua
- New:
disassembleBytes,AOBScanUnique/AOBScanModuleUnique,convertToUTF8/
convertFromUTF8/ansiToUTF8,extractFileNameWithoutExt,getFileList,
addMainMenuItem,md5memory,enumModules,getUniqueAOB,getPointerSize,
getMemoryRegionInfo, plus the CE string extensions. - Autorun: every
.luain<config>/cecore/Cheat Engine/autorun/runs at startup.
.CT interop
- Hotkeys round-trip both ways, string/AoB length and the wide-string flag are honored,
group<Options>are preserved, and dropdown options no longer drop on import.
Fixes
- Heap use-after-free generating an AOB injection (caught by the ASan CI job).
- Several pointer-record correctness fixes: freezing, inline value edits and
increase/decrease hotkeys could write to a stale address or corrupt hex-display records.
Full detail in CHANGELOG.md.
Install: .deb for Debian/Ubuntu, or the AppImage (chmod +x, then run). Both need
cap_sys_ptrace to attach; the .deb sets it for you.
v0.6.6 — find-what-writes and code injection on Wine/Proton
"Find what writes/accesses" and auto-assembler code injection used to freeze or
crash a game running under Wine/Proton (validated against Mount & Blade Warband
under Proton 9.0). This release makes every transient debug operation Wine-safe.
Watchpoints ("Find what writes / accesses")
- No longer freezes Wine/Proton games. The watchpoint used to
PTRACE_SEIZE+
stop the game's entire thread group to arm; stopping wineserver, esync/fsync and
GPU/driver threads deadlocks the game (it went unresponsive, black screen). On a
Wine/Proton target it now arms a hardware watchpoint on the main thread only
(the game-logic thread that writes money/HP), touching nothing else. Native Linux
keeps the full all-thread watch. The software page-guard backend (which fought
Proton's kernel write-watch / userfaultfd) is no longer used on Wine. - Repeatable. A finished monitor kept the thread traced, so a second
find-what-writes silently found nothing; finished monitors are now stopped (and
released when their window closes) before a new one starts. - Fixed a use-after-free crash at exit (the monitor thread could call into a
debugger object that had already been destroyed). CE_CODEFINDER_MODE=hw|sw|stoverrides the backend for diagnostics;
CE_LOG=debugger:debuglogs arming, hits and teardown.
Code injection (auto-assembler scripts, loadlibrary, Mono agent)
- AA code-injection scripts work on Wine/Proton. The code-cave allocation
(remoteSyscall->mmap) usedPTRACE_ATTACHand hijacked a thread parked in a
syscall, which on Wine (threads sit in esync/fsync/wineserver waits) corrupted the
wineserver RPC and froze the game. Switched toPTRACE_SEIZE + PTRACE_INTERRUPT, a
clean stop that preserves the interrupted syscall's restart state. - WoW64 allocations no longer wrongly reported as failures. A valid 32-bit
mmap2address at or above 2 GB (e.g.0xEBDF9000) was sign-extended to a
negative value, soallocate()rejected a successful mapping. Only the i386
-errnorange is treated as an error now. - The dlopen injector (
loadlibrary(),createthread(), the Mono agent) and
the WoW64 bitness probe were switched to the same Wine-safe stop. - The full debugger and break-and-trace still stop the whole process on purpose
(that is what they do); everything meant to be quick and transparent does not.
v0.6.5 — whole-app UI/UX overhaul + IL2CPP dissector depth
A comprehensive, panel-by-panel usability pass over the entire app, on top of
IL2CPP dissector depth and a batch of Cheat-Engine-parity scanning details.
Every panel, window, dialog and settings page was audited (screenshot-verified
in both the light and dark themes via a new --pid / --panel /
--settings-page launch harness).
UI / UX overhaul (every panel audited)
- Table columns stop clipping their contents. Every list/table in the app
(pointer scanner, memory/heap/module/thread regions, find-statics, structure
dissector, code-finder, break-and-trace, register/SIMD editor, code references,
stacktrace, advanced options, settings hotkeys) used to leave its columns at
Qt's 100px default, so 16-digit hex addresses and 64/128-bit register values
were truncated. Each table now sizes its fixed columns to content and lets the
one variable column take the slack. - No more text-less or cryptic controls. Spinbox/combo dropdown arrows were
unstyled and rendered blank app-wide (now drawn); the Structure Dissector's
"Compare" field had collapsed to a bare "..." because its toolbar overflowed a
single row (now a two-row layout with the field spelled out); the Lua console
grew explicit Run and Clear buttons instead of relying on the Enter key. - Analysis tools are findable from the main window. Auto Assemble, Pointer
scan, Dissect data/structures, Find static addresses, the Mono dissector, the
Lua engine and the ELF inspector were only in the Memory Viewer's own menus;
they now also appear in the main window's Tools menu. - Decluttered layout. The Structure Dissector toolbar was split into an
address row and an actions row; the dead Windows-only D3D menu (every item
permanently disabled on Linux) was removed; the Fill Memory dialog's fields are
aligned in a form layout, and the process picker groups Open/Cancel on the right. - Colours fit both themes. The two remaining hardcoded disassembler colours
(DWARF source-line annotations and the breakpoint gutter glyph) washed out on
the light theme; both are now theme-gated. The Lua console dims echoed commands
and shows errors in red. - Code References no longer looks frozen. Analyzing a module drives a
cancelable progress dialog across its eight scan passes instead of hanging. - System theme on first launch, working scrollbars, real breakpoint toggle
(from the same pass): the app follows the desktop's light/dark preference on
first run; the memory-view scrollbars track an absolute flattened-memory model
so dragging no longer snaps back; and Toggle Breakpoint truly toggles.
IL2CPP (Unity) dissector
- Managed field type names resolved offline from the GameAssembly binary:
System.Single,UnityEngine.Vector3(VALUETYPE/CLASS), arrays (MyClass[]),
pointers, and generics spelled out (List`1<System.String>,
Dictionary`2<K, V>). Every field on real v27/v31 games resolves a name. - Base class of each type (parent chain), rendered as
Foo : Barin the
dissector; and full object layouts with inherited fields
(getIl2CppObjectLayout,cescan il2cpp --object <class>), each field tagged
with its declaring type.
Scanning
- Tri-state Writable/Executable region filters (CE's grey/checked/unchecked
boxes): must-have / must-not-have / don't-care. - "Pause target while scanning" — SIGSTOP the target for a consistent snapshot
during each scan, then resume (skips a target that's already stopped). - New Scan flow: First Scan becomes New Scan after scanning and locks the
value type; a Previous column shows the scan-time value next to the live one. - AOB / string result values fixed — they showed "?"; AOB now renders as
48 8B 05and strings as text, in both the results list and the cheat table
(which also keeps AOB/string entry types across save/load). - Enter-to-scan, type-aware value placeholders (
48 8B ?? 05for AOB), the value
box greys out for no-value compares, thousands-separated result counts, clear
status feedback when there's no process, and Save current scanresults to
txt/csv.
UI / memory viewer
- Scan panel rebuilt with real Qt layouts (was absolute pixel coordinates:
dead space, no scaling); results list expands into the reclaimed width. - Memory-view scrollbars work — the disassembly/hex panes had a dead or
missing scrollbar; both now scroll memory, and scrolling up past mapped memory
no longer strands the view ("no memory" with no way back). - Jumping to unmapped memory now says so: a Go / follow / back-forward that
lands on an unreadable address shows0x… is not readable (unmapped or protected page)in the status bar, instead of a silent pane of??. - Memory viewer gives the disassembly/hex more width by default: the register
and stack panels only show placeholders (live registers are in the Debugger
window), so they no longer take a quarter of the width, and the disassembler's
module+offset/ data-reference annotations stop truncating off the edge. - Every "Browse this memory region" opens the full memory viewer. Opening it
from a scan result, a cheat-table entry, Advanced Options, or the Memory
Regions/Heap/Module/referenced-strings windows used to give a stripped-down
viewer with no breakpoints, "add to list", Tools/Debug menus or debugger launch;
all of those now open the same fully-wired viewer as the Memory View button. - No crash when the target exits with a Memory Viewer or Structure Dissector
open. When the attached process ends (or you attach to a different one), open
Memory Viewers and Structure Dissectors are frozen and the Lua engine's process
pointer is cleared before the process handle is destroyed, so a refresh timer or
table script can't read the freed handle. - Dark-theme tree fix — tree widgets (Mono dissector, breakpoint/thread/module
lists, structure dissector) rendered class rows as unreadable white stripes;
now themed. Tool buttons, radios, and the speedhack slider themed to match. - Addresses as
module+offset(game.bin+0x1234) in the cheat table, stable
across restarts; empty-state hints on the results and cheat-table panes; the
window title shows the attached process. - Static scan results shown in green (CE's cue): a result address inside a
loaded module is pointer-stable across restarts, so it is coloured green and
hovering it reveals themodule+offsetit belongs to. - Disassembler annotates unnamed call/jmp targets with
module+offset
(e.g.jmp 0x… ; GameAssembly.so+0x1234) when no symbol exists, so stripped
game binaries are still navigable; conditional jumps stay uncluttered. - Hex view: multi-byte range selection by drag or shift+click (highlighted
in both the hex and ASCII columns), with right-click "Copy selection as AOB"
and "Copy selection (hex, no spaces)". Editing or arrow-navigating collapses
the range back to a single byte. - Scan result count no longer looks truncated: the results table shows at
most 10,000 rows for responsiveness, so when a scan finds more the "Found"
label now says e.g.Found: 2,000,000 (showing first 10,000)instead of
leaving the capped list unexplained. - Disassembler colours fixed for the light theme. The operand text, the
selected/branch-target row highlights, and the user-comment colour were
hardcoded to dark-theme values, so on the light theme operands rendered as
near-invisible pale lavender and a selected row became a dark bar. These are
now theme-aware (readable dark-slate operands and a soft selection tint on
light; unchanged on dark). - Memory viewer debug toolbar decluttered: the six near-identical Run/Step
buttons (which all just opened the separate Debugger window) collapse to a
single "Debugger" button, leaving Toggle BP / Debugger / Preferences. - Memory viewer hides the register/stack panels by default. They only
populate during a debug session (which runs in the separate Debugger window),
so they were dead-placeholders taking a quarter of the width; now the
disassembly and hex use the full width, and a persisted View toggle ("CPU
registers & stack panels") brings them back for CE's layout. - Settings dialog redesigned with a vertical category sidebar. The 15
categories used to overflow a horizontal tab bar (most tab names hidden); they
now sit in an always-visible left-hand list (horizontal text) with the page on
the right, the standard modern settings layout.--settingsopens the dialog
straight on launch. - Zebra-striped result and cheat-table rows for easier scanning of dense
address/value lists. The theme already defined the alternating colour but the
views never enabled it; enabled now, with the light stripe nudged from nearly
invisible to a soft, readable grey. - Scan panel declutters for the value type: the float-only Rounding and
Tolerance controls are now hidden (not just greyed) for integer/text scans, so
the row collapses instead of leaving dead controls under Value Type. They
reappear when you pick Float/Double (Tolerance only in "Extreme" mode). - Percentage scan fields hide until needed: the "Compare by %" value and the
"Percent max" row now appear only when "Compare by %" is ticked (and the max
only for a "between" compare), instead of sitting greyed on every scan. - The main window and Memory Viewer remember their size, position and panel
layout across runs (window geometry and every splitter are saved on close,
restored on launch), instead of always reopening at the default 760x560 /
900x600. - File > Load Recent now works. It was a permanently empty menu; it now
lists the last 10 cheat tables you opened or saved (most recent first, full
path on hover), greys out ones that have since moved, and has a "Clear list". - Paste records copied from Cheat Engine. Ctrl+V in the ...
v0.6.0: scanner performance overhaul
Performance release. The value scanner (first scan and next scan) was rebuilt around the memory pipeline. Results are unchanged, verified against a brute-force reference across every value width, alignment, comparator, and both scan phases, and clean under ASan/UBSan.
Now the fastest memory scanner on Linux
In a same-machine, same-target benchmark, a first scan for a value is about 2x faster than Cheat Engine 7.7 (the official native Linux build) and 30 to 40x faster than scanmem, GameConqueror, and PINCE, with larger margins on some scans (up to ~13x vs Cheat Engine, ~145x vs scanmem). Full methodology and how to reproduce it: BENCHMARK.md.
| First scan, 1 GB, exact int32 | Time | Throughput |
|---|---|---|
| This project | 0.085 s | ~12 GB/s |
| Cheat Engine 7.7 (native Linux) | 0.156 s | ~6.6 GB/s |
gdb find |
0.749 s | ~1.4 GB/s |
| scanmem 0.17 / GameConqueror | 2.924 s | ~0.34 GB/s |
| PINCE (libmemscan) | 3.480 s | ~0.29 GB/s |
Typical first-scan lead over CE 7.7 is ~2x; "up to" figures are best cases. One machine (i5-10500H, 12 threads); CE timed excluding its GUI startup. GameConqueror uses scanmem's engine; PINCE uses its own Zig backend (libmemscan), benchmarked here on exact-value scans (it is reportedly faster on unknown-value scans, not covered here).
What changed
- Cache-blocked first scan (~3x): memory is read in L2-sized chunks so the scan runs from cache, not RAM.
- Every core on one region: a single large mapping is now split across threads (was single-threaded).
- Skip reserved/untouched pages via
/proc/pid/pagemap(resident-only reads). - SIMD for exact and rounded numeric scans and for byte-pattern (AOB / string) scans.
- Next scan: batched, multithreaded, and coalesced reads (millions of tiny reads become large transfers).
- Compact result storage: 4-byte offset addresses, no duplicated first-value stream.
Full details under "Performance" in CHANGELOG.md.
Install
The .deb and AppImage below are built by CI. The .deb/AppImage set cap_sys_ptrace, so scanning works without root.
v0.5.0
v0.5.0 — Mono/Unity dissector, CE_LOG diagnostics, Lua table compatib…
v0.4.1 — Theme, memory-access, and packaging fixes
Bug-fix release on top of v0.4.0.
Fixes
- Light/dark theme now works. Toggling "Dark theme" in Settings applies live
(no restart), and the startup default and the Settings checkbox agree (both
default to light). The two stylesheets moved into a sharedgui/thememodule so
startup and the dialog use the same sheets. - Memory browser no longer fails silently. When a process can't be read
because of the kernel's ptrace policy (kernel.yama.ptrace_scope), the
disassembler shows a clear explanation instead of a blank pane, and opening such
a process pops a warning naming the exact remedies. This is why "browse memory"
appeared to work only on some processes.
Packaging
- Automatic ptrace access. The
.debnow runssetcap cap_sys_ptrace+epon
the installed binaries at install time (via a postinst; depends onlibcap2-bin),
so scanning/browsing/debugging works without running as root. For AppImage or
ad-hoc runs, the "process not readable" dialog has a one-click Grant access…
button (viapkexec). - Working AppImage. The AppImage is now built with linuxdeploy + the Qt plugin,
so it bundles the Qt platform plugins and actually runs (the earlier hand-rolled
bundle was missing them).
Install (.deb, recommended): grants memory-access rights automatically —
sudo apt install ./cheat-engine-linux_0.4.1_amd64.deb
AppImage: chmod +x CheatEngine-0.4.1-x86_64.AppImage && ./CheatEngine-0.4.1-x86_64.AppImage
(if a process shows no memory, use the in-app "Grant access…" button).
v0.4.0 — CLI / headless parity for every GUI tool
Everything the GUI can do is now doable from the terminal. The guiding principle:
a GUI action must only gather input and call a shared core function (in cecore
or exposed via Lua), never hold its own copy of the logic, so the terminal path
and the GUI path run the same code and any GUI-only failure is provably a GUI bug.
Every item below is exercised by an automated test in cecore_test
(test_lua_headless_bindings, test_lua_ceserver_connect, test_trainer_generation);
the full suite is green.
Headless runner
cescan lua <file> | -e "<code>" | -plus a REPL — runs the sameLuaEngine
the GUI console uses, backed by a headless in-memory address list, so the whole
Lua API (now ~205 functions) works from the terminal.
New Lua tool bindings (each calls the same core code as the GUI)
- Cheat table:
saveTable(path)/loadTable(path)(the GUI's.CT/JSON format). - Create trainer:
generateTrainer(path)compiles a standalone trainer binary;
generateTrainerSource()returns the C source. - Pointer scanner:
pointerScan(target[, maxDepth[, maxOffset[, opts]]]). - Structure dissect:
dissectStructure(addr | {addrs}, size)— the
discriminating-field detector across N instances. - Detect Mono/.NET:
getManagedRuntimes(). - Find what accesses / writes:
findWhatWrites/findWhatAccesses
(hardware data watchpoint via the code finder). - Break and trace:
breakAndTrace(start[, maxSteps[, opts]]). - Branch mapper:
branchMap([secs[, tid]])/branchMapAvailable()(hardware LBR). - Debug register / stack:
debug_getRegisters/debug_setRegister/
debug_getStack;debug_pumpEventsnow publishes the full GP register set. - Find statics:
findStatics([module]). - Connect to ceserver:
connectToCeserver(host, port, pid)installs a remote
process as the target so the whole read/write/scan API works over the network. - Address-list grouping:
createGroup([desc])and memory-record.Indent
(indent / outdent).
Fixes
- Trainer code generation: a whole-number float value (e.g.
9999) was emitted
as9999f— an invalid integer-with-f-suffix that failed to compile. Now forces
a fractional part (9999.0f), guards against inf/nan, and defines_GNU_SOURCEin
the generated source soprocess_vm_readv/writevuse their realssize_t
prototype (was implicitly declared, truncating the 64-bit return on x86-64). This
also fixed the GUI's Create Trainer.
v0.2.1 — AOB injection fix + 32-bit/WoW64/Wine support
Cheat Engine for Linux v0.2.1
A correctness release headlined by a fix that makes AOB injection scripts actually work, plus 32-bit / WoW64 / Wine support and cheat-table inline editing.
Headline fix: AOB injection scripts
aobscanmodule(INJECT, game, <pattern>) + INJECT: jmp newmem silently failed to patch its target. The auto-assembler's label pre-pass did not include the aobscan family, so a phantom label(INJECT) shadowed the scanned address with a zero-address forward label. The hook and stolen bytes went into the code cave instead of over the target instruction, while execute() still reported success.
The result name of aobscan / aobscanmodule / aobscanregion / aobscanall now resolves to the scanned address when used as an injection label. Verified end-to-end on 64-bit no-PIE, 64-bit PIE (ASLR), and 32-bit targets, guarded by a regression test.
Since v0.2.0
- 32-bit code injection (WoW64) and Wine / Proton module enumeration with correct bitness, so
aobscanmodule(name, game.exe, ...)resolves against the right module. - Cheat-table inline editing: edit an entry's address with the full expression syntax (hex, symbol, module+offset,
[pointer]+off), and edit auto-assembler scripts in place (saved back to the same entry). - ptrace self-conflict fix, pre-filled injection templates from the memory browser, syntax-highlighted AA scripts, and a live scan progress bar.
Repository
This project is now a single self-contained repository: Lua 5.3 is vendored and built from source, so it compiles offline with no upstream Cheat Engine checkout. Full per-version history is in CHANGELOG.md.
Single-player and reverse-engineering focus; this release does not target multiplayer anti-cheat. The CheatEngine-x86_64.AppImage asset below is built by CI.