Windows 95 and 98
WINBOOT.SYS consists of three parts: the initial loader, the payload and the device configuration manager executable.
The initial loader comprises the first three (Windows 95 ‘OSR0’, OSR1) or four (Windows 95 OSR2 and later) 512-byte sectors of the file; the first sector begins with the signature
MZ, the second starts with the signature
BJ and the last ends with the signature
MS. The FAT boot sector puts the initial loader at real mode address
0070:0000, checks the first two of these signatures, then starts execution at address
0070:0200. The job of the loader is to copy boot sector variables into its data area (the first sector), relocate itself to a higher memory address, load the rest of the file starting at segment 0x70 again and then transfer control to
The loader is immediately followed by the payload, which is the DOS kernel proper, loaded as described above. Although it is loaded into a single contiguous region of memory, it is too large to fit into a single 64 KiB real-mode segment, and so the DOS kernel is further split into regions addressed using different segment bases; discovering those is up to the person performing the disassembly. Also, the real-mode kernel does not comprise the entirety of the rest of the file; the kernel image is additionally followed by the device configuration manager.
The device configuration manager (MSDCM) is a small executable whose purpose is to read the Registry and prompt the user to pick a hardware configuration if there are multiple configurations defined. As it turns out, it is no accident that
IO.SYS begins with the signature
MZ, just like DOS executables; the file is simultaneously a regular EXEPACK-compressed DOS executable, and the values in the MZ header at the beginning of the file allow it to be executed using interrupt 0x21 service 0x4b, which will load the third part of the file. If the real-mode kernel detects that it is booting a Windows installation, the kernel will do just that early in the boot process, before even
CONFIG.SYS is processed. In interactive boot, this step can be skipped by answering
N to the prompt ‘Process the system Registry [Enter=Y,Esc=N]?’; it is also controlled by the
SystemReg= setting in
In Windows Me, the
IO.SYS file has a similar structure, with the following differences:
- The device configuration manager has been integrated into the payload as a subroutine; it is no longer independently executable. The
MZ signature remains where it was, but the rest of the executable header is filled with dummy data.
- The payload may be compressed; this is indicated by the signature
CM. If the loader sees the payload starting with that signature, it looks for the end for the compressed stream and will then use the decompression routine located at a 16-byte-aligned offset right after the payload (also preceded by a signature
CM, and some headers). If the compression signature is not found, the payload is loaded directly, like in previous versions. The compression format itself is a rather unremarkable LZSS-ish stream split into chunks. Rudolph Loew’s IO8DCOMP can decompress the file in-place, or you may see how GRUB4DOS accomplishes it.
- The loader can be disassembled by loading it as a raw binary at address
0070:0000 and placing the entry point at
- The payload can be disassembled by cutting it out of the file, decompressing it (if compressed), loading it starting at segment 0x70, placing the entry point at the beginning of that segment (i.e. at
0070:0000), and discovering other segments by trial and error;
- The device configuration manager (before Millenium) can be disassembled by using an EXEPACK decompressor and disassembling the output as an MZ executable.
As it happens, the Windows Me ‘crippling’ code is located entirely within the initial loader; there is no need to decompress the file. In fact, taking a copy of
IO.SYS from the Emergency Boot Disk and overwriting its first four sectors with the initial loader from the hard disk’s copy of
IO.SYS would accomplish the same thing as applying the patch.