![]() |
Hard
drive won't boot? Lost partitions? Let us help! MBR & Boot Sector Repair |
NTFS Boot Sector
The NTFS partition boot sector is part of the NTFS metadata file $Boot, which includes the boot sector as its initial 512 bytes, followed by several sectors containing code which is called by the boot sector's IPL (initial program loader). The NTFS boot sector is very similar in structure to FAT partition boot sectors. It begins with a jump to the entry point of the boot loader code, followed by a data record, called the BIOS Paramter Block, which describes the geometry of the partition, followed by the IPL or initial program loader, a small program that locates, loads to memory, and then executes the partition's startup code.
HEX/ASCII dump of NTFS Boot Sector
Table - The BIOS Parameter Block
|
| Note: The values Clusters per FRS (File
Record Segment) and Clusters per Index will be negative
in the case where the sectors per cluster is greater than
size of the FRS or Index record. In the case described
here the value in the Clusters per FRS field is F6h or
-10. The actual size of the record is then calculated as
2 ^ (-1 * -10) or 1024 bytes. The IPL begins by determining whether or not the active partition begins outside the 8GB boundry. If it does, the IPL must use the BIOS LBA extensions to read from the disk. It uses CHS addressing otherwise (only the first 8GB of a hard disk can be addressed via CHS). Having the addressing mode, the IPL then reads the initial 16 sectors of $Boot, which includes a copy of the boot sector itself plus the extended part of the IPL, to memory at 0D00h:0000h, then continues execution at 0D00h:026Ah. The code in the extended part of the IPL has the job of locating and starting NTLDR. This requires reading the Master File Table to locate the root index (ie, root directory), finding the NTLDR entry in the root index, reading NTLDR's file record segment from the Master File Table, then loading NTLDR to memory and jumping into it. Problems Involving the Boot Sector The most common boot sector-related problem is an overwritten boot sector. NTFS partitions on Windows 2000 & XP systems have a backup boot sector stored in last sector of the partition, and the backup can be used to replace a damaged primary boot sector. For example, here is the procedure used to replace a damaged boot sector with BootMaster. Also, the Windows Recovery Console command FIXBOOT will replace the $Boot metadata file, inlcuding the boot sector. In rather rare cases both the boot sector and its backup are corrupted. Sometimes this situation is complicated by the presence of a FAT12 boot sector in the place of the NTFS boot sector. The presence of the FAT12 boot sector is a "feature" of FIXBOOT. It writes the FAT12 boot sector into place, apparently, when the partition is damaged in a way that prevents it from reliably writing an NTFS boot sector. At any rate, the boot sector must be rebuilt from scratch, regardless of whether or not the FAT12 boot sector is present. Our method for doing this is to install a boot sector template, then obtain and fill in the values which are given in red in the BIOS Parameter Block table above. The needed values are obtained from the BootMaster Diagnostics report and from a supplemental report created by the utility FINDMFT.EXE which we provide free for customers in those cases where it is needed. Note that rebuilding the boot sector in this way is not neccessarily sufficient to recover a partition that is internally damaged, but it is worth doing as a preparation for recovering files from the damaged partition. |
Disassembly of NTFS Partition Boot Sector For readers with an interest in technical details, here is a disassembly of the NTFS Boot Sector. I have disassembled the actual boot sector (the initial 512 bytes of $Boot), but not the extended IPL code which reads from the Master File Table in order to locate NTLDR in the partition's root directory. The boot sector begins with a jump over the BPB area to the IPL's entry point. 07C0:0000 jmp 052h ; jump to IPL entry point 07C0:0002 90h ; nop BIOS PARAMETER BLOCK 07C0:0003 db "NTFS " ; OEM 07C0:000B dw 0200h (512) ; Bytes per sector 07C0:000D db 08h ; Sectors per cluster 07C0:000E dw 0h ; Reserved Sectors 07C0:0010 db 0h DUP(3) ; undefined - always 0 07C0:0013 dw 0h ; not used 07C0:0015 db 0F8h ; Media Type 07C0:0016 dw 0h ; undefined - always 0 07C0:0018 dw 03Fh (63) ; Sectors per track 07C0:001A dw 0FFh (255) ; Heads 07C0:001C dd 03fh (63) ; Hidden Sectors 07C0:0020 dd 0h ; not used 07C0:0024 dd 80008000h ; not used 07C0:0028 qw 9C25FDh (10233341); Total Sectors 07C0:0030 qw 04h ; MFT start cluster 07C0:0038 qw 9C25Fh (639583) ; MFT mirror start cluster 07C0:0040 dd 0F6h ; Clusters per FRS 07C0:0044 dd 01h ; Clusters per index 07C0:0048 qw D83AEE5E98D83B12h ; Volume serial number 07C0:0050 dd 0h ; CRC IPL ENTRY POINT 07C0:0054 cli ; disable interrupts 07C0:0055 xor ax, ax ; ax = 0 07C0:0057 mov ss, ax ; stack segment = 0 07C0:0059 mov sp, 7C00h ; stack @ 0:7C00 07C0:005C sti ; enable interrupts 07C0:005D mov ax, 7C0h ; 07C0:0060 mov ds, ax ; data segment = 07C0 07C0:0062 call 007B ; calculate drive's total sectors 07C0:0065 mov ax, 0D00h ; 07C0:0068 mov es, ax ; es = 0D00h 07C0:006A xor bx, bx ; zero bx 07C0:006C mov b(ds:0Eh), 10h ; store read count index 07C0:0071 call 00C7 ; go read the disk JUMP INTO IPL EXTENSION 07C0:0074 push 0D00h ; segment for retf 07C0:0077 push 26Ah ; offset for retf 07C0:007A retf ; jump into extended IPL CALCULATE TOTAL SECTORS ON DRIVE ; Note that this routine uses the old interrupt 13, function 8 ("Get Drive
; Parameters"), which is limitied to a maximum cylinder count of 1023, so
; the total sector count will be 16450560 (8 GB) for drives larger than 8GB.
; The IPL uses this count to determine if the active partition begins
; beyond the 8GB point, in which case it will use it will use the interrupt
; 13 LBA extensions to read the drive. Otherwise, CHS addressing is used.
07C0:007B mov dl, ds:24h ; load BIOS drive number (80h) to dl 07C0:007F mov ah, 8 ; Function "Get Hard Drive Parameters" 07C0:0081 int 13h ; call BIOS disk service ;This call returns the hard disk's CHS parameters in registers cx and ;dh as follows: ; ch = low eight bits of maximum cylinder number ; cl = maximum sector number (bits 5-0) ; high two bits of maximum cylinder number (bits 7-6) ; dh = maximum head number ; ;Note that the maximum cylinders this call can return is 1023. 07C0:0083 jnb 07C0:008A ; jump if no error 07C0:0085 mov cx, 0FFFFh ; use dummy values for the calculation 07C0:0088 mov dh, cl ; below if an error occurs 07C0:008A movzx eax, dh ; max head number to eax...
07C0:008E inc ax ; and increment
07C0:008F movzx edx, cl ; max sector number and high 2 bits
; of max cylinder number
07C0:0093 and dl, 3Fh ; mask off high 2 - dx now has
; max sector number
07C0:0096 mul dx ; multiply (max sectors) * (max heads)
07C0:0098 xchg cl, ch ; get maximum...
07C0:009A shr ch, 6 ; cylinder number...
07C0:009D inc cx ; in...
07C0:009E movzx ecx, cx ; ecx
07C0:00A2 mul ecx ; result will be drive's total sectors
07C0:00A5 mov ds:20h, eax ; store the value in the BPB area
07C0:00A9 retn ; return
TEST FOR INTERRUPT 13 LBA EXTENSIONS SUPPORT IN BIOS 07C0:00AA mov ah, 41h ; int13 Extensions Installation check 07C0:00AC mov bx, 55AAh ; test pattern in 07C0:00AF mov dl, ds:24h ; BIOS number of hard drive 07C0:00B3 int 13h ; do interrupt 07C0:00B5 jb 07C0:00C6 ; jump if error 07C0:00B7 cmp bx, 0AA55h ; test pattern out 07C0:00BB jnz 07C0:00C6 ; jump if pattern miscompare 07C0:00BD test cl, 1 ; are extended read/write supported? 07C0:00C0 jz 07C0:00C6 ; jump if not 07C0:00C2 inc bp(ds:14h) ; set marker 07C0:00C6 retn ; return READ $BOOT (16 SECTORS) TO MEMORY
07C0:00C7 pushad ; save
07C0:00C9 push ds ; registers on
07C0:00CA push es ; stack
07C0:00CB mov eax, ds:10h ; sector counter - starts at zero
07C0:00CF add eax, ds:1Ch ; hidden sectors
07C0:00D4 cmp eax, ds:20h ; ds:20 has drive's total sectors
07C0:00D9 jb 0117 ; jump if active partition start
; is not beyond 8GB point
READ IN LBA MODE ;Now read first 16 sectors of the NTFS partition (these sectors comprise ;the metadata file $Boot) using either LBA or CHS reads as determined ;above 07C0:00DD push ds ; save ds on stack ;Here, the IPL builds a disk address packet on the stack (the packet is ;defined for LBA reads/writes via int 13 functions 42h and 43h). The ;format of the packet is... ; ; ; Offset Size Description ; ------ ---- ----------- ; 00h BYTE 10h (size of packet) ; 01h BYTE reserved (0) ; 02h WORD number of blocks to transfer ; 04h DWORD pointer to transfer buffer ; 08h QWORD starting logical block address ; ; 07C0:00DE push large 0 ; address of 07C0:00E1 push eax ; sectors to read 07C0:00E3 push es ; read buffer is at 07C0:00E4 push bx ; 0D00:0000 07C0:00E5 push large 10010h ; read 1 sector, etc 07C0:00EB cmp bp(ds:14h), 0 ; byte is set if BIOS is LBA capable 07C0:00F0 jnz 0100 ; jump if LBA capable, 07C0:00F4 call 00AA ; else do install check for LBA extns 07C0:00F7 cmp bp(ds:14h), 0 ; recheck - LBA capable? 07C0:00FC jz 0161 ; go print error msg if not 07C0:0100 mov ah, 42h ; set up for int 13 LBA read 07C0:0102 mov dl, bp[ds:24h] ; BIOS drive number into dl 07C0:0106 push ss ; ss on stack (ss = 0h) 07C0:0107 pop ds ; ds:si = address of packet 07C0:0108 mov si, sp ; buffer (0:7C00) 07C0:010A int 13h ; perform read 07C0:010C pop eax ; clean up 07C0:010E pop bx ; . 07C0:010F pop es ; . 07C0:0110 pop eax ; . 07C0:0112 pop eax ; . 07C0:0114 pop ds ; the stack 07C0:0115 jmp 0144h ; set up to read next sector READ IN CHS MODE ;on entry eax = number of hidden sectors. 07C0:0117 xor edx, edx ; zero edx
07C0:011A movzx ecx, wp(ds:18h) ; ecx = sectors per track
07C0:0120 div ecx ; (hidden sectors) / (sectors per track)
07C0:0123 inc dl ; increment remainder in dl
07C0:0125 mov cl, dl ; move to cl
; this is sector number for read
07C0:0127 mov edx, eax ; hidden sectors now in edx
07C0:012A shr edx, 10h ;
07C0:012E div wp(ds:1Ah) ; (hidden sectors) / (number of heads)
07C0:0132 xchg dl, dh ; remainder to dh
; this is head number for ead
07C0:0134 mov dl, ds:24h ; BIOS drive number (80h) in dl
07C0:0138 mov ch, al ; high 8 bits of cylinder number
07C0:013A shl ah, 6 ; shift hi 2 bits of cyl number
07C0:013D or cl, ah ; or them to cl - now ready to read
07C0:013F mov ax, 201h ; read 1 sector
07C0:0142 int 13h ; do it
07C0:0144 jb 0161 ; jump if error 07C0:0148 mov ax, es ; move read buffer up 07C0:014A add ax, 20h ; by 512 bytes 07C0:014D mov es, ax ; to receive next sector 07C0:014F inc dp(ds:10h) ; increment sector counter 07C0:0154 dec wp(ds:0Eh) ; decrement loop counter 07C0:0158 jnz 00CB ; go read next sector 07C0:015C pop es ; 16 sectors have been read... 07C0:015D pop ds ; clean up 07C0:015E popad ; and 07C0:0160 retn ; return ERROR MESSAGES PRINT ROUTINE 07C0:0161 mov al, ds:1F8h ; value is 83h
07C0:0164 call 0170h ; print err msg
07C0:0167 mov al, ds:1FBh ; value is C9h
07C0:016A call sub_0_170 ; print err msg
07C0:016D sti ; enable interrupts
07C0:016E jmp 016Eh ; hang (wait for CTL+ALT+DEL)
;
;
07C0:0170 mov ah, 1 ;
07C0:0172 mov si, ax ; offset to start of msg
07C0:0174 lodsb ; get next char to print
07C0:0175 cmp al, 0 ; test for end of msg
07C0:0177 jz 0182h ; jump if done
07C0:0179 mov ah, 0Eh ; BIOS TTY output function
07C0:017B mov bx, 7 ; color wht on blk
07C0:017E int 10h ; print the character
07C0:0180 jmp 0174h ; get next character
07C0:0182 retn ; return when done
ERROR MESSAGES 07C0:0183 db "A disk read error occurred" 07C0:019F db 0 ; 07C0:01A0 db 0Dh ; CR 07C0:01A1 db 0Ah ; LF 07C0:01A2 db "NTLDR is missing" 07C0:01B2 db 0 07C0:01B3 db 0Dh ; CR 07C0:01B4 db 0Ah ; LF 07C0:01B5 db "NTLDR is compressed" 07C0:01C8 db 0 ; 07C0:01C9 db 0Dh ; CR 07C0:01CA db 0Ah ; LF 07C0:01CB db "Press Ctrl+Alt+Del to restart" 07C0:01E7 db 0 07C0:01E8 db 0Dh ; CR 07C0:01E9 db 0Ah ; LF 07C0:01EA db 0 DUP (0Eh) ; 07C0:01F8 db 83h ; these values are 07C0:01F9 db A0h ; offsets to the 4 07C0:01FA db B3h ; text message strings 07C0:01FB db C9h ; which the IPL can print 07C0:01FC db 0 ; 07C0:01FD db 0 ; SYSTEM SECTOR SIGNATURE 07C0:01FE db 55h ; U 07C0:01FF db AAh ; ª |