These are notes made while disassembling the Windows XP Service Pack 3 bootsector (x86 real mode) code.
; This is the "Volume Boot Record".
; Also called "Windows bootstrap loader"/"Windows partition boot sector"
; The section before this is the MBR of the hard disk, next is NTLDR.
.686p
.mmx
.model flat
; Segment type: Pure code
BOOT_SECTOR segment byte public 'CODE' use16
assume cs:BOOT_SECTOR
;org 7C00h
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
; =============== S U B R O U T I N E =======================================
; DL = 0x82
; drive number on Bochs XP
; Attributes: noreturn
public start
start proc far ; CODE XREF: debug002:20101J
; DATA XREF: BOOT_SECTOR:7CDEo
jmp short near ptr sub_7C54 ; jump over BPB to beginning of code
start endp
; ---------------------------------------------------------------------------
db 90h
db 4Eh, 54h ; 8-byte OEM ID or System Name ("NTFS ")
db 46h
db 53h
db 20h
db 20h, 20h, 20h,
; Start of NTFS BPB (Bios Parameter Block) (size 54h inclusive)
; REMEMBER THIS IS A MEMORY SNAPSHOT, THUS VALUES ARE IN LITTLE ENDIAN, whereas on disk
; it's big endian
dw 0002h, ; Sector Size (in bytes) (512)
db 8, ; Sectors per Cluster
; Number of sectors per allocation unit.
; 512 * 8 = 4096 (4kb cluster size)
; value is 4 if partition < 2GB
byte_7C0E dw 0000 ; DATA XREF: sub_7C54+18w
; Reserved Sectors (on disk)
; After loading into memory:
; stores number of sectors of Boot Record read into memory
; begins with value of 0x10 (16) and counts down to zero
dword_7C10 dd 0 ; DATA XREF: BOOT_SECTOR:loc_7CCBr
byte_7C14 db 0 ; DATA XREF: sub_7CAA+18w
; BOOT_SECTOR:7CEBr ...
db 0F8h ; "Fixed Disk" Media Descriptor ID
dw 0000 ; Must be set to zero for NTFS volumes
; Originally for FAT 12/16 "Sectors per FAT" value
dw 3F00h ; Sectors per track (0x3F)
dw 0FF00h ; Number of Heads (sides) (0xFF)
dword_7C1C dd 3Fh ; DATA XREF: BOOT_SECTOR:7CCFr
; stored 3F 00 00 00 in memory
; number of "Hidden (Reserved) Sectors" (cylinder = head = 0)
; count of hidden sectors preceding partition that contains this volume
dword_7C20 dd 0 ; DATA XREF: sub_7C54+51w
; on disk:
; not used in NTFS
; Total Number of Sectors (FAT32)
; in memory:
; stores Total Number of Sectors in the partition we're trying to boot
; BOOT_SECTOR:7CD4r
byte_7C24 db 80h ; DATA XREF: sub_7C7Br sub_7CAA+5r ...
; First byte of four byte sequence indicates Drive Number (0x80)
; NTFS OS always fills these bytes with "80 00 80 00"
db 0
db 80h
db 0
db 89h ; Total Sectors (in the Volume)
db B6h ; 00000000007FB689 (long long word)
db 7Fh ; 1 sector less than total number to account for NTFS "Backup Sector"
db 0 ; "Backup Sector" not considered part of NTFS volume
db 0
db 0
db 0
db 0
; end of Total Sectors
db 0 ; Starting Cluster Number (for $MFT file in this partition)
db 0 ; 0000000000040000 (long long word)
db 4
db 0
db 0
db 0
db 0
db 0
; end of Starting Cluster Number
db 68h ; Starting Cluster Number (for $MFTMirror file in this partition)
db 0FBh ; 000000000007FB68 (long long word)
db 7
db 0
db 0
db 0
db 0
db 0
; end of Starting Cluster Number
db 0F6h ; Clusters (or bytes) per File Record Segment (FRS)
; signed byte (-10 -> 1024 bytes)
; Can be negative when cluster size (8) > MFT File Record Size
; From MS:
; "If this number is positive (up to 0x7F), it represents Clusters
; per MFT record.
; If the number is negative (0x80 to 0xFF), the size of the File Record
; is 2 raised to the absolute value of this number."
; 2 ^ (|-10|) = 2 ^ 10 = 1024 bytes
db 0, 0, 0 ; Unused by NTFS (3 bytes)
db 1 ; Clusters per Index Buffer
; signed byte
; Size of each index buffer, used to allocate space for NTFS structures
; such as directories
db 0, 0, 0 ; Unused by NTFS (3 bytes)
db 0B8h, 0F8h, 0FBh ; NTFS Volume Serial Number
db 0B4h, 1Ah, 0FCh, 0B4h, 0D8h ; [B4 FB] F8 B8
; D8 [B4 FC] 1A (long long word)
; the [] are different than thestarman's
dd 0 ; Unused by NTFS
; =============== S U B R O U T I N E =======================================
; Attributes: noreturn
sub_7C54 proc far ; CODE XREF: startj
; FUNCTION CHUNK AT 026A SIZE 00000199 BYTES
; FUNCTION CHUNK AT 7C81 SIZE 00000029 BYTES
cli ; disable maskable interrupts
xor ax, ax ; AX = 0
mov ss, ax ; SS = 0
assume es:debug002, ss:debug002, ds:debug002 ; inaccurate and wtf is assume?
mov sp, 7C00h ; SP = 0x7C00
sti ; enable interrupts
mov ax, 7C0h ; AX = 0x07C0
mov ds, ax ; DS = 0x07C0
assume ds:nothing
call sub_7C7B ; drive number (80)
; stores total sectors of boot partition in BPB offset 20h
mov ax, 0D00h ; 0000:7C65, AX = 0x0D00
mov es, ax ; ES = 0x0D00
assume es:nothing
xor bx, bx ; BX = 0
mov byte ptr ds:0Eh, 10h ; *(BYTE *)07C0:000E (0x7C0E) = 0x10
call loc_7CC7 ; reads all boot sectors in memory beginning at 0D00:0000 and ending
; at 0F00:0000
; ---------------------------------------------------------------------------
push 0D00h
push 26Ah
retf ; returns to 0D00:026A or 0xD26A
sub_7C54 endp ; sp-analysis failed
; =============== S U B R O U T I N E =======================================
; drive number (80)
; sub_7C7B is _CalculateTotalSectorsBootPartition
; Calculates total sectors in the boot partition using only CHS
sub_7C7B proc near ; CODE XREF: sub_7C54+Ep
mov dl, ds:24h ; 07C0:0024 = 0x80 (DX = 0x0180)
; DS:0000 is pointer to start of BPB
; 0x80 is the first hard drive (offset 24h of BPB)
mov ah, 8 ; AX = 0x08C0
sub_7C7B endp ; sp-analysis failed
; START OF FUNCTION CHUNK FOR sub_7C54
int 13h ; DISK - DISK - GET CURRENT DRIVE PARAMETERS (XT,AT,XT286,CONV,PS)
; DL = drive number
; Return: CF set on error, AH = status code (0), BL = drive type (0)
; DL = number of consecutive drives on the system (1)
; DH = maximum head number (0xFE)
; ES:DI -> drive parameter [ EB 52 90 4E 54 46 53 20 20 20 20 ]
; Disk Base Table
; CX = 0x09BF
; CH = low 8 bits of maximum cylinder number
; CL (bits 7 - 6) = high 2 bits of maximum cylinder number
; CL (bits 5 - 0) = maximum sector number
; Returns CF = 0
jnb short loc_7C8A ; jump if successful
mov cx, 0FFFFh ; Not successful? Set CX = 0xFFFF and DH = 0xFF
mov dh, cl ; Results in count of 0x00FC0000 or 16515072 sectors being placed into
; [0000:7C20] and following
loc_7C8A: ; CODE XREF: sub_7C54+2Fj
movzx eax, dh ; EAX = 0x000000FE
inc ax ; AX = 0x00FF (total heads)
movzx edx, cl ; EDX = 0x000000BF (maximum sector number)
and dl, 3Fh ; DX = 0x003F (0x3F is maximum number of heads)
mul dx ; head number * max number of sectors
; 0x3F * 0xFF
; DX = 0
; AX = 0x3EC1
xchg cl, ch ; CH = 0xBF
; CL = 0x09 (low 8 bits of maximum cylinder number)
shr ch, 6 ; CX = 0x0209 (shift bits 7-6 of CH next to CL)
; CX now equals the maximum cylinder number
inc cx ; CX = 0x020A (total cylinders)
movzx ecx, cx ; ECX = 0x0000020A
mul ecx ; 20A * 3EC1
; EDX = 0
; EAX = 0x007FF58A (total number of sectors)
mov ds:20h, eax ; 07C0:0020 (7C20) = 8A F5 7F 00 (little endian)
; stores total number of sectors in offset 20h of BPB
retn ; returns to 7C65 (07C0:0065)
; END OF FUNCTION CHUNK FOR sub_7C54
; =============== S U B R O U T I N E =======================================
; sub_7CAA is _TestInt13Extensions
sub_7CAA proc near ; CODE XREF: BOOT_SECTOR:7CF4p
mov ah, 41h ; function 0x41
mov bx, 55AAh ; has to be 0x55AA for function
mov dl, ds:24h
int 13h ; DISK - Check for INT 13h Extensions
; BX = 55AAh, DL = drive number
; Return: CF set if not supported
; AH = extensions version
; BX = AA55h
; CX = Interface support bit map
jb short locret_7CC6 ; jump if failed
cmp bx, 0AA55h
jnz short locret_7CC6 ; jump if failed
test cl, 1
jz short locret_7CC6 ; test if "Device Access using the packet structure"
inc byte ptr ds:14h ; increase high-byte of small sectors count for FAT12/16
locret_7CC6: ; CODE XREF: sub_7CAA+Bj sub_7CAA+11j ...
retn
sub_7CAA endp
; ---------------------------------------------------------------------------
; loc_7CC7 is _ReadAllBootSectors
; stores all boot sectors (16) into memory at 0D00:000
loc_7CC7: ; CODE XREF: sub_7C54+1Dp
pushad ; save all (double) general-purpose registers
push ds ; *SP = 0x07C0 (pointer to BPB offset 0)
push es ; *SP = 0x0D00 (pointer to RAM location)
loc_7CCB: ; CODE XREF: BOOT_SECTOR:7D58j
mov eax, ds:10h ; EAX = 0x00000000 (for NTFS, always 0)
add eax, ds:1Ch ; EAX = 0x0000003F (number of "Hidden Sectors")
cmp eax, ds:20h ; 0x3F < 0x7FF58A (Logical Block Addressing)
; This is making sure we have at least a full "track" (63 sectors).
; We can't have more "Hidden Sectors" than the total number of sectors!
jb loc_7D17 ; jump on success
push ds
push large (offset start - offset start) ; DMA page register 74LS612:
; Channel 7 (address bits 17-23)
push eax
push es
push bx
push large 10010h ; DMA page register 74LS612:
; Channel 7 (address bits 17-23)
cmp byte ptr ds:14h, 0 ; Not used on NTFS/FAT32 (0),
; but it's the high-byte of the "small sectors count" for FAT12/16
jnz loc_7D00
call sub_7CAA ; check for extended Int13 code
cmp byte ptr ds:14h, 0
loc_7CFC:
jz loc_7D61 ; jumps if extensions are NOT installed ("A disk read error")
loc_7D00: ; CODE XREF: BOOT_SECTOR:7CF0j
mov ah, 42h ; 'B'
mov dl, ds:24h
push ss
pop ds
assume ds:debug002
mov si, sp
int 13h ; DISK - IBM/MS Extension - EXTENDED READ (DL - drive, DS:SI - disk address packet)
pop eax
pop bx
pop es
assume es:nothing
pop eax
pop eax
pop ds
assume ds:nothing
jmp short loc_7D44
; ---------------------------------------------------------------------------
; This function will read in the first 16 sectors of the primary drive into RAM starting
; at 0D00:0000 to 0F00:0000 (exclusive).
; I believe what is read is the "Initial Program Loader."
; loc_7D17 is _ReadFirstSectorOfSecondHead
loc_7D17: ; CODE XREF: BOOT_SECTOR:7CD9j
xor edx, edx ; EDX = 0
movzx ecx, word ptr ds:18h ; ECX = 0x0000003F (sectors per track) offset 18h from BPB
div ecx ; 0x3F (sectors per track) / 0x3F (number of "hidden sectors")
; DX = 0x0000
; AX = 0x0001
inc dl ; DX = 0x0001
mov cl, dl ; CX = 0x0001
mov edx, eax ; EDX = 0x00000001
shr edx, 10h ; EDX = 0x00000000 (align remainder on 512 boundary)
div word ptr ds:1Ah ; 0x00FF (number of heads/sides) / 0x01
; 1000000000b = 512 = 0x200
; DX = 0x0001
; AX = 0x0000 ?
xchg dl, dh ; DH = 0x01 (head = 0x1)
; DL = 0x00 (drive 0)
mov dl, ds:24h ; DX = 0x0180 (80 is drive number)
mov ch, al ; CX = 0x0001 (track = 0, sector = 1)
shl ah, 6 ; AX = 0
; calculates the cube of AH, assuming AH <= 2 (10b)
or cl, ah ; sets CL only if it is 1 (which it is)
mov ax, 201h ; AX = 0x0201 (AH = function, AL = sectors to read)
int 13h ; DISK - READ SECTORS INTO MEMORY
; AL = number of sectors to read (max 512 bytes (0x200)), CH = track, CL = sector
; DH = head, DL = drive, ES:BX -> buffer to fill (0D00:0000) (D000)
; Return: CF set on error, AH = status (0), AL = number of sectors read (1)
; Returns CF = 0
loc_7D44: ; CODE XREF: BOOT_SECTOR:7D15j
jb loc_7D61 ; jump if failed ("A disk read error")
mov ax, es ; AX = 0x0D00
add ax, 20h ; ' ' ; AX = 0x0D20
; adds 0x200 (512) bytes to (0D00:0000) or (0xD000) offset
mov es, ax ; ES = 0x0D20 (0D20:xxxx) or (0xD200)
assume es:nothing
inc dword ptr ds:10h; *(DWORD* ) 07C0:0010 (0x7C10) = 0x1
; Field that apparently is used to count how many sectors were read
; BPB offset 10h
dec word ptr ds:0Eh ; *(WORD* ) 07C0:000E (0x7C0E) = 0x000F
; BPB offset 0Eh is count of how many sectors are left to read
; AF = 1, PF = 1
jnz loc_7CCB ; jumps if 0x7C0E is not zero (now)
; loops for 16 (0x10) times
pop es ; restore original ES (0D00)
assume es:nothing
pop ds ; restore original DS (07C0), wasn't modified
popad ; restore all (double) registers
retn ; return to 0000:7C74 (assuming everything just worked)
; ---------------------------------------------------------------------------
; loc_7D61 is _SomethingFailedRtn
loc_7D61: ; CODE XREF: BOOT_SECTOR:loc_7CFCj
; BOOT_SECTOR:loc_7D44j
mov al, ds:1F8h ; pointer value for message area ("A disk read error occurred")
call sub_7D70
; ---------------------------------------------------------------------------
mov al, ds:1FBh
call sub_7D70
; ---------------------------------------------------------------------------
db 0FBh
; ---------------------------------------------------------------------------
loc_7D6E: ; CODE XREF: BOOT_SECTOR:loc_7D6Ej
jmp short loc_7D6E ; endless loop
; =============== S U B R O U T I N E =======================================
; Attributes: noreturn
sub_7D70 proc near ; CODE XREF: BOOT_SECTOR:7D64p
; BOOT_SECTOR:7D6Ap
mov ah, 1
lodsb ; CODE XREF: BOOT_SECTOR:7D80j
cmp al, 0 ; check for NULL byte
jz short locret_7D82 ; jump if done printing message
mov ah, 0Eh ; teletype output, one char at a time
mov bx, 7
int 10h ; - VIDEO - WRITE CHARACTER AND ADVANCE CURSOR (TTY WRITE)
; AL = character, BH = display page (alpha modes)
; BL = foreground color (graphics modes)
jmp short loc_7D74 ; continue printing while character exists
; ---------------------------------------------------------------------------
locret_7D82: ; CODE XREF: BOOT_SECTOR:7D77j
retn
; ---------------------------------------------------------------------------
aADiskReadErrorOccurred db 0Dh,0Ah
db 'A disk read error occurred',0
aNtldrIsMissing db 0Dh,0Ah
db 'NTLDR is missing',0
db 0Dh, 0Ah, 4Eh, 54h, 4Ch
dd 69205244h ; disk signature
aSCompressed db 's compressed',0
aPressCtrlAltDelToResta db 0Dh,0Ah
db 'Press Ctrl+Alt+Del to restart',0Dh,0Ah,0
align 8
byte_7DF0 db 0, 0, 0, 0, 0, 0, 0, 0, 83h, 0A0h, 0B3h, 0C9h, 0, 0
word_7DFE dw 0AA55h ; DATA XREF: sub_61B:loc_681r
BOOT_SECTOR ends
; ===========================================================================
; Segment type: Pure data
; Segment permissions: Read/Write
debug002 segment byte public 'DATA' use16
assume cs:debug002
;org 7E00h
; =============== S U B R O U T I N E =======================================
; F000:FFF0
; First instruction on all BIOS chips.
; Hard coded into all processors.
; Attributes: thunk
PowerOnResetVector proc near
jmp far ptr loc_FE05B
PowerOnResetVector endp
; ---------------------------------------------------------------------------
db 30h ; 0
db 38h ; 8
db 2Fh ; /
db 32h ; 2
db 31h ; 1
db 2Fh ; /
db 31h ; 1
db 32h ; 2
db 0
db 0FCh ; �
db 80h ; �
BIOS ends
; ===========================================================================