MBR 부트 코드 분석 - forensic-proof.com 펌. File Control

MBR 부트 코드 분석 - forensic-proof.com 펌.
원글 : http://forensic-proof.com/archives/439
원글을 그냥 링크로 걸어도 되겠지만... 백업용도로 그저 개인블로그에 저장합니다.

이전 포스팅에서 MBR(Master Boot Record)의 구조에 대해서 살펴보았다. 이번에는 MBR 영역의 부트 코드(Boot Code)에 대해 분석해보자. MBR이나 VBR(Volume Boot Record) 또는 BS(Boot Sector)에는 컴퓨터가 부팅될 때 처리되는 코드가 들어간다. 부팅 절차는 기본적으로 BIOS에 의해 POST(Power On Self-Test) 과정이 수행되고, 이후에 추가적인 BIOS(SCSI, Video등)가 로드된다. 그리고 POST 과정에 의해 처리된 데이터들도 로드된다. 

이러한 하드웨어적인 테스트를 모두 마치게 되면, 주(Primary) 하드디스크 드라이브의 MBR의 부트 코드를 호출한다. 간단히 말해, MBR의 부트 코드는 파티션 테이블에서 부팅 가능한 파티션을 찾아 해당 파티션의 VBR의 부트 코드를 호출하는 역할을 한다. VBR의 부트 코드는 운영체제를 로드하게 된다(윈도우는 NT Loader). 이를 간략히 그림으로 나타내면 다음과 같다. 좀 더 자세한 부팅 절차를 살펴보고 싶다면 이전 글을 참조하기 바란다.

MBR의 부트 코드 영역은 앞서 살펴본 바와 같이 446 바이트 크기이다. 이후에 파티션 테이블과 시그니처가 따라온다. 다음은 필자의 주 드라이브의 MBR 영역을 덤프한 것이다.

해당 부트 코드 영역은 BIOS에서 해석할 수 있는 16비트 기계어 코드들로 이루어져 있기 때문에 디스어셈블러를 이용하면 부트 코드에 대한 어셈블리 코드를 얻을 수 있다. 다음은 IDA Pro 를 통해 해당 영역의 코드를 살펴본 것이다.

 

 

MBR 부트 코드 분석

다음은 필자의 MBR 부트 코드 영역을 분석한 내용이다. 
먼저, POST 과정을 마치면 부트로더에 의해 LBA 0(MBR)의 512 바이트가 메모리에 로드된다. 
로드되는 위치는 항상 0000:7C00 이다. 
 

MBR이 메모리 0:7C00 위치에 로드된 후 7C1B부터 485 바이트를 061B 위치로 복사한다.현재 코드에서 27 바이트를 사용하므로, 나머지 485 바이트만 복사한다.RELOCATION : 0:7C1B to 0:061B as 1E5h (except for 0:7C00 - 7C1A)0000:7C00   xor  ax, ax                 ;set zero accumulator reg0000:7C02   mov  ss, ax                 ;set zero stack seg reg0000:7C04   mov  sp, 7C00               ;set stack ptr to 0000:7C000000:7C07   sti                         ;enable interrupts0000:7C08   push ax                     ;AX=0 at this point0000:7C09   pop  es                     ;ES(Extra Segment) now 00000:7C0A   push ax                     ;AX=0 at this point0000:7C0B   pop  ds                     ;DS(Data Segment) now 00000:7C0C   cld                         ;clear direction flag0000:7C0D   mov  si, 7C1B               ;SI(Src Index) now 7C1Bh0000:7C10   mov  di, 061B               ;DI(Des Index) now 061Bh0000:7C13   push ax                     ;set up AX and DI0000:7C14   push di                     ; form jump to 0000:061B0000:7C15   mov  cx, 1E5                ;move as 1E5(512-27=485) bytes0000:7C18   repz movsb                  ;move a portion of MBR from 7C1B to 061B0000:7C1A   retf                        ;jump to 0000:061B---------------------------------------------------------------------------부팅 가능한 파티션 테이블 엔트리를 찾는다. 파티션 테이블 첫 바이트가 0x80 값을 가지면부팅 가능한 파티션이다. 0x80을 부팅 가능한 값으로 사용한 이유는 일반적으로 첫 번째하드디스크를  부팅 가능한 드라이브로 사용하는데, 첫 번째 하드디스크의 드라이브 인덱스 값이0x80 이기 때문이다. 부팅가능한 엔트리를 찾았다면 0:062E로 점프한다.4개의 엔트리 모두부팅가능하지 않다면 "PRESS A KEY TO REBOOT"를 출력한다.SECARH FOR AN BOOTABLE ENTRY :0000:061B   mov  bp, 07BE               ;point to first partition table entry0000:061E   mov  cl, 04                 ;maximum table entries are 40000:0620   cmp  [bp+00], ch            ;(ch=0)is this a non-bootable entry?0000:0623   jl   062E                   ;no, this is bootable entry0000:0625   jnz  063A                   ;jump to error message navigator0000:0627   add  bp, 0010               ;next to partition table entry0000:062A   loop 0620                   ;jump to 0620h unless cl=00000:062C   int  18                     ;None of them were bootable                                        ;GO TO ROM BASIC (only available IBM-PC)                                        ;just many BIOS display                                        ;"PRESS A KEY TO REBOOT"---------------------------------------------------------------------------부팅 가능한 엔트리를 찾은 경우, 나머지 엔트리가 부팅 가능하지 않은지 검사한다.기본적으로 MBR에 의해 부팅되는 경우, 부팅 가능한 엔트리는 하나만 존재해야 한다.부팅 가능한 엔트리가 하나 이상인 경우, 0:063A 로 점프한다.부팅 가능한 엔트리가 하나인 경우, 0:064F 로 점프한다.FOUND THE BOOTABLE ENTRY, MAKE SURE ONLY ONE BOOTABLE ENTRY :0000:062E   mov  si, bp                 ;SI now 07BE---------------------------------------------------------------------------0000:0630   add  si, 0010               ;SI now 10(16 bytes)0000:0633   dec  cx                     ;iF CX=0, done here0000:0634   jz   064F                   ;jump to 064F0000:0636   cmp  [si], ch               ;(ch=0)is this a non-bootable entry?0000:0638   jz   0630                   ;if not zero, fall into the following                                        ; error message routine---------------------------------------------------------------------------부팅 가능한 엔트리를 하나 이상 찾은 경우 "Invalid partition table"을 출력한다.FOUND MORE THAN ONE BOOTABLE ENTRY :0000:063A   mov  al, [07B5]             ;al now [07B5]=2C                                        ;(072C) in the src index reg;following                                        ;display "Invalid partition table"---------------------------------------------------------------------------오류에 맞는 에러 메시지를 출력한다.DISPLAY ERROR MESSAGE :0000:063D   mov  ah, 07                 ;ah now 070000:063F   mov  si, ax                 ;ax(07xx) in the src index reg                                        ;xx = 2C,44,63 (if english ver)0000:0641   lodsb                       ;Load character into AL from [SI]0000:0642   cmp  al, 00                 ;is this end of message marker?0000:0644   je   0642                   ;infinite loop0000:0646   mov  bx, 0007               ;0000:0649   mov  ah, 0E                 ;0000:064B   int  10                     ;VIDEO - WRITE CHARACTER AND ADVANCE CURSOR                                        ;(TTY WRITE)                                        ;AL = character, BH = display page (alpha modes)                                        ;BL = foreground color (graphics modes)0000:064D   jmp  0641                   ;go back for another message---------------------------------------------------------------------------0000:064F   mov  [bp+10], cl            ;0000:0652   call 069B                   ;jump to 069B0000:0055   jnb  0681                   ;jump to 0681, if CF=0---------------------------------------------------------------------------0000:0657   inc  byte ptr [bp+10]       ;0000:065A   cmp  byte ptr [bp+04], 0B   ;0000:065E   je   066B                   ;0000:0660   cmp  byte prt [bp+04], 0C   ;0000:0664   je   066B                   ;0000:0666   mov  al, [07B6]             ;al now [07B6] = 44 -> (0744)0000:0669   jne  063D                   ;display "Error loading operating system"---------------------------------------------------------------------------0000:066B   add  byte ptr [bp+02], 06   ;0000:066F   add  word ptr [bp+08], 0006 ;0000:0673   adc  word ptr [bp+0A], 0000 ;0000:0677   call 069B                   ;0000:067A   jnb  0681                   ;jump to 0681, if CF=00000:067C   mov  al, [07B6]             ;al now [07B6] = 44 -> (0744)0000:067F   jmp  063D                   ;display "Error loading operating system"---------------------------------------------------------------------------시그니처 검사후 시그니처가 올바르지 않다면 에러 메시지를 출력한다.CHECK FOR SIGNATURE : 0000:0681   cmp  word ptr [7DFE], AA55  ;check for signature0000:0687   je   0694                   ;ok, jump to 06940000:0689   cmp  byte ptr [bp+10], 00   ;0000:068D   je   0657                   ;0000:068F   mov  al, [07B7]             ;al now [07B7] = 63 -> (0763)0000:0692   jmp  063D                   ;display "Missing operating system"---------------------------------------------------------------------------0000:0694   mov  di, sp                 ;sp is still 7C000000:0696   push ds                     ;ds now 00000000:0697   push di                     ;di now 7C000000:0698   mov  si, bp                 ;bp is still 07BE0000:069A   retf                        ;jump to 0000:7C00---------------------------------------------------------------------------부팅 가능한 파티션 테이블 엔트리를 읽은 후, 해당 파티션의 시작 위치를 찾는다.CHS 주소를 사용할 경우 0:06CA로 점프하고, LBA 주소를 사용할 경우 0:06E6로 점프한다.GET DRIVE PARAMETERS :0000:069B   mov  di, 0005               ;des index now 00050000:069E   mov  dl, [bp+00]            ;0000:06A1   mov  ah, 08                 ;AH = 08, Get Drive Parameters0000:06A3   int  13                     ;DISK - GET CURRENT DRIVE PARAMETERS                                        ;DL = drive number                                        ;Return: CF set on error, AH = status code,                                        ;BL = drive type                                        ;DL = number of consecutive drives                                        ;DH = maximum value for head number,                                        ;ES:DI -> drive parameter0000:06A5   jb   06CA                   ;jump to 06CA, if CF=1(not successful)0000:06A7   mov  al, cl                 ;0000:06A9   and  al, 3F                 ;0000:06AB   cbw                         ;convert byte to word0000:06AC   mov  bl, dh                 ;0000:06AE   mov  bh, ah                 ;0000:06B0   inc  bx                     ;0000:06B1   mul  bx                     ;0000:06B3   mov  dx, cx                 ;0000:06B5   xchg dh, dl                 ;0000:06B7   mov  cl, 06                 ;0000:06B9   shr  dh, cl                 ;0000:06BB   inc  dx                     ;0000:06BC   mul  dx                     ;0000:06BE   cmp  [bp+0A], dx            ;0000:06C1   ja   06E6                   ;check for extended INT 130000:06C3   jb   06CA                   ;read sectors from drive0000:06C5   cmp  [bp+08], ax            ;0000:06C8   jnb  06E6                   ;check for extended INT 13---------------------------------------------------------------------------CHS 주소를 사용할 경우 주소 값을 읽는다.부팅 가능한 파티션의 첫 섹터(512 bytes)를 읽어서 메모리 0:7C00에 덮어쓴다.READ DISK SECTORS USING CHS FORMAT : 0000:06CA   mov  ax, 0201               ;INT 13 AH=020000:06CD   mov  bx, 7C00               ;bx = memory buffer0000:06D0   mov  cx, [bp+02]            ;0000:06D3   mov  dx, [bp]               ;0000:06D6   int  13                     ;Read Sectors From Drive                                        ;AL = number of sectors to read, CH = track,                                        ;CL = sector                                        ;DH = head, DL = drive, ES:BX -> buffer to fill                                        ;Return: CF set on error, AH = status,                                        ;AL = number of sectors read0000:06D8   jnb  072B                   ;return0000:06DA   dec  di0000:06DB   je   072B                   ;return0000:06DD   xor  ah, ah                 ;INT 13 AH=00h0000:06DF   mov  dl, [bp+00]0000:06E2   int  13                     ;Reset Disk Drives0000:06E4   jmp  06CA                   ;go back for read sectors from drive---------------------------------------------------------------------------확장된 INT 13 명령(LBA를 사용하기 위한)을 지원하는지 검사한다.IBM/MS INT 13 EXTENSIONS - INSTALLATION CHECK : 0000:06E6   mov  dl, [bp+00]            ;dl = drive number0000:06E9   pusha                       ;push all registers onto stack0000:06EA   mov  bx, 55AA               ;bx now 55AA0000:06ED   mov  ah, 41                 ;INT 13 AH=410000:06EF   int  13                     ;Check Extensions Present0000:06F1   jb   0729                   ;if not, return with CF set0000:06F3   cmp  bx, AA55               ;bx now AA550000:06F7   jne  0729                   ;0000:06F9   test cl, 01                 ;0000:06FC   je   0729                   ;0000:06FE   popa                        ;pop alll registers from stack---------------------------------------------------------------------------확장된 INT 13 명령을 지원한 경우 LBA 주소 값을 읽는다.부팅 가능한 파티션의 첫 섹터(512 bytes)를 읽어서 메모리 0:7C00에 덮어쓴다.EXTENDED READ - READS DISK SECTORS USING LBA FORMAT : 0000:06FF   pusha                       ;push all registers onto stack0000:0700   push 00000000:0702   push 00000000:0704   push word ptr [bp+0A]0000:0707   push word ptr [bp+08]0000:070A   push 00000000:070C   push 7C000000:070F   push 00010000:0711   push 00100000:0713   mov  ah, 42                 ;INT 13 AH=420000:0715   mov  si, sp                 ; using LBA0000:0717   int  13                     ;Extended Read Sectors From Drive0000:0719   popa                        ;0000:071A   popa                        ;0000:071B   jnb  072B                   ;return0000:071D   dec  di                     ;0000:071E   je   072B                   ;return0000:0720   xor  ah, ah                 ;INT 13 AH=000000:0722   mov  dl, [bp+00]            ;0000:0725   int  13                     ;Reset Disk Drives0000:0727   jmp  06FF                   ;go back---------------------------------------------------------------------------0000:0729   popa0000:072A   stc                         ;set carry flag---------------------------------------------------------------------------0000:072B   ret
ERROR MESSAGES:0000:0720 32E48A56 00CD13EB D661F9C3 496E7661 *2..V.....a..Inva*0000:0730 6C696420 70617274 6974696F 6E207461 *lid partition ta*0000:0740 626C6500 4572726F 72206C6F 6164696E *ble.Error loadin*0000:0750 67206F70 65726174 696E6720 73797374 *g operating syst*0000:0760 656D004D 69737369 6E67206F 70657261 *em.Missing opera*0000:0770 74696E67 20737973 74656D00 00000000 *ting system.....*

 

예상한 대로 MBR 영역의 부트 코드는 파티션 테이블 엔트리에서 부팅 가능한 엔트리를 찾은 후, 해당 엔트리 외에 부팅 가능한 엔트리가 추가적으로 있는지 검사한다. 부팅 가능한 엔트리가 하나라면 해당 엔트리를 해석하여 CHS 주소, LBA 주소 사용에 따라 각각 점프하여 해당 주소 값을 읽는다. 최근에는 CHS 주소를 사용하지 않으므로 항상 LBA 루틴으로 점프한다.

이후 주소를 읽어 부팅 가능한 파티션의 첫 번째 섹터(부트 섹터)를 메모리 0:7C00 번지에 로드한다. 부트 섹터에 대한 부트 코드는 추후에 알아보도록 하자.


덧글

댓글 입력 영역


통계 위젯 (블랙)

08
98
411790