Digital Forensics/File System

#2 GPT - GUID Partition Table (2)

geunyeong 2022. 1. 8. 01:57

Table of Contents

    Abstract

    저번 글에서는 UEFI와 GPT에 대한 개요, Protective MBR에 대해 설명했다. 본 글에서는 GPT의 주요 내용인 GPT 헤더와 파티션 테이블, 백업 GPT에 대한 설명을 진행한다.

    UEFI와 GPT에 대한 개요, Protective MBR에 대한 설명이 필요하다면 아래 링크를 따라가면 된다.

    • #1 GPT - GUID Partition Table (1)
    https://geun-yeong.tistory.com/70

    GPT

    본 글을 작성하는 데 사용한 GPT VHD 이미지는 아래 링크에서 다운로드 할 수 있다.

    https://drive.google.com/file/d/1BjRI-70riDwOUxK8C0MxT4vtb2t3lRFq/view?usp=sharing

    들어가기 전에 GPT 시스템의 디스크 레이아웃 그림을 보고 가자.

    GPT Disk Layout

    GPT(GUID Partition Table)

    거의 대부분의 내용이 UEFI Specification 2.9를 참고해 작성됐다. 그래서 처음 보는 용어가 나타날 수 있으니 당황하지 말길 바란다. 어느정도 설명을 추가하거나 익숙한 단어로 치환했다.

    GPT의 가장 큰 특징으론 LBA를 지정하는 필드의 크기가 8바이트로 늘어났다는 점이다. 섹터 크기를 512 바이트로 가정해도 512 * (2^64) 이라는 어마어마한 크기의 디스크를 인식할 수 있게 됐다는 것이 가장 큰 특징이다. 

    GPT Header

    GPT 헤더는 시그니처와 리비전 번호, GPT Partition Entry Array(파티션 테이블)의 위치, 개수 등 GPT 디스크 레이아웃을 구성하는 데 필요한 다양한 데이터들을 갖고 있다. GPT 헤더는 Primary GPT Header와 Backup GPT Header, 2 헤더가 디스크 상에 존재하는데, Primary GPT 헤더는 반드시 LBA 1 위치의 섹터에 존재해야 하며 Backup GPT Header는 마지막 LBA 위치의 섹터에 존재하게 된다.

    LBA 1 위치의 Primary GPT Header

    Primary GPT Header의 내용은 아래 표와 같다.

    영역 오프셋(Hex) 크기 내용
    Signature 0 8 EFI 호환 파티션 테이블 헤더임을 식별할 수 있는 시그니처다. 8자의 ASCII 문자열로 된 "EFI PART"가 저장된다.
    Revision 8 4 이 헤더의 리비전 번호를 의미한다. 리비전 번호 값은 UEFI Specification 버전과 무관하며, 1.0을 의미하는 경우 0x00010000 값이 저장된다.
    4 바이트 중 상위 2 바이트가 Major, 하위 2 바이트가 Minor를 의미하도록 한 거 같은데 UEFI Specification에 그런 말이 없어서 확신은 못하겠다.
    HeaderSize 12(0xC) 4 GPT 헤더의 크기다. 이 값은 92(0x5C)보다 크거나 같아야 하고 Logical Block Size(섹터 크기)보다 작거나 같아야 한다. 위 그림에서도 알 수 있듯 기본값으론 92(0x5C)를 가진다.
    92(0x5C) <= HeaderSize <= SectorSize
    HeaderCRC32 16(0x10) 4 GPT 헤더에 대한 CRC32 체크섬 값이다. HeaderCRC32 값을 만들 때는 이 값을 0으로 비운 후 HeaderSize 만큼에 대한 CRC32 값을 계산하고, HeaderCRC32 위치에 저장한다.
    위 그림에선 0xE67F36AD다.
    Reserved 20(0x14) 4 0으로 설정되어야 한다.
    MyLBA 24(0x18) 8 현재 GPT Header를 가리키는 LBA 값이다. Primary GPT Header라면 LBA 1이고, Backup GPT Header라면 마지막 섹터의 LBA 값이 저장된다.
    위 그림에선 1이다.
    AlternateLBA 32(0x20) 8 상호 GPT Header의 위치를 가리키는 LBA 값이다. Priamry GPT Header라면 Backup GPT Header의 LBA가, Backup GPT Header라면 Primary GPT Header의 LBA가 저장된다.
    위 그림에선 8,388,609(0x7FFFFF)다.
    FirstUsableLBA 40(0x28) 8 GUID Partition Entry에 서술된 파티션에 의해 사용되는 사용 가능한 첫번째 Logical Block을 나타낸다. 쉽게 말해 파티셔닝 되는, 즉 실제 사용 영역의 시작 LBA다.
    위 그림에선 34(0x22)다.
    LastUsableLBA 48(0x30) 8 GUID Partition Entry에 서술된 파티션에 의해 사용되는 사용 가능한 마지막 Logical Block을 나타낸다. 쉽게 말해 파티셔닝 되는, 즉 실제 사용 영역의 마지막 LBA다.
    위 그림에선 8,388,574(0x7FFFDE)다.
    DiskGUID 56(0x38) 16 디스크를 고유하게 식별할 수 있도록 하는 GUID 값이다.
    위 그림에선 F8463214-BB31-4888-9C80-322FCC9BC031이다.
    PartitionEntryLBA 72(0x48) 8 GUID Partition Entry Array가 위치한 섹터의 LBA 값이다.
    위 그림에선 2로, Primary GPT Header 바로 뒤 섹터에 나타난다.
    NumberOfPartitionEntries 80(0x50) 4 GUID Partition Entry Array에 저장될 수 있는 Partition Entry의 개수를 나타낸다. 현재 존재하는 파티션의 개수가 아니라 최대 개수임을 알아두자.
    위 그림에선 0x80으로 128개가 존재할 수 있음을 나타내고 있다.
    SizeOfPartitionEntry 84(0x54) 4 GUID Partition Entry Array에서 각 GUID Partition Entry 구조체의 크기를 나타낸다. 이 필드의 크기는 128 * (2^n)으로 설정되며 n은 0보다 크거나 같다. 즉 128, 256, 512, ... 순으로 커질 수 있다.
    위 그림에선 0x80으로 128 바이트 크기의 엔트리 구조체가 128개 있을 수 있다는 걸 알 수 있다.
    PartitionEntryArrayCRC32 88(0x58) 4 GUID Partition Entry Array 영역 전체에 대한 CRC32 체크섬 값이다. 파티션 개수가 몇개든 상관없이 NumberOfPartitionEntries * SizeOfPartitionEntry 크기 만큼을 대상으로 CRC32 체크섬을 계산한다.
    위 그림에선 0x3B43B040이다.
    Reserved 92(0x5C) Remained bytes 0으로 설정되어야 한다.

    GPT가 올바른지 확인하는 방법은 아래와 같다.

    • Signature("EFI PART")를 검사한다.
    • Header CRC를 검사한다.
    • MyLBA 엔트리가 GUID 파티션 테이블을 포함하는 LBA를 가리는지 검사한다.
    • GUID Partition Entry Array에 대한 CRC를 검사한다.
    • (만약 Primary GPT Header가 LBA 1에 있다면) AlternateLBA가 올바른 GPT인지 검사한다.

    Backup GPT Header는 Primary GPT Header의 Alternate GPT Header라고도 한다. 반대로 Backup GPT Header의 Alternate GPT Header는 Primary GPT Header다. Alternate의 뜻대로 '상호참조 GPT'인 셈이다. Primary GPT가 망가졌다면 Primary GPT의 Alternate인 Backup GPT를 참조해 Primary GPT를 복원할 수 있다. 단 이러한 소프트웨어들은 Primary GPT를 복원할 때 마다 사용자에게 확인을 요청하고 보고해야 한다.

    Primary GPT를 업데이트하는 어떤 소프트웨어든 Backup GPT도 함께 업데이트해야 한다. GPT Header와 GPT Partition Entry Array의 무엇이라도 업데이트 했다면 GPT 헤더에 포함된 모든 CRC를 업데이트 해야 한다.

    GPT Partition Entry Array(a.k.a. Partition Table)

    GPT Partition Entry Array는 GPT Partition Entry들을 포함하는 영역이다. GPT Partition Entry Array의 크기는 GPT 헤더의 NumberOfPartitionEntries와 SizeOfPartitionEntry를 곱한 값이다. 각 엔트리 구조의 크기가 128 바이트고, 최대 128개 엔트리가 존재할 수 있다면 GPT Partition Entry Array 영역의 크기는 16,384 바이트가 된다. 또한 섹터 크기가 512 바이트라면 각 섹터 당 4개의 엔트리가 들어갈 수 있으므로 총 32개 섹터가 GPT Partition Entry Array 영역으로 사용된다.

    LBA 2에 위치한 4개 Partition Entries

    아래 표는 GPT Partition Entry의 구조를 설명한 것이다.

    영역 오프셋(Hex) 크기 내용
    PartitionTypeGUID 0 16 MBR 시스템에서 OSType과 비슷한 역할로, 파티션의 목적와 유형을 나타내는 GUID 값이다. 파티션 타입 GUID는 위키피디아에 잘 정리돼 있다. 만약 이 영역이 모두 널 바이트로 채워져있다면 해당 엔트리는 사용되지 않는다는 것을 의미한다.
    https://ko.wikipedia.org/wiki/GUID_파티션_테이블#파티션_유형_GUID
    UniquePartitionGUID 16(0x10) 16 전체 GPT Partition Entry 중에서 고유하게 갖는 GUID로 생성될 때 할당받는다.
    StartingLBA 32(0x20) 8 해당 엔트리가 정의하는 파티션 영역의 시작 섹터 LBA다.
    EndingLBA 40(0x28) 8 해당 엔트리가 정의하는 파티션 영역의 마지막 섹터 LBA다.
    Attributes 48(0x30) 8 UEFI를 위해 예약된 속성 필드다. 궁금하다면 UEFI Specification을 참조하자.
    PartitionName 56(0x38) 72 널 문자로 끝나는 문자열로 사람이 읽을 수 있는(human-readable) 멀티 바이트 파티션 이름이다. 우리가 흔히 생각하는 볼륨 레이블(c 드라이브의 경우 "Local Disk")과는 다르다.
    Reserved 128(0x80) <can be exist> GPT Partition Entry의 남은 공간으로 UEFI에 의해 예약되었으며 널 바이트로 채워져야 한다. 일반적인 경우라면 GPT 헤더의 SizeOfPartitionEntry가 128이기 때문에 이 공간이 실존하진 않지만 스펙 상으로는 존재할 수 있다.
    만약 존재하는 경우 그 크기는 SizeOfPartitionEntry - 128이다.

    PartitionTypeGUID에 대한 대표적인 예시는 아래와 같다. PartitionTypeGUID에 들어가는 값들은 이미 정의된 값들이기 때문에 아무 값이나 넣으면 안된다.

    • Unused Entry: 00000000-0000-0000-0000-000000000000
    • EFI System Partition: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
    • Partition containing a legacy MBR: 024DEE41-33E7-11D3-9D69-0008C781F39F

    시작 LBA와 섹터 크기를 나타냈던 MBR 시스템과 다르게 CHS처럼 시작 섹터 LBA와 마지막 섹터의 LBA를 저장한다. 때문에 GPT 시스템에서 파티션의 크기를 구하려면 EndingLBA - StartingLBA를 해야 한다. PartitionName은 36자의 멀티 바이트를 가지며(널 바이트로 끝나야 하기 때문에 실제론 35자다), 보통 "Basic data partition"이라는 이름을 가진다. 볼륨 레이블과 다름을 유의해야 한다. EFI 시스템 파티션인 경우 "EFI system partition"이라는 문자열이 저장된다.

    Backup GPT(or Secondary GPT)

    앞서 GPT 헤더는 Primary와 Backup이 존재한다고 했다. 또한 서로가 서로의 Altenate GPT라는 설명도 했다. LBA 1 위치에 있는 GPT 헤더를 Primary GPT Header라고 하고, 디스크 내 전체 Logical Block(섹터) 중 가장 마지막 섹터에 위치한 GPT 헤더를 Backup GPT Header라고 한다. 아래 그림은 Backup GPT 영역이다.

    마지막 섹터에 위치한 Backup GPT Header

    Backup GPT Header의 MyLBA는 Backup GPT Header의 LBA를, AlternateLBA 값은 Primary GPT Header의 LBA 가진다. 때문에 Backup GPT Header와 Primary GPT Header의 CRC 값이 다르다. 복원할 때 GPT 헤더에 존재하는 모든 CRC 값을 새로 계산해야 한다. Backup GPT Header를 사용해 Primary GPT 헤더를 복원할 때 주의해야 할 점이다. 또한 Primary GPT를 수정했다면 Backup GPT도 함께 업데이트 해야 한다.

    GPT 헤더 뿐만 아니라 GPT Partition Entry Array도 백업본이 존재한다. Backup GPT Partition Entry Array라고 하며 Primary GPT Partition Entry Array와 같은 크기로 Backup GPT Header 바로 앞에 존재한다. LBA 상으론 일반적으론 마지막 LBA에서 33을 뺀 영역부터 시작한다. GPT 헤더의 NumberOfPartitionEntries 값에 따라 이 영역의 크기는 달라질 수 있다.

    Backup GPT Partition Entry Array

    Backup GPT Header와 Backup GPT Partition Entry Array 영역을 합쳐 Backup GPT라고 한다.

    Close

    이상으로 GPT  헤더와 Partition Entry Array를 분석하는 방법에 대해 알아봤다. 얼핏 복잡해 보이지만 각 필드 크기가 큼직하고 복잡한 내용을 담은 필드가 없어 이해하는 데 큰 어려움은 없었을 것이라 생각한다.

    이번 글에서는 Backup GPT에 대한 설명이 조금 빈약한데, 추후 Backup GPT를 통해 Primary GPT를 복원하는 방법에 대한 글에서 상세하게 설명할 예정이다.

    'Digital Forensics > File System' 카테고리의 다른 글

    File System 개론  (0) 2022.01.15
    #3 GPT - GPT 복구  (0) 2022.01.08
    #1 GPT - GUID Partition Table (1)  (0) 2022.01.06
    #3 MBR - 파티션 테이블 복구  (0) 2022.01.02
    #2 MBR - Extended Partition Table  (0) 2022.01.02