how a from-scratch PC emulator learned to boot Windows 2000

CHAPTER 5 · THE TWIST

THE INVISIBLE DIALOG

Chapter 4 ended with a machine that was provably waiting and demonstrably not asleep. The resolution was not another fix. It was a single injected Enter key, a one-bit X-ray of video memory, and a confession: the world's most heavily instrumented Windows install had been running without the install CD.

1
Enter key, injected at capture 15 — “deadlocked” setup woke instantly
5,757 B
the byte-identical black PNG that every screenshot of the wall recorded
A000:0000
VGA plane memory — where the hidden wizard page was still drawn
778CF4FB
the only instruction trigger that fired in capture 18 — SetupPromptForDiskW
03–19
the captures run with no CD attached — both PIIX3 “verdict” runs among them
0xEA76
the SP4 message resource naming the second prompt: “The CD Key you entered is not valid.”

Where things stood

By the small hours of 16 July the census (chapter 4) had said everything it could say: eight processes, seventy-one threads, every one of them Waiting, zero runnable, kernel stacks paged out, the CPU parked in mddosem's emulation of the idle loop — while the guest went on making protected-mode stack transitions at about eighteen a second, so the machine was demonstrably not asleep. Three real emulation fixes had landed in a single night — the interrupt shadow, the parked timer (both chapter 6), and the PIIX3 status latch (chapter 3) — and capture 11 proved the wall had survived all three. The screen showed what every capture since the campaign began had recorded: the same byte-identical 5,757-byte black PNG — a frame that would go on recurring for four days. Nothing was crashing. Nothing was lost. Everything, everywhere, was waiting.

One keystroke

Capture 15 was the experiment nobody had thought to run. Wait for setup to reach the fully out-swapped terminal state — threads parked, kernel stacks paged out, the census signature stable — then inject a single Enter key through the emulator's control pipe. Setup immediately became active. The journal draws the conclusion in one line: the wait was a modal dialog, not a lost kernel wakeup.

A modal dialog is Windows at its most patient. The thread that owns it stops doing work and sits in a message loop, blocked until input arrives; from the kernel's side that is an ordinary event wait, indistinguishable from a deadlock — because the producer that will eventually signal it is a human being with a keyboard. The census had been perfectly correct: everyone really was waiting, and nothing really was going to wake them. It had misread only one thing — who they were waiting for. Three weeks of kernel-level forensics, ended by a keystroke.

The X-ray

But a dialog for whom? The screen was black — had been black in every capture that reached the wall. The way past that contradiction was to stop trusting the screen. In the VGA's 16-colour graphics modes the picture does not live in a tidy byte-per-pixel framebuffer: it lives in four parallel bit-planes, overlaid at the same guest addresses starting at A000:0000, one bit of each pixel in each plane. Whatever the emulator's renderer was putting on the visible display, the plane memory underneath could still be read directly — and reconstructed at one bit per pixel into a crude black-and-white image of whatever had been drawn, whether or not it was being shown.

The decode was startling. Behind the darkness, GUI Setup was alive and mid-conversation: a wizard page titled Your Product Key, the CD-case illustration with Microsoft's generic placeholder sticker art, the instruction about the 25-character key on the yellow sticker, < Back and Next > buttons — and, overlapping it, an error box reading “The CD Key you entered is not valid.” The frame recreated below dates from the following evening, after the campaign had learned where to look, and it portrays the second of the two prompts this chapter uncovers — but it is the definitive portrait of the wall: Setup politely asking questions into a void.

THE X-RAY — WHAT THE MONITOR SHOWED vs WHAT MEMORY HELD drag the slider · 0% framebuffer · 100% plane memory
framebuffer plane memory 0%
Recreation in HTML/CSS; the original is capture-24/a000-1bpp.png (18,729 bytes, 16 July 21:35) in the run archive — a 1-bit-per-pixel decode of VGA plane memory at A000:0000, taken while the visible screen was black. No product key appears in the original or here: the illustration on the real page is Microsoft's generic placeholder art.

Naming the dialog

A keystroke proves a dialog; forensics demands to know which. Two instrument repairs made that possible. mddosem's time-travel debugger can arm instruction triggers — watchpoints that fire when execution reaches a chosen CS:EIP — but one interpreter path compared only the low sixteen bits of the instruction pointer, so a trigger set on an NT kernel-mode address like 0x804xxxxx could never match. The fix, Cpu::instruction_offset(), selects the full 32-bit EIP whenever the code segment is 32-bit and the truncated IP only for genuine 16-bit segments. And trigger logging was made side-effect-free: on firing, it records the registers and the stack frame, and where a frame slot points at readable UTF-16 it logs an escaped preview of the string — all through a debug translation path that sets no Accessed bits and pollutes no TLB. A debugger should read guest memory the way a bus analyser would: without leaving fingerprints.

Static cross-references in the exact SP4 build of setupapi.dll suggested three code paths that could raise a blocking dialog — a failed-verification prompt, a copy-error dialog, and a missing-media prompt. Triggers went on all three; capture 18 ran to the wall. Exactly one fired: 001B:778CF4FB, the internal implementation of the exported SetupPromptForDiskW. The other two never executed. Capture 19 then logged the live call frame — the decisive evidence of the whole campaign:

capture 19 · time-machine log trigger @ 001B:778CF4FB · setupapi!SetupPromptForDiskW (internal)
  DiskName         "Service Pack 4 CD"
  PathToSource     "D:\I386"
  FileSought       "NT5INF.CAT"
  TagFile          NULL
  DiskPromptStyle  0x105

Field values as recorded in the capture-19 trigger log; layout stylised. The journal's reading of the frame: a normal request for missing installation media — not a signature failure, not a copy error. Windows was not crashing, not corrupting, not deadlocked. It wanted its CD back, and it had asked in the politest way it knew: a dialog box, on a screen nobody could see.

The confession

Why was there no CD? Because the harness had removed it. The original install driver, drive-install.sh, always attached the SP4 ISO to the emulated CD-ROM drive. The capture harness — rebuilt for the forensic campaign, with its hash-verified fresh disk copies and its census watchers — did not: captures 03 through 19 omitted --cdrom. The guest was never stuck. It was waiting, politely and invisibly, for a disc that nobody had inserted, on hardware that faithfully reported an empty drive.

The sting in the tail: both post-PIIX3 “verdict” runs were among the CD-less captures. The journal is unsparing — those runs “cannot decide whether the CPU/PIT/IDE fixes complete setup; Windows correctly prompted for the absent source.” Every retest since capture 03 had been answering a different question from the one being asked.

THE PAPER TRAIL — FROM KEYSTROKE TO CONFESSION step with the buttons, or ← → when the list has focus
  1. 16 JUL · CAPTURE 11The wall survives fix three With the reviewed PIIX3 latch landed, setup still parks: black screen, three threads Waiting, kernel stacks out-swapped.
  2. 16 JUL · CAPTURE 15One Enter key Injected at the fully out-swapped state — setup wakes instantly. A modal dialog, not a lost kernel wakeup.
  3. 16 JUL · INSTRUMENTSThe triggers learn 32-bit addresses Full-EIP trigger matching (Cpu::instruction_offset()) and side-effect-free frame logging with escaped UTF-16 previews.
  4. 16 JUL · CAPTURE 18One trigger fires Of three candidate dialog paths in setupapi.dll, only 001B:778CF4FB executes — SetupPromptForDiskW's internal implementation.
  5. 16 JUL · CAPTURE 19The frame decoded DiskName “Service Pack 4 CD” · PathToSource D:\I386 · FileSought NT5INF.CAT · DiskPromptStyle 0x105 — routine missing media.
  6. 16 JUL · THE AUDITThe confession drive-install.sh had always attached the ISO; captures 03–19 omitted --cdrom. Windows was right all along.
  7. 16 JUL · CAPTURE 20The CD returns Straight past the three-thread wall; setup grows from four threads to eight; real progress — until a second invisible page.
Sequence and quoted field values from the 15–16 July engineering journals and capture logs (bug ledger MDD-BUG-KILN-00004). Evidence pointers: how we know.

The second invisible page

Capture 20 restored the ISO as drive D:. Setup went straight past the old three-thread wall, grew from four threads to eight, and alternated between installer work and service-control traffic — the first real forward progress at this phase in weeks. Then it parked again, black again: nine processes now, eighty-four threads, setup holding six Waiting threads with their stacks paged out. Capture 21 repeated the keystroke experiment, and once again a single Enter made setup runnable. Another invisible modal — but this time the media prompt stayed silent: SetupPromptForDiskW's fire count was zero.

So the probes went generic: triggers on USER32's own dialog machinery, the functions any Windows program must call to put a box on screen. MessageBoxW at 0x77E36AB3 fired, called from a return address inside syssetup.dll at 0x67015998; static disassembly just above the call site showed it raising message resource 0xEA76 with the error icon, and the SP4 message table decodes 0xEA76 to “The CD Key you entered is not valid.” EndDialog fired once as the injected Enter dismissed the error. The guest was sitting at an invisible Product Key wizard page — the page the X-ray above portrays — rejecting what it had been given and asking, unseen, for another try.

Entering a secret politely

The next problem was not emulation at all. Someone had to type a product key into a machine that could not display its own key page — through tooling that logs everything it touches. The existing remote-keystroke command, ctl key TEXT, put its payload in the client's process command line and shell history: unusable for a secret. So the emulator gained ctl key --stdin, which reads the keystroke text from standard input and feeds the unchanged keyboard-injection path. Arthur typed the key into a hidden PowerShell secure prompt (Read-Host -AsSecureString) that piped it through stdin and zeroed the temporary buffer. The journal states the boundary plainly: the agent “must not read, print, log, or transmit the key.” No key value entered a command line, a log file, a journal, or a chat transcript — and none enters this site.

The correction of 18 July

The first key-entry experiment was recorded as a success — setup, the journal initially said, rejected one candidate key and accepted another. Two days later that claim was formally withdrawn, and the withdrawal is worth telling with the same respect as the fixes. Three faults were found in the procedure, none in the emulator. The ctl scancodes command parsed digit-only tokens as decimal, so chord sequences written as hex had never sent the intended keystrokes — explicit 0x prefixes were required. The SP4 Product Key dialog turned out to own five edit controls, each limited to five characters and auto-advancing to the next — so the procedure's deliberate Tab between groups had silently skipped every other field. And the key the successful QEMU reference install had actually used matched neither key that had been tried. An experiment whose input procedure is broken proves nothing, and the journal says so in plain terms: claim withdrawn.

Capture 26 redid the entry properly — a cold boot of a hash-verified copy of the capture-24 disk, the QEMU-proven key, explicit-hex chords, five clean five-character groups, no hyphens, no Tabs, Enter. Instrument honesty is a running theme of this story: the same campaign that retracted its own census (chapter 4) also retracted its own success.

With the CD in the drive and the key entered by a procedure proven against the oracle, native setup ran its full course for the first time — chapter 7 tells that night minute by minute. But none of this motion was free. For the machine to reach these dialogs at all — and to survive what came after them — four instruction-level truths had to hold, and chapter 6 is about those.

Sources for this chapter — the 15 July master journal for the boot-to-desktop goal (section: “the terminal wait was an invisible media prompt”) and the 16–18 July entries, bug ledger MDD-BUG-KILN-00004; the capture archive (captures 11–26), including capture-24/a000-1bpp.png and the byte-identical 5,757-byte wall frame; the commit record for the time-machine trigger fixes and ctl key --stdin. Full evidence table: how we know, under mddosem.