Skip to content

Releases: wleeaf/cheat-engine-linux

v0.8.0: CE parity sweep, global symbols, code pages, Lua breadth

Choose a tag to compare

@github-actions github-actions released this 20 Jul 01:30

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 .lua in <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

Choose a tag to compare

@wleeaf wleeaf released this 18 Jul 16:58

"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|st overrides the backend for diagnostics;
    CE_LOG=debugger:debug logs 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) used PTRACE_ATTACH and 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 to PTRACE_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
    mmap2 address at or above 2 GB (e.g. 0xEBDF9000) was sign-extended to a
    negative value, so allocate() rejected a successful mapping. Only the i386
    -errno range 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

Choose a tag to compare

@wleeaf wleeaf released this 18 Jul 00:29

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 : Bar in 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 05 and 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 ?? 05 for 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 shows 0x… 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 the module+offset it 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. --settings opens 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 ...
Read more

v0.6.0: scanner performance overhaul

Choose a tag to compare

@wleeaf wleeaf released this 16 Jul 15:06

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

Choose a tag to compare

@github-actions github-actions released this 15 Jul 08:26
v0.5.0 — Mono/Unity dissector, CE_LOG diagnostics, Lua table compatib…

v0.4.1 — Theme, memory-access, and packaging fixes

Choose a tag to compare

@wleeaf wleeaf released this 13 Jul 10:49

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 shared gui/theme module 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 .deb now runs setcap cap_sys_ptrace+ep on
    the installed binaries at install time (via a postinst; depends on libcap2-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 (via pkexec).
  • 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

Choose a tag to compare

@wleeaf wleeaf released this 13 Jul 09:42

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 same LuaEngine
    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_pumpEvents now 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
    as 9999f — an invalid integer-with-f-suffix that failed to compile. Now forces
    a fractional part (9999.0f), guards against inf/nan, and defines _GNU_SOURCE in
    the generated source so process_vm_readv/writev use their real ssize_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

Choose a tag to compare

@wleeaf wleeaf released this 11 Jul 13:32

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.