Darker executable: first-pass investigation
Follow-up: the startup has now been traced and the executable unpacked. See Executable unpacking and the first internal map for the verified 64 KiB image, reconstructed MZ, and newly identified tables. This document retains the observations and uncertainty of the original pass.
Investigation date: 2026-09-18. This records the supplied installation, not an assumed original retail executable. Original files were not modified.
Result
darker/X.EXE is a 52,936-byte DOS MZ executable with an unusually opaque,
self-modifying entry sequence. Its DOS container can be described precisely;
its original compiler, linker, runtime, sound middleware and game-code layout
cannot yet be identified reliably. Compression or protection is a reasonable
working hypothesis, not an identified format.
This copy has been confirmed to run. The local exploratory execution attempts did not establish a successful game startup or a trustworthy failure diagnosis. Do not classify the executable as corrupt on the basis of this investigation.
Specimen and reproducibility
SHA-256 of darker/X.EXE:
0e211d7bee660a9956bfd8a896bdfbda82f1d946a71942177eb8b77460fbb819
From the repository directory:
python3 tools/inspect_exe.py
objdump -D -b binary -m i8086 --start-address=32 --stop-address=288 darker/X.EXE
The Python script uses only the standard library. It reads the executable and
writes analysis/executable/mz-report.json and strings.txt. The saved
entry-disassembly.txt is a linear decoding of the on-disk bytes, not a
validated control-flow listing or a recovered startup routine. Offsets in these
artifacts are file offsets unless expressly stated otherwise.
DOS container
| Property | Value | Interpretation |
|---|---|---|
| Signature | MZ |
DOS executable container |
| Header size | 0x20 / 32 bytes |
Two paragraphs |
| File pages | 104 | Pages are 512 bytes |
| Final-page bytes | 200 | Declared size exactly matches physical size |
| Loaded image | 0x20..0xCEC7, 52,904 bytes |
Bytes after the header |
| Trailing data outside declared image | 0 bytes | No appended overlay outside the MZ size |
| DOS relocation count | 0 | DOS performs no relocation-table fixups |
| Relocation-table offset | 0x1E |
No entries to read at that location |
Initial CS:IP |
load segment + 0000:0000 |
Entry at file 0x20 |
Initial SS:SP |
load segment + 1161:0086 |
Stack address is image-relative 0x11696 |
| Minimum extra allocation | 0x7AEF paragraphs |
503,536 bytes beyond the rounded image |
| Maximum extra allocation | 0xFFFF paragraphs |
Permits DOS to allocate available conventional memory |
| Checksum | 0000 |
Does not provide an integrity check |
| Overlay number | 0 | Main MZ module |
The minimum allocation implied by the header is 556,704 bytes including the 256-byte PSP, after paragraph rounding. That is approximately 543.7 KiB. It is not a measurement of actual working memory, and says nothing conclusive about additional EMS/XMS use. The initial stack lies beyond the file-backed image but within the requested allocation; that is not itself an error.
There is no normal Windows extended header demonstrated here. In particular,
offset 0x3C is inside the loaded image, not an established e_lfanew field of
a larger DOS header. The outer MZ format establishes real-mode DOS entry; it
does not prove that all later code remains real-mode or uses only 16-bit
instructions.
Zero DOS relocations likewise does not prove a single code segment or position-independent game code. Startup code could relocate a recovered image itself. Absence of a trailing overlay does not preclude modules loaded from the external data files.
The startup puzzle
The four header bytes at 0x1C are:
01 00 44 31 ...D1
D1 is a useful candidate marker. Its meaning is unknown. It is tempting to
associate it with the supplied readme's “Day 1” production credit, but two
characters are insufficient to establish that connection or name a packer.
The declared entry begins:
file offset bytes 16-bit interpretation
00000020 D0 EC shr ah, 1
00000022 99 cwd
00000023 FD std
00000024 E4 08 in al, 08h
00000026 23 F2 and si, dx
00000028 F6 EE imul dh
0000002A 8B FE mov di, si
0000002C FE 06 9D 01 inc byte ptr [019Dh]
The instruction at 0x2C is especially useful. Under normal DOS EXE loading,
DS points at the PSP and the image starts 256 bytes later. Here initial CS is
the image segment. Thus DS:019D corresponds to image offset 0x9D, or
file offset 0xBD. The on-disk byte there is FF; the increment changes it
to 00. This is concrete self-modification at startup, not a guessed compiler
idiom.
A limited Unicorn experiment, using a synthetic PSP and zero-initialized RAM, also observed changes to later instruction bytes. It did not recover a game entry point. That experiment lacked a complete DOS/BIOS/hardware environment; its branches, interrupts and stopping point are not authoritative evidence of what happens during the working game launch. In particular, it does not yet establish a decompression algorithm, an anti-debugging mechanism, or an unpacked image size.
Consequence: following an on-disk linear disassembly as though the bytes remain unchanged would produce misleading function boundaries and references. We need an execution trace plus memory snapshots around the startup writes.
Fingerprints and build attribution
Whole-file Shannon entropy is about 7.889 bits/byte. Across 4 KiB windows it ranges approximately 7.55–7.90 bits/byte. There are very few convincing plaintext strings; printable runs mostly look incidental. These observations are consistent with packed/encoded contents, but entropy alone cannot distinguish compression, encryption, or mixed binary data.
Case-sensitive plaintext searches found none of the following markers:
DOS/4G DOS/32A CWSDPMI Borland Microsoft Watcom
PKLITE LZ90 LZ91 dlz DARKER BLASTER Miles AIL HMI DIGPAK
These are negative string-search results, not proof that the corresponding components are absent. A component's strings could be encoded, omitted, or stored elsewhere. Even the game's own name is absent in plaintext.
Deark recognized the MZ container but did not identify/extract a supported packed format in its automatic inspection. A reference review of common MS-DOS packer signatures did not yield a justified attribution. This was not an exhaustive application of every Detect It Easy rule.
Consequently, this pass does not support claims such as “Watcom C with DOS/4GW”, “Borland Pascal”, “Miles sound”, or “entirely handwritten assembly”. The small file size and custom-looking startup are not enough to choose among those possibilities. Any outer protection/packing stage may also obscure evidence of an otherwise conventional build.
Reference implementations consulted:
- Deark executable inspector and DIET handler.
- Detect It Easy MS-DOS signature rules.
- DOSBox-X DOS executable loader, useful for loader initialization, not proof of Darker's internal architecture.
Installation clues
DARKER.BAT contains:
X Lang=0 Mus=1
This establishes the intended executable and two supplied arguments. Their names suggest language and music settings; accepted values, defaults and exact effects remain to be confirmed in the recovered argument parser.
PLAY.BAT and JOGAR.BAT instead attempt to display AVISO.txt, pause, then
run X.EXE without arguments. AVISO.txt is absent from this directory. The
different wrappers suggest a repackaged installation, but do not establish that
the executable itself was modified.
The included READ.ME credits a Day 1 production, Jas.C.Brooke for writing,
Lyndon Brooke for artwork, and PC Music for music. These are credits, not
compiler or audio-driver signatures; “PC Music” must not be mistaken for an
identified playback library.
The five DARKER.00–.04 files total 5,523,083 bytes. Their names and sizes
make external game data plausible, but this pass has not decoded them or
established their record/container format. Only directory metadata and a short
header peek were used. DARKER.SAV is 6,600 bytes; its structure is also left
for a later pass.
Next investigation
- Reproduce the known-working launch in a debugger-capable DOS environment, retaining emulator version/configuration and the executable hash.
- Break at the DOS entry and watch writes to the loaded image, beginning with
image offset
0x9D. Record executed bytes rather than only original bytes. - Identify the transition from startup transformation to stable code and dump that memory with its segment/load-address information.
- Repeat strings, runtime and compiler fingerprinting on that recovered image. Trace DOS file opens to locate the archive reader before interpreting the external files. Map video, input, sound and memory interfaces from actual reachable call sites.
That will provide a sound basis for separating game logic, embedded tables, procedural generation and external assets. None of those boundaries is yet established by this initial container inspection.