[파일시스템]MBR

임정환·2023년 9월 6일
0

컴퓨터에 전원이 들어오면, 바이오스가 BootSector(0x00..)에서 부트로더를 메모리에 적재한다. 원래는 운영체제가 존재했으나, 현대의 운영체제는 512바이트 안에서 해결될 수 없을만큼 거대하고 커다란 용량을 자랑하기에 메모리에 적재된 부트로더가 다시 커널을 메모리에 적재시키는 역활을 한다.

파일시스템 레이아웃

흔히 윈도우를 사용하다보면 , C드라이브 및 D드라이브등으로 하드 디스크가 파티션 되어있다는 것을 볼 수 있다. 각 파티션은 , 독립적으로 쓰이는 개별적 파일 시스템으로 생각해볼 수 있는데 지금부터 파일시스템 디스크 레이아웃과 파일시스템 구현에 대해 알아보자

MBR

Master Boot Record , 이하 MBR은 하드디스크 0byte에서 512byte에 존재한다. 한 개의 sector는 512byte이므로 0 번째 sector에 존재한다고 생각해도 무방하다.

MBR layout

  • 0~446 byte
    • Boot코드가 존재한다. 부트로더 바이너리라고 생각하자
  • 446~510 byte
    • 16byte짜리 partition table entry 4개가 존재한다.
  • 510~512 byte
    • boot 시그니처가 존재

Partion table entry

첫 512바이트 부트섹터에서 , offset 446부터 64바이트에 PTE가 들어있다.
그럼 한가지 의문이 발생하는데 , 'MBR에서 PTE가 16바이트라는데 그럼 디스크 레이아웃을 4개밖에 생성 못하나?' 라는 의문이 발생할 수 있다. MBR은 이 문제를 Extended Boot Record로 해결하였다. 이는 조금 나중에 알아보도록 하고 , 먼저 PTE의 구조에 대해 살펴보자 .


위의 PTE를 보면 LBA Address of Start를 통해서 파티션의 시작 위치와 , Num of sectors로 해당 파티션의 볼륨을 알아낼 수 있다.

Extended Boot Record

MBR이 4개의 파티션을 나눌 수 밖에 없다는 단점이 존재하므로 , 이를 해결하기 위한 방안이다. 기본적으로 중첩 구조를 사용해서 , 추가적인 Partion Table Entry를 표현할 수 있게 만들었다. 3번째 인덱스의 PTE offset으로 이동할 경우 EBR이 나오게 되고 , MBR과 유사한 형식을 따르고 있다.


계속 디스크 레이아웃이 확장될경우, EBR의 PTE가 다음 EBR을 가르키도록 링크드 리스트구조를 따르고 있다. 첫번째 EBR을 base로 하여, 두번째 EBR부터의 offset을 계산하게된다.

Parsing code

import sys 
import struct 


_PTE_STRUCT = "<1s3s1s3sII"
_EBR_STRUCT = "<446s16s16s16s16s2s"
_PARTITION_START = 446
_SECTOR_SIZE = 512

def usage():
    print("Usage : python mbr_parser.py <image name> ")

types = {
    b'\x07' : "NTFS",
    b'\x05' : "Extend",
    b'\0B' : "FAT32(CHS)",
    b'\0C' : "FAT32(LBA)"
}


if len(sys.argv)!=2:
    usage()
    exit(-1)

image_name = sys.argv[1]

try:
    image_file = open("./"+image_name,'rb')
except:
    print("can not open file")
    exit(-1)

partionTableEntries = []

#Parse Table Entries
for i in range(4):
    image_file.seek(_PARTITION_START+ 16*i)
    TableEntry = image_file.read(16)
    partionTableEntries.append(TableEntry)

partitions = []

#MBR 엔트리 분석
for idx,entry in enumerate(partionTableEntries):
    partitionTableEntry = struct.unpack(_PTE_STRUCT,entry)
    isActive = partitionTableEntry[0]
    CHS_Address = partitionTableEntry[1]
    partionType = partitionTableEntry[2]
    CHS_Address = partitionTableEntry[3]
    LBA_Address = partitionTableEntry[4]
    Num_of_Sector = partitionTableEntry[5]
    startAddress = LBA_Address*_SECTOR_SIZE
    size = Num_of_Sector*_SECTOR_SIZE
    
    if types[partionType]=="Extend":
        pivotAddress = startAddress
        offset = 0
        while True:
            nowAddress = pivotAddress+offset
            #EBR로 이동 및 Read 
            image_file.seek(nowAddress) # EBR 섹터 시작 주소 
            EBR = image_file.read(_SECTOR_SIZE) # 512 byte read 
            EBR = struct.unpack(_EBR_STRUCT,EBR)
            partition = EBR[1] 
            nextEBR = EBR[2]
            

            # 여기서부터는 상대 주소를 사용해서 file seek
            unpacked_partition = struct.unpack(_PTE_STRUCT,partition)
            isActive = unpacked_partition[0]
            CHS_Address = unpacked_partition[1]
            partionType = unpacked_partition[2]
            CHS_Address = unpacked_partition[3]
            LBA_Address = unpacked_partition[4]
            Num_of_Sector = unpacked_partition[5]
            startAddress = (nowAddress+LBA_Address)*_SECTOR_SIZE
            size = Num_of_Sector*_SECTOR_SIZE
            
            partitions.append([
                types[partionType],
                str(startAddress),
                str(size)
            ])

            if(int.from_bytes(nextEBR,byteorder='big')==0):
                break
            
            unpacked_nextEBR = struct.unpack(_PTE_STRUCT,nextEBR)
        
            isActive = unpacked_nextEBR[0]
            CHS_Address = unpacked_nextEBR[1]
            partionType = unpacked_nextEBR[2]
            CHS_Address = unpacked_nextEBR[3]
            start_Address = unpacked_nextEBR[4]
            Num_of_Sector = unpacked_nextEBR[5]

            offset = start_Address*_SECTOR_SIZE
            
            

    else:
        partitions.append([
            types[partionType],
            str(startAddress),
            str(size)
        ])



for i,partition in enumerate(partitions):

    print("index : "+str(i+1))
    print("     FileSystem : "+partition[0])
    print("     StartAddress : "+partition[1])
    print("     Size : "+partition[2])

조금 길기는 하지만 , MBR 이미지 파일을 통해 각 파티션의 정보를 파싱하고 출력해볼 수 있는 코드이다. 구조체와 바이트 스트림을 python으로 다룰려니 상당히 애를 먹었지만 , struct 모듈 사용법을 알게되어서 굉장히 재미있었다.

profile
CS 박제

0개의 댓글

관련 채용 정보