Bitlocker : analyse du MBR de Windows Vista
Quel rapport entre le Master Boot Record (MBR) d'un disque dur et Bitlocker ? Une des fonctions de Bitlocker est le Secure Startup. Cette fonction consiste à s'assurer que le système ne démarre que si le code de démarrage pré-OS (du BIOS jusqu'à bootmgr, avant winload.exe) n'a pas changé depuis l'activation de Bitlocker. Si un bit de cette chaîne de code a changé, le TPM ne déchiffrera pas la clé de chiffrement du disque système.
Dans cette chaîne de code pré-OS se trouve le MBR du disque : lors du démarrage d'un PC sur un disque dur, le BIOS charge le MBR (secteur 0) du disque et l'exécute. Celui-ci lit la table de partition qu'il contient, identifie la partition active, charge son secteur de boot (premier secteur de la partition) et l'exécute.
Avec Vista cette séquence est modifiée pour le Secure Startup : le BIOS charge le MBR, le hashe dans le PCR (Platform Configuration Register) 4 du TPM, puis l'exécute. A son tour, le MBR charge le secteur de boot de la partition active, le hashe dans le PCR 8 du TPM, puis l'exécute, et ainsi de suite.
A la fin du processus de boot, un certain nombre de PCR du TPM sont renseignés par les hashes des différents codes exécutés. Lorsque bootmgr demande au TPM de déchiffrer la clé Bitlocker (en fait la Volume Master Key - VMK) du disque système, le TPM ne répond favorablement que si les valeurs de tous ces PCR sont identiques à ce qu'elles étaient au moment de l'activation de Bitlocker.
Intéressons-nous aujourd'hui à cette phase durant laquelle, comme dit plus haut, le MBR charge le secteur de boot de la partition active, le hashe dans le PCR 8 du TPM, puis l'exécute. Il y a à peu près un an, j'étais en train de travailler sur Bitlocker pour une formation que je préparais, et toute cette théorie sur le Secure Startup me paraissait trop vague pour l'expliquer correctement. J'ai alors décidé de désassembler le MBR, pour voir et comprendre comment se faisait ce hash. Car je suis comme ça : je ne comprends que ce que je vois ! :)
Pour rappel, le MBR est écrit en assembleur et son code est assez court puisque dans 512 octets doivent tenir le code, les messages d'erreurs correspondants, et la table des partitions. Il s'agit de code Intel en mode réel. Pour ceux qui étaient nés et qui s'en souviennent, au début des années 80, c'est l'assembleur que l'on utilisait sous MS-DOS : la mémoire adressée sur 20 bits (donc 1 Mo adressable) mais avec segmentation. Une adresse mémoire est représentée par un segment et un offset, chacun de 16 bits, et l'adresse physique est déterminée par segment * 0x10 + offset.
Avant de continuer sur l'analyse du MBR, rappelons que le TPM répond à une spécification TCG v1.21 (Trusted Computing Group). Microsoft a publié en mai 2006 le document Windows Vista BitLocker Client Platform Requirements qui décrit en détail les interfaces que doit supporter le BIOS d'une machine équipée d'un TPM pour que Bitlocker fonctionne. Extrait : The BIOS must expose a TCG INT 1Ah, sub-function BBh interface to pre-operating system environment applications. C'est à dire que c'est à travers l'interruption 1AH, avec AH=BBh, que le code en mode réel accède aux fonctions du TPM.
Première étape : extraire le MBR sous la forme d'un fichier. N'importe quel Windows Vista installé fera l'affaire : même si Bitlocker n'est disponible qu'avec les versions Enterprise et Ultimate, le MBR est le même pour toutes les versions et comporte toujours ce code de hash du secteur de boot par le TPM.
Sector Inspector (SecInspect.exe) est un outil en téléchargement gratuit qui suffira amplement. Après l'avoir installé, utilisez simplement la commande suivante, dans une ligne de commande Administrateur :
secinspect.exe -backup PhysicalDrive0 mbr.bin 0 1
Le fichier mbr.bin contient maintenant la copie du MBR. Utilisons encore SecInspect.exe pour afficher en hexa le fichier mbr.bin (cette fois, il est inutile d'être administrateur puisque nous ne faisons que lire un fichier, et non le disque physique) :
secinspect.exe -dfile mbr.bin
Le résultat ressemble à ceci :
Le code se trouve de l'offset 0 à l'offset 0x0161. Les messages d'erreur de 0x0162 à 0x01b4, et finalement la table des partitions de 0x01be à 0x01fd. Les deux derniers octets (55 aa) forment la signature du MBR.
Si l'on regarde bien le code en hexa, on remarque deux instructions "cd 1a". Ces deux octets forment l'instruction INT 1Ah. Comme dit précédemment, cela doit correspondre aux instructions du TPM pour hasher le secteur de boot.
Deuxième étape : désassembler le MBR. Pour ce faire, j'ai utilisé IDA Pro Freeware Freeware v4.3, dernière version freeware de IDA Pro. Passons sur le processus de désassemblage : après un peu de renommage de variables et de labels, on arrive assez rapidement à quelque chose de clair. Concentrons-nous plutôt sur les sections de code les plus importantes.
Analyse du code du MBR
Le BIOS charge toujours le MBR à l'adresse 0000:7C00. Comme le MBR charge plus tard le secteur de boot à la même adresse, la première chose qu'il fait est de se copier à l'adresse 0000:0600 et de continuer son exécution à ce nouvel emplacement :
seg000 segment byte public 'CODE' use16
assume cs:seg000
;org 600h
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
xor ax, ax ; Executing at 0000:C700h
mov ss, ax ; SS=0000
assume ss:seg000
mov sp, 7C00h
mov es, ax ; ES=0000
mov ds, ax ; DS=0000
mov si, 7C00h ; Source=0000:7C00h
mov di, 600h ; Target=0000:0600h
mov cx, 200h ; Length=0200h=512
cld
repe movsb ; Copy MBR from 0000:C700h to 0000:0600h
push ax
push 61Ch
retf ; Jump at 0000:061Ch
sti ; Now at 0000:061Ch
Il enchaîne directement sur la boucle d'analyse de la table des partitions à l'offset 0600+01be = 07be :
mov cx, 4
mov bp, 7BEh
check_entry:
cmp byte ptr [bp+0], 0
jl active_entry
jnz invalid_table
add bp, 10h ; Next entry
loop check_entry
int 18h ; retour au BIOS
active_entry:
Le code précédent cherche la partition active en testant si le bit de poids fort est à 1 dans le 1er octet (0x80). Si aucune partition active n'est présente, alors on rend la main au BIOS (pour info l'instruction INT 18h, sur les premiers IBM-PC, donnait la main au BASIC en ROM...)
Une fois la partition active trouvée, on lit son secteur de boot en 0000:7C00. Le code est un peu trop long pour être présenté ici dans son intégralité, du fait d'une gymnastique un peu lourde en fonction de la présence ou non des extensions de l'INT 13h du BIOS pour lire le disque.
C'est une fois le secteur de boot chargé que les choses deviennent intéressantes.
Vérification de la signature 55 AA à la fin du secteur de boot :
check_boot_sect:
cmp word ptr ds:7DFEh, 0AA55h
jnz missing_os
push word ptr [bp+0] ; Sauvegarde du numéro lecteur
Suit ensuite un bout de code dont le but est d'activer la "ligne d'adresse A20" afin de gérer l'adressage au-delà d'1 Mo (requis par certains BIOS TCG). Ce bout de code gère le contrôleur clavier 8042 pour éviter les conflits. Tout ceci n'est pas extrêmement clair pour moi, mais peu importe, la suite est plus intéressante.
Arrive ensuite le test de présence du TPM. D'après Windows Vista BitLocker Client Platform Requirements, la fonction TCG_StatusCheck (INT 1Ah avec AX=BB00h) renvoie EBX=41504354h ("TCPA") si un TPM est présent et si le BIOS est compatible, et la version du BIOS TCG dans CX (0102h pour 1.2). Ce test est effectué ici :
check_TPM:
mov ax, 0BB00h
int 1Ah ; TCG_StatusCheck
and eax, eax
jnz exec_boot_sect
cmp ebx, 41504354h ; "TCPA"
jnz exec_boot_sect
cmp cx, 102h
jb exec_boot_sect
Vient ensuite le hash du secteur de boot, grâce à la fonction TCG_CompactHashLogExtendEvent (INT 1Ah avec AX=0B07h) :
hash_boot_sect: ; AX=BB07h: TCG_CompactHashLogExtendEvent
push large 0BB07h
push large 200h ; ECX: length of buffer to be hashed=512
push large 8 ; EDX: PCR Number=8
push ebx ; EBX: 41504354h
push ebx
push ebp ; BP
push large 0 ; SI
push large 7C00h ; DI: offset of buffer to be hashed = 7C00h
popad
push 0
pop es ; ES: segment of buffer to be hashed = 0
assume es:seg000
int 1Ah ; TCG_CompactHashLogExtendEvent
Comme on le voit, le hash est effectué par le TPM : le MBR lui fournit le numéro de PCR destination et le buffer à hasher, et le TPM effectue l'opération.
Puis vient l'exécution du secteur de boot, sans explication nécessaire :
exec_boot_sect:
pop dx
xor dh, dh
jmp far ptr 0:7C00h
Cet article montre de quelle façon le MBR de Vista participe à la chaîne de confiance du Secure Startup de Bitlocker en faisant hasher le secteur de boot par le TPM dans le PCR 8. Finalement, les vieux souvenirs d'assembleur peuvent toujours servir !
Pour en savoir plus
[03/12/2007 - quelques corrections et ajouté comme référence la spécification TCG 1.2]