Skip to content

Cracking a Hostile Executable

Conan the Cimmerian’s Amiga executables resisted the usual approach this project uses on every other game here — disassemble the static code, annotate it, trace data structures from labelled call sites. That approach doesn’t work on Conan, because there’s almost no static code to disassemble. This page tells that story; the byte-level detail behind every claim below lives in docs/conan/plan.md and docs/conan/amiga/engine.md, which this page summarizes rather than repeats.

Both Game (the main game) and Conan (the splash-screen loader) are Amiga HUNK executables with the same shape: two tiny CODE hunks totalling 632 bytes, one large BSS reserve, and one large DATA hunk (89,588 bytes in Game, 37,984 in Conan) that’s compressed on disk. The 632 bytes of code are a self-decompressing bootstrap: they unpack the DATA hunk into the BSS reserve at runtime, and the real game logic lives in whatever comes out of that decompression — not anywhere directly visible in the file.

This is why Game.cnf/Conan.cnf’s zero-length CODE ranges beyond the first aren’t misconfigurations to fix — there just isn’t more code on disk to widen the range into. The productive move was analysing the decompressed data directly, which meant first working out what compressed it.

The decompressor turned out to be a custom, backwards-reading LZ77

Section titled “The decompressor turned out to be a custom, backwards-reading LZ77”

The DATA hunk isn’t PackBits (the compressor every other resource in this game’s .res/.L32 files uses) — an earlier pass assumed it was and got a plausible-looking 148,002-byte blob that was actually wrong data end to end, corrected in docs/conan/plan.md’s “Palette Investigation Summary”. The real algorithm is a custom engine embedded in hunk 4, and rather than hand-port it (the usual failure mode for a hostile decompressor — see this project’s own reverse-engineering playbook on emulating a game’s own routine instead of re-deriving it by hand), tools/conan/decompress-data-hunk/ runs the game’s own 496-byte decompression routine under a musashi 68k emulator core and dumps the output buffer directly. Verified by legible recovered game text in the output (arbitrary English prose surviving is strong evidence — a wrong plane order, wrong stride, or wrong decompressor reliably produces garbage, not readable sentences), not just a byte-count match.

The palette hunt (now resolved, after two dead ends)

Section titled “The palette hunt (now resolved, after two dead ends)”

With correctly decompressed data in hand, the palette search still took two more wrong turns before it landed:

  1. Wrong byte width. Standard 16-bit-per-entry (Amiga OCS 12-bit, 0RGB) and 32-bit RRGGBB00 (the Spirit/Vengeance/WIME convention) palette scans both came back empty against Game hunk1 and gave only sliding-window artefacts against hunk2 — matches that looked like a 32-entry table but were really one region getting hit repeatedly at every possible byte offset.
  2. The real shape: a 4-bit-nibble {R, G, B, pad=0} record — each channel a raw 0–15 value, never left-shifted into a byte. Once that shape was tried, Game hunk2 offset 0xe8c (originally misread as 0xe88, one record early — see the engine doc’s correction) turned out to hold four 32-entry palettes: a base scene palette, a darkened night variant, a warm variant, and the base with three slots forced to white.

The DOS port’s own executables (CONAN.EXE, START.EXE) needed a separate trace, because they never touch VGA hardware directly — an early “no 0x3C8/0x3C9 port writes anywhere” scan was a true negative about a false premise: all palette I/O goes through a swappable driver module (MCGA.DVR for VGA/MCGA, EGA.DVR for EGA/Tandy) named in GAME.CFG, not inlined in the game executable. Tracing each driver’s own jump table found the real palette tables at CONAN.EXE+0x12163 (loader screens) and START.EXE+0x2b8f7 (in-game scene palettes) — nibble triples again, same shape as the Amiga side, just packed 3 bytes instead of 4.

Cross-platform check: decoding the Amiga’s 4-byte records and the DOS port’s 3-byte records completely independently and comparing gives 0 of 576 mismatched nibble components for the loader palettes, and 381 of 384 for the in-game scene palettes (the one genuine difference is an authoring change in the night variant, not a decode error). See docs/conan/amiga/engine.md § “Per-screen palettes” and § “In-game scene palettes” for the full derivation, including how each palette block’s exact boundaries were pinned from both sides rather than inferred from where a byte scan happened to start.

The DOS scene format came from a cracked copy

Section titled “The DOS scene format came from a cracked copy”

SCENES.RES’s SCEN format (236 scene/room object-placement lists, 6,542 object entries) is fully traced — but not from either Amiga executable, which generally have no scene-drawing code to find given how little static code either one contains. It came from the DOS port’s START.EXE, and getting there took an unexpected detour: this copy of START.EXE had been written off as “LZEXE-compressed but rejected by the standard unpacker.”

The rejection turned out to be a tampered MZ header, not an unsupported compression variant — RUNME.COM in the same DOS release embeds the strings CONAN.EXE and RAZOR.COM, i.e. this copy had been crumbled through a contemporary cracking group’s loader, which rewrote the executable’s entry point (e_ip/e_cs) to point at its own unpacking stub instead of the original LZEXE stub. Patching those two fields back to the real LZEXE entry point (found by searching for the LZEXE stub’s own byte signature elsewhere in the file) let the stock unlzexe unpack it cleanly. From there, tracing _LoadScene gave a byte-exact 4-byte-header + packed-bitfield object format — see docs/conan/amiga/engine.md § “SCEN Format” for the full trace, including two earlier failed decode attempts (bit-width brute force against a wrong header-size assumption) that are kept on record with why each one structurally couldn’t have worked.

.L32 sub-frame splitting: a hardcoded animation table

Section titled “.L32 sub-frame splitting: a hardcoded animation table”

One more case worth telling: HORSE.L32 contains images that are really horizontal filmstrips — several riders side-by-side in one bitmap — with no FRML resource anywhere describing how to split them (see the Image Gallery for the rendered filmstrips themselves). The split lives in CONAN.EXE as a hardcoded 68-step lookup table: six parallel 70-byte arrays giving, per animation step, which of the 11 loaded resources to draw from, a source rectangle within it, and where to place it on screen (including a vertical-anchor adjustment that keeps a horse’s feet on one ground line as its sprite grows from a tiny distant silhouette to a near-full-screen close-up). Cross-checked two independent ways — a pixel-domain re-derivation from the background-column structure of the images themselves, and the exact frame counts the two originally puzzling renders show — both matching the table exactly. See docs/conan/amiga/engine.md § “.L32 sub-frame splitting” for the byte offsets and the full verification.

The animation-frame segmentation described on the Animation Gallery page is the one area still honestly marked as a hypothesis rather than confirmed: 23 of 101 FRML resources have a code-confirmed classification (scene decoration), but the other 78 — mostly combat sprites — rely on a render-derived geometric guess rather than a located code table, because no such table was found in either executable after four different search approaches. See docs/conan/TODO.md, item conan-frml-combat-animation-ranges, for the current status.