# HAVOC_NOCD.EXE — Binary Patches (run on modern Windows without CD) `HAVOC_NOCD.EXE` is a patched copy of the original `HAVOC.EXE` (485,376 bytes, 32-bit PE i386, DirectDraw + DirectSound). The patches let it run from a hard-drive install on Windows 11 via [dgVoodoo2](http://dege.freeweb.hu/) (DirectDraw wrapper, windowed mode), with no CD mounted and no admin rights. Do **not** commit `HAVOC_NOCD.EXE` or the original `HAVOC.EXE` (copyright). ## Section layout / file-offset (FO) to virtual-address (VA) mapping | Section | Raw FO start | RVA | FO → VA formula | |---|---|---|---| | `.text` | 0x00400 | 0x01000 | `VA = FO + 0x400C00` | | `.data` | 0x69400 | 0x7C000 | `VA = FO + 0x412C00` | | `.rdata` | 0x67000 | 0x79000 | `VA = FO - 0x67000 + 0x79000 + 0x400000` | | `.idata` | 0x6EC00 | 0x82000 | `VA = FO + 0x413400` | `.text` VirtualSize was extended from 0x66bbd to 0x66c00 (PE header FO 0x180) so the Patch-V code cave at VA 0x467bce is mapped into memory. ## Patch table | FO | VA | Bytes (→) | Purpose | |---|---|---|---| | 0x00180 | (PE hdr) | `bd6b`→`006c` | Extend `.text` VirtualSize to cover Patch-V cave | | 0x094fd | 0x40a0fd | `66 b8 01 00`→`66 33 c0 90` | **Patch S**: CreateSoundBuffer fail returns AX=0 (continue w/o sound) | | 0x20277 | 0x420e77 | `75`→`eb` | Force a Jcc branch (skip) | | 0x29613 | 0x42a213 | `8844242c`→`90909090` | **Patch D**: NOP drive-letter overwrite (INTRFACE) | | 0x29623 | 0x42a223 | `88442410`→`90909090` | **Patch D**: NOP drive-letter overwrite (MUSIC) | | 0x296cc | 0x42a2cc | `7c`→`eb` | **Patch Q**: skip check | | 0x29b02 | 0x42a702 | 18 bytes → NOP | Remove drive-type compare/branches | | 0x29b38 | 0x42a738 | `75`→`eb` | Drive-check skip | | 0x29b80 | 0x42a780 | `74`→`eb` | CD-check skip | | 0x29bb0 | 0x42a7b0 | `81ec6c010000`→`b001c39090 90` | NOCD stub = `MOV AL,1; RET` | | 0x29d63 | 0x42a963 | `75`→`eb` | Extra CD skip | | 0x29e10 | 0x42aa10 | `74`→`eb` | CD skip 2 | | **0x29a2b** | **0x42a62b** | `8b442414 85c0 8b442410 752a 85c0` → `8b442410 85c0 754e 8b442414 eb48` | **Copy-protection / GRAFIX fix** (see below) | | 0x2cd70 | 0x42d970 | `81`→`c3` | **Patch N3**: `show_error_dialog` returns immediately (silence popups) | | 0x2cf2e | 0x42db2e | `74`→`eb 16` | Corrupt/unavailable bypass | | 0x30393 | 0x430f93 | `e888c9ffff`→NOP | NOP a validation CALL | | 0x42514 | 0x443114 | `b8010000`→`e9b54a02` | **Patch V**: JMP to vtable-setter code cave | | 0x66fce | 0x467bce | cave | **Patch V** cave: sets vtable=1, returns EAX=1 | | 0x6b27c | 0x47de7c | `C:\MUSIC.FF`→`MUSIC.FF` | Strip drive prefix | | 0x6b288 | 0x47de88 | `C:\INTRFACE.FF`→`INTRFACE.FF` | Strip drive prefix | | 0x6b2b8 | 0x47deb8 | `C:\WORLDS\`→`WORLDS\` | Strip drive prefix | | 0x6b390 | 0x47df90 | `C:\WORLDS\LAND0101.DAT`→`WORLDS\...` | Strip drive prefix | | 0x6b3a8 | 0x47dfa8 | `C:\HAVOCDAT.DAT`→`HAVOCDAT.DAT` | Strip drive prefix | | 0x6b3b8 | 0x47dfb8 | `C:\BIGFILE.DAT`→`BIGFILE.DAT` | Strip drive prefix | ## The copy-protection / GRAFIX crash (the "black window then closes" bug) **Symptom:** dgVoodoo window appears for ~1 second, all black, then the process disappears. **Actual cause:** an ACCESS_VIOLATION (NULL-pointer read) at VA **0x444E6A** during init — found by parsing the WER minidump in `%LOCALAPPDATA%\CrashDumps` (the crash call stack shows `0x434073` → `0x407CCB`, i.e. inside `WinMain → 0x434040 init`). The WORLDS\ data-file loader `0x42a480` opens each file (`GRAFIX00-03.DAT`, `LAND*`, `MAP*`, `STUF*`) from **two** locations: the local `WORLDS\` path and a path built from the CD drive letter in `HAVOC.INI` (`[SETUP] DRIVE=D:\`). Its return logic returns a valid stream **only when the local open fails and the CD-drive open succeeds** — a copy-protection check that assumes data lives on the CD. With the files present locally and no `D:` drive, it returns NULL; the caller `0x444db0` (GRAFIX loader) dereferences that NULL without checking → crash. **Fix (FO 0x29a2b):** rewrite the return decision to prefer the successfully-opened local object: ``` 0x42a62b: mov eax,[esp+0x10] ; local WORLDS\ stream object 0x42a62f: test eax,eax 0x42a631: jnz 0x42a681 ; if local open OK, return it 0x42a633: mov eax,[esp+0x14] ; else return the drive object 0x42a637: jmp 0x42a681 ``` After this patch the game runs (window title "HAVOC(tm) by Reality Bytes", responsive, main loop executing) instead of crashing. ## Reproducing / testing The crash is launch-environment dependent (it reproduces when launched normally but not under a debugger). Reliable native run: ```bat :: run_probe.bat cd /d Z:\Development\devl\Havoc .\HAVOC_NOCD.EXE ``` Notes: - `NoDefaultCurrentDirectoryInExePath` may be set — invoke the exe with a `.\` prefix. - Launching via MSYS/Git-Bash segfaults (exit 139) and does not produce a WER dump; use `cmd`. - Minidumps can be parsed with plain Python (no cdb/windbg needed) — see the exception stream (type 6) for the faulting EIP and the memory streams (5/9) to walk the stack.