FAQ der Newsgroup de.comp.lang.assembler (d.c.l.a.)

Was ist ein "Bootsektor"? Was ist der Unterschied zu "MBR"?

Wenn beim Booten das BIOS mit seinen Prüfungen und Initialisieren fertig ist, lädt es vom Bootmedium (meist die erste Festplatte) den ersten Sektor und führt den darin befindlichen Code aus. Dieser erste Sektor wird heutzutage Master Boot Record (MBR: Hauptbootsektor) genannt und dieses erste Programm nennt sich Boot-Loader. Da sich in diesem Sektor auch die Partitionstabelle befindet, wird auch vom Partititionssektor gesprochen. Der Boot-Loader sieht in der Parttitionstabelle nach, welche Partition als aktiv markiert ist und führt den dort befindlichen Bootsektor aus, der dann das auf der Partition befindliche Betriebssystem lädt.

Der MBR und die Bootsektoren sind nicht über das Dateisystem (FAT, NTFS usw.) zu erreichen. Man benötigt einen direkten Festplattenzugriff, um sie auslesen zu können. Das nachfolgende MS-DOS-Programm benutzt die Funktion 02h des BIOS-Interrupts 13h, um den MBR auszulesen, und speichert dessen Inhalt als BOOT.BIN ab:

Nasm » 16bit.com
; Assemblieren: nasm <Name>.nasm -fbin -o <Name>.com

BITS 16
ORG 100h
SECTION .text

    mov ah, 02h                 ; Funktion 02h: Read Sectors
    mov al, 1                   ; Anzahl der Sektoren
    mov bx, sektor              ; Puffer: ES:BX
    mov ch, 0                   ; Spur 0
    mov cl, 1                   ; Sektor 1
    mov dh, 0                   ; Kopf 0
    mov dl, 80h                 ; 1. Festplatte
    int 13h                     ; BIOS
    jc Err

    mov ah, 3Ch                 ; Funktion 3Ch: Create New File
    mov cx, 0h                  ; keine besonderen Attribute
    mov dx, name                ; Dateiname (ASCIIZ): DS:DX
    int 21h                     ; MS-DOS
    jc Err
    mov [handle], ax

    mov ah, 40h                 ; Funktion 40h: Write to Handle
    mov bx, [handle]            ; Handle
    mov cx, 512                 ; Anzahl
    mov dx, sektor              ; Puffer: DS:DX
    int 21h                     ; MS-DOS
    jc Err

    mov ah, 3Eh                 ; Funktion 3Eh: Close Handle
    mov bx, [handle]            ; Handle
    int 21h                     ; MS-DOS
    jc Err

    mov ah, 09h                 ; Funktion 09h: Write to StdOut
    mov dx, ok                  ; ASCII$-Text: DS:DX
    int 21h                     ; MS-DOS
    mov ax,4C00h                ; Funktion 4Ch: Terminate with Return Code (0)
    int 21h                     ; MS-DOS

Err:
    mov ah, 09h                 ; Funktion 09h: Write to StdOut
    mov dx, nok                 ; ASCII$-Text: DS:DX
    int 21h                     ; MS-DOS
    mov ax,4C01h                ; Funktion 4Ch: Terminate with Return Code (1)
    int 21h                     ; MS-DOS

name db "BOOT.BIN", 0           ; Initialisierte Variablen, die sich direkt an
ok   db "Ok$"                   ; den Code-Block anschließen und Teil des
nok  db "Fehler$"               ; Codesegments sind.

SECTION .bss                    ; Nicht initialisierte Variablen, die erst im
sektor RESB 512                 ; Programm belegt werden.
handle RESW 1                   ; Benötigt werden lediglich deren Adressen.

Das obige 16-Bit-Programm funktioniert unter MS-DOS und auch unter Win9x (95, 98, 98SE, ME), aber nicht unter WinNT (NT, XP, 2000, Vista). WinNT übernimmt die vollständige Kontrolle über sämtliche Hardware und lässt einen direkten Zugriff unter BIOS nicht zu. Allerdings kann man in WinNT mit der Win32-Funktion CreateFile auch direkt auf die Festplatte zugreifen, wenn man mit Administratorrechten unterwegs ist:

Nasm & GoLink
; Assemblieren: nasm.exe -fwin32 -o <Name>.obj <Name>.nasm
; Linken:       GoLink.exe /console /entry _main <Name>.obj kernel32.dll
; Achtung:      Funktioniert ausschließlich in Windows NT (nicht Win9x)

BITS 32
EXTERN CreateFileA, CloseHandle, ReadFile, WriteFile    ; kernel32.dll

SECTION .bss
    sektor RESB 512
    handle RESD 1
    dummy  RESD 1

SECTION .data
    platte db "\\.\PhysicalDrive0",0
    datei  db "BOOT.BIN",0

SECTION .text
GLOBAL _main
_main:

    push 0                      ; hTemplateFile
    push 0                      ; dwFlagsAndAttributes
    push 3                      ; dwCreationDisposition = OPEN_EXISTING
    push 0                      ; lpSecurityAttributes
    push 3                      ; dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE
    push 80000000h              ; dwDesiredAccess = GENERIC_READ
    push platte                 ; lpFileName
    call CreateFileA
    cmp eax, -1                 ; INVALID_HANDLE_VALUE
    je Err
    mov [handle], eax

    push 0                      ; lpOverlapped
    push dummy                  ; lpNumberOfBytesRead
    push 512                    ; nNumberOfBytesToRead
    push sektor                 ; lpBuffer
    push dword [handle]         ; hFile
    call ReadFile

    push dword [handle]
    call CloseHandle

    push 0                      ; hTemplateFile
    push 0                      ; dwFlagsAndAttributes
    push 2                      ; dwCreationDisposition = CREATE_ALWAYS
    push 0                      ; lpSecurityAttributes
    push 3                      ; dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE
    push 40000000h              ; dwDesiredAccess = GENERIC_WRITE
    push datei                  ; lpFileName
    call CreateFileA
    cmp eax, -1                 ; INVALID_HANDLE_VALUE
    je Err
    mov [handle], eax

    push 0                      ; lpOverlapped
    push dummy                  ; lpNumberOfBytesWritten
    push 512                    ; nNumberOfBytesToWrite
    push sektor                 ; lpBuffer
    push dword [handle]         ; hFile
    call WriteFile

    push dword [handle]
    call CloseHandle

Ende:
    mov eax, 0                  ; Exitcode
    ret                         ; Ende
Err:
    mov eax, 1                  ; Exitcode
    ret                         ; Ende

In Win9x funktioniert dieses Programm nicht. Einen Zugriff wenigstens auf die Diskettenlaufwerke kann man über den virtuellen Treiber VWIN32 und die Funktion DeviceIoControl bewerkstelligen:

Nasm & GoLink
; Assemblieren: nasm.exe -fwin32 -o <Name>.obj <Name>.nasm
; Linken:       GoLink.exe /console /entry _main <Name>.obj kernel32.dll

BITS 32
EXTERN CreateFileA, CloseHandle, ReadFile, WriteFile, DeviceIoControl   ; kernel32.dll

SECTION .bss
    sektor RESB 512
    dummy  RESD 1

SECTION .data
    handle   dd 0
    DIOC_REGISTERS:
        rEBX   dd sektor          ; Puffer
        rEDX   dd 0               ; Kopf 0 (dh), Gerät A (dl) (0x00->A, 0x01->B)
        rECX   dd 1               ; Spur 0 (ch), Sektor 1 (cl)
        rEAX   dd 0x201           ; ah = Funktion 02h(Sektor lesen) , al = Anzahl der Sektoren
        rEDI   dd 0
        rESI   dd 0
        rFlags dd 0
    Len_DIOC_REGISTERS EQU $-DIOC_REGISTERS
    platte   db "\\.\vwin32",0
    datei    db "BOOT.BIN",0

SECTION .text
GLOBAL _main
_main:

    push 0                      ; hTemplateFile
    push 0x04000000             ; dwFlagsAndAttributes = FILE_FLAG_DELETE_ON_CLOSE
    push 3                      ; dwCreationDisposition = OPEN_EXISTING
    push 0                      ; lpSecurityAttributes
    push 3                      ; dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE
    push 80000000h              ; dwDesiredAccess = GENERIC_READ
    push platte                 ; lpFileName
    call CreateFileA
    cmp eax, -1                 ; INVALID_HANDLE_VALUE
    je Err
    mov [handle], eax

    push 0                      ; lpOverlapped
    push dummy                  ; lpBytesReturned
    push Len_DIOC_REGISTERS     ; nOutBufferSize
    push DIOC_REGISTERS         ; lpOutBuffer
    push Len_DIOC_REGISTERS     ; nInBufferSize
    push DIOC_REGISTERS         ; lpInBuffer
    push 4                      ; dwIoControlCode = VWIN32_DIOC_DOS_INT13
    push dword [handle]         ; hDevice
    call DeviceIoControl
    test eax, eax
    je Err
    test dword [rFlags], 1
    jne Err

    push dword [handle]
    call CloseHandle

    push 0                      ; hTemplateFile
    push 0                      ; dwFlagsAndAttributes
    push 2                      ; dwCreationDisposition = CREATE_ALWAYS
    push 0                      ; lpSecurityAttributes
    push 3                      ; dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE
    push 40000000h              ; dwDesiredAccess = GENERIC_WRITE
    push datei                  ; lpFileName
    call CreateFileA
    cmp eax, -1                 ; INVALID_HANDLE_VALUE
    je Err
    mov [handle], eax

    push 0                      ; lpOverlapped
    push dummy                  ; lpNumberOfBytesWritten
    push 512                    ; nNumberOfBytesToWrite
    push sektor                 ; lpBuffer
    push dword [handle]         ; hFile
    call WriteFile

    push dword [handle]
    call CloseHandle

Ende:
    mov eax, 0                  ; Exitcode
    ret                         ; Ende
Err:
    cmp dword [handle], 0       ; Handle vorhanden?
    je .A                       ; nein
    push dword [handle]         ; ja: schließen
    call CloseHandle
.A:
    mov eax, 1                  ; Exitcode
    ret                         ; Ende

Der Festplattenzugriff ist mit dieser Methode nicht möglich. Um auch unter Win9x in einem 32-Bit-Programm auf die Festplatte zuzugreifen können, schlägt Microsoft einen etwas abenteuerlichen Weg vor:

PRB: DeviceIoControl Int 13h Does Not Support Hard Disks
http://support.microsoft.com/?scid=kb;en-us;137176

Ralph 'rkhb' Bauer Feb 2009