.
Si vous êtes arrivés sur cette page par erreur, faites demi tour sans tarder car ici se trouve l'antre de la bête - où il ne faut pas connaître la peur car vous allez découvrir les entrailles de votre machine.
Le mbr est le premier secteur du disque chargé en mémoire lors du démarrage d'un ordinateur. Il a pour fonction principale de rechercher une partition où se trouve un système d'exploitation à lancer.
Avant tout il faudra vérifier que la table des partitions est conforme - à défaut un message d'erreur sera affiché.
Lors de la mise sous tension, différentes routines se mettent en place ; la première consiste à attendre que la tension de l'alimentation soit stabilisée puis des tests processeur, carte mère, barrettes de mémoire sont effectués. Ceci correspond à la séquence POST.
Vient ensuite la recherche de périphériques de démarrage selon l'ordre de boot.
Le bios charge alors le premier secteur du disque (512 octets) en mémoire vive à l'adresse 0000:7C00
Ce type d'adressage correspond au mode réel de fonctionnement d'un PC, c'est un fonctionnement en mode 16 bits.
Une fois le chargement terminé et sans erreur, l'exécution se poursuit à l'adresse 0000:7C00, et donc suit les instructions qui figurent dans le premier secteur du mbr
Nous avons vu comment afficher le contenu du mbr ; ici il faudra effectuer deux opérations :
- effectuer une copie de la partie exécutable du mbr (les 440 premiers octets) sous la forme d'un fichier
- utiliser un désassembleur pour afficher les instructions sous une forme lisibles par un humain (c'est tout relatif - à moins que je ne sois pas humain) au lieu de valeurs hexadécimales (opcodes)
Pour la première opération, on rentrera dans un terminal
sudo dd if=/dev/sda of=~/mbr_exec.bs bs=1 count=440
La deuxième opération transforme le fichier d'instruction hexadécimales en adresses, opcodes, instructions
ndisasm -b16 ~/mbr_exec.bs > ~/mbr_exec.asm
ndisasm est le désassembleur du paquet nasm, -b16 indique que le code doit être considéré comme de l'assembleur 16 bits.
Le résultat est écrit dans le fichier ~/mbr_exec.asm
Le résultat brut pose problème car des chaines de texte ont été considérées comme des instructions et sont donc mal interprétées. Il faut alors procéder par tâtonnement pour les repérer (par exemple avec un éditeur hexa) et les exclure du traitement de désassemblage.
Une autre difficulté est que certaines adresses peuvent contenir, soit du code à un moment donné, soit des données (zone tampon) lorsque le code n'est plus utilisé.
L'exemple qui suit concerne un mbr obtenu lors de l'installation de Lucid. Lucid a été installé en mettant grub-pc dans le mbr.
J'ai effectué une analyse (partielle) du fonctionnement des différents morceaux mais des parties restent encore nébuleuses.
Le programme utilise des fonctions du bios (seules fonctions disponibles à ce stade du démarrage du PC) et je me suis servi de la liste des interruptions
établies par Ralph Brown.
Voilà donc à quoi ressemble la chose.
; Programme 16 bits chargé en 0000:7C00 00000000 EB63 jmp short 0x65 00000002 90 nop 00000003 D0 00000004 resb 1 ; mode : 0x00 pour le mode CHS, 0x01 pour le mode LBA ; Buffer pour chargement en mode CHS 00000005 resd 1 ; nombre de secteurs 00000009 resd 1 ; nombre de têtes 0000000D resd 1 ; nombre de cylindres ; Buffer pour chargement en mode LBA 00000005 resw 1 ; 0x0010 - taille paquet 00000007 resw 1 ; 0x0001 - nb blocs à transférer 00000009 resw 1 ; 0x0000 0000000B resw 1 ; 0x7000 - buffer en 0x70000000 0000000D resd 1 ; 0x00000001 - emplacement LBA du bloc à charger 00000011 resd 1 ; 0x00000000 - démarrer au bloc 0x0000000000000001 0000005A 0080 dw 0x8000 ; Emplacement de l'offset où le contenu du tampon sera copié 0000005C 01000000 dd 0x00000001 ; Adresse LBA (valeur basse) où se trouve le secteur suivant de chargement (core.img) 00000060 00000000 dd 0x00000000 ; Valeur haute du qword de l'adresse LBA 00000064 FF db 0xff ; 0xff pour le chargement de core.img sur le disque de boot, si 0x80 premier disque dur, si 0x81 deuxième... 00000065 FA cli 00000066 90 nop 00000067 90 nop 00000068 F6C280 test dl,0x80 ; teste le bit 7 de dl (si activé=disque dur) 0000006B 7502 jnz 0x6f ; si disque dur 0000006D B280 mov dl,0x80 ; marque le périphérique comme disque dur 0000006F EA747C0000 jmp word 0x0:0x7c74 ; saute à la ligne suivante (car mbr chargé en 0000:7c00) 00000074 31C0 xor ax,ax 00000076 8ED8 mov ds,ax 00000078 8ED0 mov ss,ax 0000007A BC0020 mov sp,0x2000 ; la pile pointe sur 0000:2000 0000007D FB sti 0000007E A0647C mov al,[0x7c64] ; examine le contenu de l'adresse 7C64 (ici 0xff) 00000081 3CFF cmp al,0xff 00000083 7402 jz 0x87 00000085 88C2 mov dl,al ; si non égal à 0xff, sauvegardé dans dl - si core.img est sur un autre disque 00000087 52 push dx 00000088 BB1704 mov bx,0x417 ; adresse clavier 0000008B 802703 and byte [bx],0x3 ; teste si une touche shift est activée 0000008E 7406 jz 0x96 00000090 BE887D mov si,0x7d88 ; pointe sur la chaine "GRUB" 00000093 E81C01 call word 0x1b2 00000096 BE057C mov si,0x7c05 ; définit une zone tampon pour les paramètres de chargement 00000099 F6C280 test dl,0x80 ; teste si disque dur 0000009C 7448 jz 0xe6 ; pas disque dur 0000009E B441 mov ah,0x41 000000A0 BBAA55 mov bx,0x55aa 000000A3 CD13 int 0x13 ; installation check 000000A5 5A pop dx 000000A6 52 push dx 000000A7 723D jc 0xe6 ; extensions non supportées 000000A9 81FB55AA cmp bx,0xaa55 000000AD 7537 jnz 0xe6 ; extension non installée 000000AF 83E101 and cx,byte +0x1 000000B2 7432 jz 0xe6 ; fonctions 42h-44h, 47h et 48h non supportées ; traitement extension lba_mode 000000B4 31C0 xor ax,ax 000000B6 894404 mov [si+0x4],ax ; met zéro en 7c09 et 7c0a 000000B9 40 inc ax 000000BA 8844FF mov [si-0x1],al ; met 1 en 7c04 (1=lba_mode) 000000BD 894402 mov [si+0x2],ax ; met 0x0001 en 7c07 000000C0 C7041000 mov word [si],0x10 ; met 0x0010 en 7c05 000000C4 668B1E5C7C mov ebx,[0x7c5c] ; récupère l'emplacement (partie basse du qword) de core.img 000000C9 66895C08 mov [si+0x8],ebx ; sauvegarde cet emplacement pour le charger 000000CD 668B1E607C mov ebx,[0x7c60] ; récupère la partie haute du qword de core.img 000000D2 66895C0C mov [si+0xc],ebx ; sauvegarde 000000D6 C744060070 mov word [si+0x6],0x7000 ; adresse de segment du tampon 000000DB B442 mov ah,0x42 000000DD CD13 int 0x13 ; fonction extended read 000000DF 7205 jc 0xe6 ; si erreur 000000E1 BB0070 mov bx,0x7000 000000E4 EB76 jmp short 0x15c ; traitement sans extension chs_mode 000000E6 B408 mov ah,0x8 000000E8 CD13 int 0x13 ; récupère les paramètres du disque (numéro maxi tête, numéro maxi secteur, numéro maxi cylindre) 000000EA 730D jnc 0xf9 ; si pas d'erreur 000000EC F6C280 test dl,0x80 000000EF 0F84D000 jz word 0x1c3 000000F3 BE937D mov si,0x7d93 ; pointe sur la chaine "Hard Disk" 000000F6 E98200 jmp word 0x17b 000000F9 660FB6C6 movzx eax,dh ; numéro maxi de têtes 000000FD 8864FF mov [si-0x1],ah ; met zéro en 7c04 00000100 40 inc ax ; nombre de têtes 00000101 66894404 mov [si+0x4],eax ; sauvegardé en 7c09 00000105 0FB6D1 movzx dx,cl ; 2bits pour cylindre+nombre max de secteurs 00000108 C1E202 shl dx,0x2 ; décale de 2 bits vers la gauche 0000010B 88E8 mov al,ch ; 8 bits de poids faible n° maxi cylindre 0000010D 88F4 mov ah,dh ; 2 bits de poids fort cylindre 0000010F 40 inc ax ; nb de cylindres 00000110 894408 mov [si+0x8],ax ; sauvegardé en 7c0d 00000113 0FB6C2 movzx ax,dl ; copie le numéro maxi de secteurs (x4) 00000116 C0E802 shr al,0x2 ; numéro maxi de secteurs=nb secteurs 00000119 668904 mov [si],eax ; sauvegardé 0000011C 66A1607C mov eax,[0x7c60] 00000120 6609C0 or eax,eax ; teste si zéro 00000123 754E jnz 0x173 ; erreur de géométrie ; Convertit adresse linéaire en tête, secteur et cylindre 00000125 66A15C7C mov eax,[0x7c5c] ; charge l'adresse LBA de l'emplacement de core.img (valeur basse qword) 00000129 6631D2 xor edx,edx ; met à zéro 0000012C 66F734 div dword [si] ; divise l'adresse LBA par le nombre de secteurs par piste 0000012F 88D1 mov cl,dl ; reste de la division = numéro secteur (commencement à 0) 00000131 31D2 xor dx,dx 00000133 66F77404 div dword [si+0x4] ; divise par le nombre de têtes - donne un nb de cylindres 00000137 3B4408 cmp ax,[si+0x8] ; compare au nombre de cylindres du disque 0000013A 7D37 jnl 0x173 ; supérieur au nombre de cylindres du disque - erreur 0000013C FEC1 inc cl ; rajoute 1 car les n°secteurs commencent à 1 0000013E 88C5 mov ch,al ; 8 bits de poids faible n°cylindre 00000140 30C0 xor al,al 00000142 C1E802 shr ax,0x2 ; décale de 2 bits à droite (les bits 6 et 7 pour poids fort cylindre) 00000145 08C1 or cl,al ; modifie les bits 7 et 6 de cl en conséquence (poids fort cylindre) 00000147 88D0 mov al,dl ; numéro de tête = reste division 00000149 5A pop dx 0000014A 88C6 mov dh,al ; numéro de tête 0000014C BB0070 mov bx,0x7000 0000014F 8EC3 mov es,bx 00000151 31DB xor bx,bx ; tampon en 7000:0000 00000153 B80102 mov ax,0x201 ; fonction ah=02h = lecture secteur, al=01 = 1 secteur 00000156 CD13 int 0x13 ; lit un secteur 00000158 721E jc 0x178 ; erreur de lecture 0000015A 8CC3 mov bx,es ; Copie des données chargées (partie commune mode CHS ou LBA) 0000015C 60 pushaw 0000015D 1E push ds 0000015E B90001 mov cx,0x100 00000161 8EDB mov ds,bx ; charge le segment du buffer 00000163 31F6 xor si,si ; ds:si pointe sur 7000:0000 00000165 BF0080 mov di,0x8000 00000168 8EC6 mov es,si ; es:di pointe sur 0000:8000 0000016A FC cld 0000016B F3A5 rep movsw ; copie le contenu du buffer de 7000:0000-7000:01FF en 0000:8000-0000:81FF 0000016D 1F pop ds 0000016E 61 popaw 0000016F FF265A7C jmp word near [0x7c5a]; saute en 0000:8000 (car 7c5a contient 8000) - début de core.img 00000173 BE8E7D mov si,0x7d8e ; pointe sur la chaine "Geom" 00000176 EB03 jmp short 0x17b 00000178 BE9D7D mov si,0x7d9d ; pointe sur la chaine "Read" 0000017B E83400 call word 0x1b2 0000017E BEA27D mov si,0x7da2 ; pointe sur la chaine "Error" 00000181 E82E00 call word 0x1b2 00000184 CD18 int 0x18 00000186 EBFE jmp short 0x186 ; boucle sans fin ; Chaînes messages d'erreur 00000188 db "GRUB ",0 0000018E db "Geom",0 00000193 db "Hard Disk",0 0000019D db "Read",0 000001A2 db " Error",13,10,0 ; Affiche caractère 000001AB BB0100 mov bx,0x1 000001AE B40E mov ah,0xe 000001B0 CD10 int 0x10 ; affiche un caractère ; Affiche une chaine 000001B2 AC lodsb 000001B3 3C00 cmp al,0x0 000001B5 75F4 jnz 0x1ab ; teste la fin de la chaine 000001B7 C3 ret
Le programme chargé en mémoire à l'adresse 0000:7c00 commence par l'instruction jmp short 0x65 qui saute directement à l'adresse 065
Différentes parties sont visibles :
de 065 à 095 - Routine vérifie que le boot s'effectue à partir du disque dur
de 096 à 0b3 - Définit l'emplacement des paramètres de chargement du prochain secteur et le mode CHS ou LBA selon les capacités du bios
de 0b4 à 0e5 - Charge le premier secteur de core.img en mode LBA (interruption bios int13/ah=42h)
de 0e6 à 0f8 - Récupère les paramètres du disque et gère les erreurs
de 0f9 à 124 - Sauvegarde ces paramètres dans la zone des paramètres de chargement
de 125 à 15b - Convertit l'adresse LBA de core.img dans le mode CHS et charge ce secteur dans le tampon (interruption bios int13/ah=02h)
de 15c à 172 - Recopie le secteur chargé à son nouvel emplacement et saute à cet emplacement (première partie de core.img, soit diskboot.img)
de 173 à 187 - Affichage des messages d'erreur
de 1ab à 1b0 - Affichage de caractères
de 1b2 à 1b7 - Affichage des chaînes des messages d'erreurs
Contributeurs principaux : Nasman.