Analyse du MBR de Vista
Encore penché sur Singularity, je tombe par hasard sur un article parlant de Bitlocker et plus particulièrement la protection apporté au niveau du boot.
Je saute sur mon désassembleur préféré pour voir ça de mes propres yeux et je désassemble le MBR qui est le premier code de la séquence de boot qui est analysable facilement.
Bitlocker pour sécuriser la séquence de boot s’appuie sur un composant cryptographique matériel nommé TPM (Trusted Platform Module).
Avec un composant TPM, nous pouvons résumer la séquence de boot de Vista comme ceci :
TPM Init -> BIOS -> MBR -> Secteur de Boot -> BootMgr -> Winload.exe -> …
Commençons à analyser le MBR :
0000:0600 xor ax, ax
0000:0602 mov ss, ax
0000:0604 mov sp, 7C00h ; Mise en place de la pile
0000:0607 mov es, ax
0000:0609 mov ds, ax ; ss = es = ds = 0
0000:060B mov si, 7C00h
0000:060E mov di, 600h
0000:0611 mov cx, 200h
0000:0614 cld
0000:0615 rep movsb ; Recopie du MBR en 600h
0000:0617 push ax
0000:0618 push 61Ch
0000:061B retf ; Redirection de l'exécution en 61Ch
Comme tout le monde le sait le BIOS charge le MBR en 0000:7C00h.
Le MBR met d’abord en place la pile puis se copie 0000:0600h pour laisser la place au secteur de boot qui sera copier plus tard.
Ensuite l’exécution est redirigée en 61Ch.
Pour mieux comprendre la suite, il faut rappeler le format d’un MBR :
Et du format de la table des partitions :
struct PARTITION_DESCRIPTOR {
unsigned char ACTIVE;
unsigned char START_HEAD;
unsigned char START_SEC;
unsigned char START_CYL;
unsigned char TYPE;
unsigned char END_HEAD;
unsigned char END_SEC;
unsigned char END_CYL;
unsigned int START;
unsigned int SIZE;
};
struct PARTITION_TABLE {
struct PARTITION_DESCRIPTOR ENTRIES[4];
};
Voila, on est paré pour la suite :
0000:061D mov cx, 4
0000:0620 mov bp, offset PartitionTable
0000:0623
0000:0623 FindPartitionBootable:
0000:0623 cmp [bp+PARTITION_DESCRIPTOR.ACTIVE], 0
0000:0627 jl short PartitionBootableFound
0000:0629 jnz PrintInvalidPartitionTable
0000:062D add bp, size PARTITION_DESCRIPTOR
0000:0630 loop FindPartitionBootable ; Recherche d'une partition bootable
0000:0632 int 18h ; Sinon on repasse l'exécution au Bios
La boucle suivante parcourt la table des partitions et teste le champs ACTIVE si le bit de poids fort est a 1 alors la partition est bootable. Dans le cas où aucune partition bootable est trouvé, on repasse la main au bois avec l’interruption 18h.
0000:0634 PartitionBootableFound:
0000:0634 mov [bp+PARTITION_DESCRIPTOR.ACTIVE], dl
0000:0637 push bp
0000:0638 mov byte ptr [bp+size PARTITION_DESCRIPTOR+PARTITION_DESCRIPTOR.START_HEAD], 5
0000:063C mov byte ptr [bp+size PARTITION_DESCRIPTOR+PARTITION_DESCRIPTOR.ACTIVE], 0
0000:0640 mov ah, 41h
0000:0642 mov bx, 55AAh
0000:0645 int 13h ; Vérification de la présence de l'extension de l'interruption 13h
0000:0647 pop bp
0000:0648 jb short ReadBootSector
0000:064A cmp bx, 0AA55h
0000:064E jnz short ReadBootSector
0000:0650 test cx, 1
0000:0654 jz short ReadBootSector
0000:0656 inc byte ptr [bp+size PARTITION_DESCRIPTOR+PARTITION_DESCRIPTOR.ACTIVE]
Tout d’abord, le périphérique du MBR est stocké dans le champ ACTIVE de la partition bootable, on recycle l’octet vu qu’il nous sert plus !
Encore du recyclage après, on stocke deux octet dans le PARTITION_DESCRIPTOR suivant qui ne nous servira plus lui aussi.
On verra par la suite, l’utilisation de ces deux octets.
Ensuite il vérifie la présence de l’int 13h étendu. Si elle n’est pas présente on incrémente un des fameux octets.
0000:0659 ReadBootSector:
0000:0659 pushad
0000:065B cmp byte ptr [bp+size PARTITION_DESCRIPTOR+PARTITION_DESCRIPTOR.ACTIVE], 0
0000:065F jz short ReadWithoutExtension
0000:0661 push large 0
0000:0667 push large dword ptr [bp+8]
0000:066B push 0
0000:066E push 7C00h
0000:0671 push 1
0000:0674 push 10h
0000:0677 mov ah, 42h
0000:0679 mov dl, [bp+PARTITION_DESCRIPTOR.ACTIVE]
0000:067C mov si, sp
0000:067E int 13h ; Lecture du secteur de boot avec l'extension de l'interruption 13h
0000:0680 lahf
0000:0681 add sp, 10h
0000:0684 sahf
0000:0685 jmp short EndRead
0000:0687 ReadWithoutExtension:
0000:0687 mov ax, 201h
0000:068A mov bx, 7C00h
0000:068D mov dl, [bp+PARTITION_DESCRIPTOR.ACTIVE]
0000:0690 mov dh, [bp+PARTITION_DESCRIPTOR.START_HEAD]
0000:0693 mov cl, [bp+PARTITION_DESCRIPTOR.START_SEC]
0000:0696 mov ch, [bp+PARTITION_DESCRIPTOR.START_CYL]
0000:0699 int 13h ; DISK - READ SECTORS INTO MEMORY
0000:0699 ; AL = number of sectors to read, CH = track, CL = sector
0000:0699 ; DH = head, DL = drive, ES:BX -> buffer to fill
0000:0699 ; Return: CF set on error, AH = status, AL = number of sectors read
Tout d’abord, on compare l’octet pour voir si l’extension est présente et l’exécution est redirigé sur l’utilisation de l’int 13h avec ou sans extension pour lire le secteur de boot et le placé en 0000:7C00h.
0000:069B popad
0000:069D jnb short CheckBootSector
0000:069F dec byte ptr [bp+size PARTITION_DESCRIPTOR+PARTITION_DESCRIPTOR.START_HEAD]
0000:06A2 jnz ResetDisk
0000:06A6 cmp [bp+PARTITION_DESCRIPTOR.ACTIVE], 80h
0000:06AA jz PrintErrorLoadingOS
0000:06AE mov dl, 80h
0000:06B0 jmp short PartitionBootableFound
0000:06B2
0000:06B2 ResetDisk:
0000:06B2 push bp
0000:06B3 xor ah, ah
0000:06B5 mov dl, [bp+PARTITION_DESCRIPTOR.ACTIVE]
0000:06B8 int 13h ; DISK - RESET DISK SYSTEM
0000:06B8 ; DL = drive (if bit 7 is set both hard disks and floppy disks reset)
0000:06BA pop bp
0000:06BB jmp short ReadBootSector
Ensuite on vérifie la bonne lecture du secteur sinon on décrémente le 2eme fameux octet dont je vous parlais, il sert tout simplement de compteur. C’est-à-dire qu’il y’a 5 tentatives d’essaie de lecteur du secteur de boot. Pour chaque erreur, on décrémente, on fait un reset du périphérique et on réessaie de lire.
0000:06BD CheckBootSector:
0000:06BD cmp word ptr ds:7DFEh, 0AA55h
0000:06C3 jnz short PrintMissingOS
0000:06C5 push word ptr [bp+PARTITION_DESCRIPTOR.ACTIVE]
0000:06C8
0000:06C8 ActivateA20:
0000:06C8 call WaitKeyboadIsEmplty
0000:06CB jnz CheckTPM
0000:06CF mov al, 0D1h ; D1: Write Output Port: next byte written to port 60h is placed
0000:06CF ; in the 8042 output port (which is inaccessible to the data bus)
0000:06CF ;
0000:06CF ; |7|6|5|4|3|2|1|0| 8042 Output Port
0000:06CF ; | | | | | | | `---- system reset line
0000:06CF ; | | | | | | `----- gate A20
0000:06CF ; | | | | `-------- undefined
0000:06CF ; | | | `--------- output buffer full
0000:06CF ; | | `---------- input buffer empty
0000:06CF ; | `----------- keyboard clock (output)
0000:06CF ; `------------ keyboard data (output)
0000:06CF
0000:06D1 out 64h, al ; AT Keyboard controller 8042
0000:06D3 call WaitKeyboadIsEmplty
0000:06D6 mov al, 0DFh ; DFh = 11011111b
0000:06D8 out 60h, al ; AT Keyboard controller 8042
0000:06DA call WaitKeyboadIsEmplty
0000:06DD mov al, 0FFh ; F0h-FFh : Pulse Output Port
0000:06DF out 64h, al ; AT Keyboard controller 8042
0000:06DF ; Reset the keyboard and start internal diagnostics
0000:06E1 call WaitKeyboadIsEmplty
On commence par vérifier si le secteur de boot contient bien la signature AA55h. Ensuite une routine que l’on retrouve dans la plupart des MBR, elle permet d’activer la ligne A20. Jusque ici cette analyse, rien de bien nouveau pour un MBR ! C’est maintenant qu’on va avoir du nouveau !
0000:06FD HashBootSector:
0000:06FD push large 0BB07h
0000:0703 push large 200h
0000:0709 push large 8
0000:070F push ebx
0000:0711 push ebx
0000:0713 push ebp
0000:0715 push large 0
0000:071B push large 7C00h
0000:0721 popad
0000:0723 push 0
0000:0726 pop es
0000:0727 int 1Ah ; TCG_CompactHashLogExtendEvent
0000:0727 ;
0000:0727 ; AX = BB07h
0000:0727 ; ES:DI = segment:offset of buffer to be hashed. Physical location of buffer starts at 16*ES+DI
0000:0727 ; ECX = length of buffer to be hashed. Lengths of 0 through 32K must be supported
0000:0727 ; EDX = PCR number that that hash must be extended into
0000:0727 ; ESI = an informative value that must be stored in the event log
0000:0727 ; EBX = 41504354h
0000:0727 ; ECX = 0h
0000:0727 ; EDX = 0h
Le code commence par vérifié la présence d’un TPM avec l’int 1Ah et AX = BB00h. Si c’est le cas, un hash du secteur de boot est effectué avec le TPM et le résultat est stocké dans le PCR[8]. Enfin l’exécution est passé au MBR :
0000:0729 ExecuteBootSector:
0000:0729 pop dx
0000:072A xor dh, dh
0000:072C jmp far ptr 0:7C00h
Donc le MBR est bien différent depuis l’introduction de la technologie Bitlocker. Voila, ayant trouvé aucune analyse complète du MBR de Vista, j’ai décidé de poster cette petite aventure !
Vous pouvez retrouver le dump et le listing du MBR de Vista ici
Autres documents :
http://netah25.developpez.com/windows/BitLocker/
http://download.microsoft.com/download/5/D/6/5D6EAF2B-7DDF-476B-93DC-7CF0072878E6/secure-start_tech.doc
http://download.microsoft.com/documents/uk/security/downloads/BitLocker%20Forensics%20UK%20LE2.ppt
http://actes.sstic.org/SSTIC06/BitLocker/SSTIC06-article-Ourghanlian-BitLocker.pdf
http://www.blackhat.com/presentations/bh-europe-07/Kumar/Presentation/bh-eu-07-kumar-apr19.pdf