2013.07.28

Recently I came across an old recovery disk for my parents’ old 486 from 1993 (the first machine I really seriously programmed — the Tandy before it got some QBasic time, but nothing as crazy as a C compiler. Some real mode assembly though once or twice). I managed to cobble things together to get it booting in VirtualBox (you see, in those days booting from CD didn’t exist, so the CD itself isn’t bootable, it just has a bunch of zips that were written to floppies by an operational system — getting that up and running is a post for another day, but it required hexediting the virtual harddrive to lay down IO.SYS and MSDOS.SYS properly in FAT16, whee).

Anyway, after it was up and running, I figured it might be fun to start tearing it apart (since the entire machine state fits in RAM (4MB RAM, 512MB harddrive, 527MB restore CD, a couple 1.4MB boot floppies) it’s easy to blow it away and restore it very rapidly.

So, let’s start from the beginning, the Master Boot Record. This the MBR laid down by DOS 6. Before this there’s the BIOS, but unfortunately I don’t have access to that (otherwise, it’d be a fun but much more complex piece of software to explore), and the MBR’s really tiny (just 512 bytes, of which only like 446 or so are executable code).

Here’s the Hex for a DOS 6 MBR on a ~512MB harddrive with just 1 “primary DOS partition”.

55AA at the end indicates a “valid” boot sector (sort of). The long stream of zeros mid-way through starts the unused bit, and the stuff just before the end is the Partition Table (we’ll ignore that for now — look it up on wikipedia if you’re curious).

So we see a couple strings in there: “Invalid partition table” and “Error loading operating system” and “Missing operating system”. Those three cover about a third of the bytes. Now let’s look at the disassembly.

For reference, the first string starts at 0x8C (140) bytes, and after the strings it looks like it’s just zeros until the partition table, so we’ll focus our disassembly efforts on the first 0x8B (139) bytes. Also, the BIOS loads the MBR at 0x7C00, so we’ll tell the disassembler to pretend like the code is at that location in memory. It also starts in Real Mode (1MB addressable, segmented, no virtual memory, dark ages stuff). Here we go.

Ok, so just a few instructions in, we’ve got some basic but useful stuff going on. Initially it zeros out all the segment registers (ss, es, ds, and cs) because the BIOS could leave them weird, it sets up the stack at 0x7c00 (just under the MBR), and then it copies 256 words (512 bytes) from 0x7c00 to 0x600 (this copies the MBR from 0x7c00 to 0x600). Then we jump to 0x61d (notice how the next instruction is at address 0x7C1D — that’s 0x1D bytes after the beginning of the MBR. 0x61d is 0x1d bytes beyond the start of the copied MBR, so basically that jump jumps to the next instruction, but at a different location in memory). It’s common for MBRs to copy themselves elsewhere, and then load stuff at 0x7c00 (this is why they move themselves out of the way). So now let’s disassemble it again, but with the assembler thinking it’s at 0x600 (since that’s where the remainder of the code will be executing).

This time, there’s a lot of flow control, so I’ve interjected a lot of commentary in-line

So there we have it. 139 bytes of code, and it relocates itself to 0x600, checks the partition table, if it’s good it loads the first sector of the active partition to 0x7c00, and then jumps to it, otherwise it prints error messages. It has some retries and some sanity checks, but otherwise it’s pretty straightforward. Next time we’ll check out the sector it loads and see what that does.