MBR – 부트 코드 분석 (Boot Code Analysis)
이전 포스팅에서 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 reg
0000:7C02 mov ss, ax ;set zero stack seg reg
0000:7C04 mov sp, 7C00 ;set stack ptr to 0000:7C00
0000:7C07 sti ;enable interrupts
0000:7C08 push ax ;AX=0 at this point
0000:7C09 pop es ;ES(Extra Segment) now 0
0000:7C0A push ax ;AX=0 at this point
0000:7C0B pop ds ;DS(Data Segment) now 0
0000:7C0C cld ;clear direction flag
0000:7C0D mov si, 7C1B ;SI(Src Index) now 7C1Bh
0000:7C10 mov di, 061B ;DI(Des Index) now 061Bh
0000:7C13 push ax ;set up AX and DI
0000:7C14 push di ; form jump to 0000:061B
0000:7C15 mov cx, 1E5 ;move as 1E5(512-27=485) bytes
0000:7C18 repz movsb ;move a portion of MBR from 7C1B to 061B
0000: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 entry
0000:061E mov cl, 04 ;maximum table entries are 4
0000:0620 cmp [bp+00], ch ;(ch=0)is this a non-bootable entry?
0000:0623 jl 062E ;no, this is bootable entry
0000:0625 jnz 063A ;jump to error message navigator
0000:0627 add bp, 0010 ;next to partition table entry
0000:062A loop 0620 ;jump to 0620h unless cl=0
0000: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 here
0000:0634 jz 064F ;jump to 064F
0000: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 07
0000: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 loop
0000: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 069B
0000: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=0
0000: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 signature
0000:0687 je 0694 ;ok, jump to 0694
0000: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 7C00
0000:0696 push ds ;ds now 0000
0000:0697 push di ;di now 7C00
0000:0698 mov si, bp ;bp is still 07BE
0000:069A retf ;jump to 0000:7C00
---------------------------------------------------------------------------
부팅 가능한 파티션 테이블 엔트리를 읽은 후, 해당 파티션의 시작 위치를 찾는다.
CHS 주소를 사용할 경우 0:06CA로 점프하고, LBA 주소를 사용할 경우 0:06E6로 점프한다.
GET DRIVE PARAMETERS :
0000:069B mov di, 0005 ;des index now 0005
0000:069E mov dl, [bp+00] ;
0000:06A1 mov ah, 08 ;AH = 08, Get Drive Parameters
0000: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 parameter
0000: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 word
0000: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 13
0000:06C3 jb 06CA ;read sectors from drive
0000: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=02
0000:06CD mov bx, 7C00 ;bx = memory buffer
0000: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 read
0000:06D8 jnb 072B ;return
0000:06DA dec di
0000:06DB je 072B ;return
0000:06DD xor ah, ah ;INT 13 AH=00h
0000:06DF mov dl, [bp+00]
0000:06E2 int 13 ;Reset Disk Drives
0000: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 number
0000:06E9 pusha ;push all registers onto stack
0000:06EA mov bx, 55AA ;bx now 55AA
0000:06ED mov ah, 41 ;INT 13 AH=41
0000:06EF int 13 ;Check Extensions Present
0000:06F1 jb 0729 ;if not, return with CF set
0000:06F3 cmp bx, AA55 ;bx now AA55
0000: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 stack
0000:0700 push 0000
0000:0702 push 0000
0000:0704 push word ptr [bp+0A]
0000:0707 push word ptr [bp+08]
0000:070A push 0000
0000:070C push 7C00
0000:070F push 0001
0000:0711 push 0010
0000:0713 mov ah, 42 ;INT 13 AH=42
0000:0715 mov si, sp ; using LBA
0000:0717 int 13 ;Extended Read Sectors From Drive
0000:0719 popa ;
0000:071A popa ;
0000:071B jnb 072B ;return
0000:071D dec di ;
0000:071E je 072B ;return
0000:0720 xor ah, ah ;INT 13 AH=00
0000:0722 mov dl, [bp+00] ;
0000:0725 int 13 ;Reset Disk Drives
0000:0727 jmp 06FF ;go back
---------------------------------------------------------------------------
0000:0729 popa
0000: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 번지에 로드한다. 부트 섹터에 대한 부트 코드는 추후에 알아보도록 하자.
-
http://www.facebook.com/ruony Eun Gyun Kim
-
http://www.facebook.com/proneer Jinkook Kim
Categories
- 0×01 News (16)
- 0×02 Fundamentals (11)
- 0×03 Data Forensics (12)
- 0×04 Storage Forensics (14)
- 0×05 File System Forensics (31)
- 0×06 Windows Forensics (19)
- 0×07 *nix Forensics (2)
- 0×09 Web Forensics (5)
- 0x0A Virtual Forensics (5)
- 0x0B Forensic Challenges (15)
- 0x0C Forensic Education (8)
- 0x0D EnCase (12)
- 0x0E Forensic Tools (8)
- 0x0F Slides (24)
- 0×10 Articles (2)
addressing artifacts BIOS boot code boot process challenge Cluster Codegate cookie Data Carving Data recovery DC3 DCO defcon ENCASE EnCE encoding EnScript exFAT FAT File System firmware Forensic Challenge hardware imaging Live Forensics live response mbr network ntfs padocon process RAID Recycle Bin SCSI signature Slack slide SSD steganography timeline timestamp virtual forensics WDFS writeup
WP Cumulus Flash tag cloud by Roy Tanck requires Flash Player 9 or better.
What I'm Doing...
- E-evidence info updated!! http://t.co/x9xNaFC6 9 hrs ago
- Always appreciate your links, http://t.co/g6sNUg5Q 10 hrs ago
- Searching With VirusTotal using VirusTotal's API, http://t.co/b0qdYIKU 10 hrs ago
- More updates...
Recent Comments
- Jinkook Kim on 리눅스 메모리 포렌식 개요 (An Introduction to Linux Memory Forensics)
- blesslaw on 리눅스 메모리 포렌식 개요 (An Introduction to Linux Memory Forensics)
- MaJ3stY on 리눅스 메모리 포렌식 개요 (An Introduction to Linux Memory Forensics)
- Jinkook Kim on 리눅스 메모리 포렌식 개요 (An Introduction to Linux Memory Forensics)
- MaJ3stY on 리눅스 메모리 포렌식 개요 (An Introduction to Linux Memory Forensics)


