2012. 12. 18. 12:43

Partition(파티션) 정보 [MBR/Primary(주)/Extended(확장)/Logical(논리)/GPT] 구하기 (Visual C++/GCC)

(CCL 라이센스. 상업용 사용 금지. 관련 내용은 아래 부분 참조)

fdisk, diskpart, 디스크 관리자, 그리고 gparted

과거의 windows나 linux에 fdisk라는 툴이 있었는데, 단위 Harddisk에서 파티션 관련 Operation을 수행하는 툴이였습니다. 보통 일반 유저는 쉽게 다가설 수 없는 내용들로 구성되어 있는데, 왜냐하면 Partition이라는 이론을 알아야 했고, 잘못 설정하는 경우 Disk를 날릴 수 있기 때문에 쉽지 않았습니다.

최근 window에서는 diskpart 혹은 '디스크 관리자'를 통해 fdisk 시절 보다 쉽게 접근할 수 있으며, linux에서는 보통 gparted 툴을 이용해 손쉽게 접근할 수 있습니다.

C:\Windows\system32>diskpart

Microsoft DiskPart 버전 6.1.7601
Copyright (C) 1999-2008 Microsoft Corporation.
컴퓨터: GREENFISH-PC

DISKPART> help

Microsoft DiskPart 버전 6.1.7601

ACTIVE      - 선택한 파티션을 활성으로 표시합니다.
ADD         - 단순 볼륨에 미러를 추가합니다.
...
UNIQUEID    - 디스크의 GPT(GUID 파티션 테이블) 식별자 또는
              MBR(마스터 부트 레코드) 서명을 표시하거나 설정합니다.

DISKPART>

- diskpart 실행 화면

- 디스크 관리자 실행 화면 (내컴퓨터->오른쪽 클릭->관리->디스크 관리)

- linux의 gparted 실행 화면 ($ sudo gparted) 만일 없는 경우 ($ sudo apt-get install gparted)로 설치 필요

partition과 forensic

포렌식(위키)은 보통 IT 장치의 정보를 기반으로 수집된 정보를 가지고 사법 기관등에 사건의 증거로 제출하는 과정으로 정의될거 같습니다. 법정에서 증거로 채택되기 위해서는 몇가지 제약사항들이 있습니다. (변경되지 않았음을 보장, 등등등). 일반적으로 보통 디스크의 문제가 되는 PC의 하드 디스크를 Imaging 백업한뒤 해당 File system을 분석하는 과정이 주를 이룰 수 있습니다. 그러기 위해서는 일반적인 OS 제공의 File 접근법이 아니라 Raw Level의 접근법을 따라야 할 때가 있는데, 그 첫번째 단계가 Disk의 Partition 논리를 알아야 하는 것입니다. 혹시나 범죄자가 Disk Partition을 변경/삭제할 수 있으니깐요. Partition에 대한 논리가 이해된 다음 NTFS등 일반적인 File system의 이론으로 진행해야 할거 같습니다.

PartitionInfo (Windows(Visual C++) / Linux(GCC)) 실행해 보기

위 PartitionInfo의 소크 코드와 바이너리를 공유합니다.
[ CCL license 조항을 주의하세요. No commercial only 이므로 상업적 용도를 금지합니다.
상업적 용도 사용시 저에게 사전에 알려주세요.]

내부 코드는 class가 있는 C++이지만, 모두 class의 static 함수로 이뤄져 있습니다. 즉, C++이지만 c 형태의 코드로 이뤄져 있습니다. 그러니 조금만 수정하면 c로 porting 가능할 것이므로, driver등에서 쉽게 적용할 수 있습니다.

PartitionInfo는 fdisk 같이 Disk 경로를 입력받아 Partition 정보를 출력합니다. 물론 Read-only로 수행되므로 Partition 정보 설정은 제공되지 않습니다. 그리고, PartitionInfo.sln과 Makefile이 동시에 제공되는데, 이는 하나의 소스 코드로 Windows와 Linux를 모두 지원한다는 뜻입니다. 포렌식 작업 특징상 Linux로 부팅하여 분석해야 할 경우가 발생할 수 도 있는데, 그때를 위해서 Multi-platform/multi-compiler를 지원하게 되었습니다. Linux의 compile은 다음과 같습니다.
# make clean
rm PartHelper.o PartitionInfo.o
rm PartitionInfo
# make
g++  -c -O -I/usr/local/include -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE  -c -o PartHelper.o PartHelper.cpp
g++  -c -O -I/usr/local/include -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE  -c -o PartitionInfo.o PartitionInfo.cpp
gcc -o PartitionInfo PartHelper.o PartitionInfo.o
#
(작업의 특징상 PartitionInfo는 모두 관리자 모드(sudo)에서 진행이 가능하다는 점을 알아두시기 바랍니다.)

실행 Argument는 다음과 같습니다. (windows에서도 동일하게 동작합니다.)
# ./PartitionInfo
Usage : PartitionInfo <device>  [/d [startsector sectorsize]]
<device>
 Windows :
\\.\PhysicalDrive2
 Linux : /dev/sda
/d : Dump disk to stdout
  startsector : Begin Sector
  sectorsize  : Dump Sector Length
즉, PartitionInfo 다음에 device가 옵니다. window에서는 \\.\PhysicalDriven 형태로 입렵합니다. n은 Disk 번호인데, windows 같은 경우 다음과 같이 레지스트리를 통해 가져올 수 있습니다.


즉, HKLM\SYSTEM\CurrentControlSet\services\Disk\Enum에 그 정보가 있습니다.
Linux에서는 /dev/xdx와 같은 형태인데 다음과 같이 그 정보를 가져올 수 있습니다.
$ ls /dev/?d?
/dev/fd0  /dev/sda  /dev/sdb  /dev/sdc  /dev/sdd  /dev/sde
아니면 ls /dev/sd? /dev/hd? 와 같이 할  수도 있습니다.
다음과 같이 0번 디스크의 파티션 정보를 가져와 보겠습니다.(window)
(cmd.exe를 반드시 마우스 오른쪽 클릭한뒤 '관리자 권한으로 실행'으로 하시기 바랍니다.)
E:\....\>PartitionInfo.exe \\.\PhysicalDrive0
*******
* MBR *
*******
> Disk :
\\.\PhysicalDrive0
------------------
* Partition no : 0
------------------
        Boot Type : 1 (ENUM_BOOT_TYPE)
        Type : 4 (ENUM_PART_TYPE)
        Seek Pos : 1048576 (Sector : 2048)
        Size :  31.25 GB (33554432000 Bytes) (Sector : 65536000)
------------------
* Partition no : 1
------------------
        Boot Type : 2 (ENUM_BOOT_TYPE)
        Type : 4 (ENUM_PART_TYPE)
        Seek Pos : 33555480576 (Sector : 65538048)
        Size :  28.37 GB (30465327104 Bytes) (Sector : 59502592)
즉, 위와 같이 2개의 Partition으로 분리되어 있음을 알 수 있으며, 각 Partition의 Disk상의 절대 위치(Seek 위치)와 크기를 전달하기 때문에, 임의접근(Random access)가 가능합니다.
Linux에서는 다음과 같습니다.

$ sudo /tmp/PartitionInfo /dev/sdb
[sudo] password for greenfish:
*******
* MBR *
*******
> Disk : /dev/sdb
------------------
* Partition no : 0
------------------
        Boot Type : 2 (ENUM_BOOT_TYPE)
        Type : 4 (ENUM_PART_TYPE)
        Seek Pos : 32256 (Sector : 63)
        Size :   4.70 GB (5048675840 Bytes) (Sector : 9860695)
------------------
* Partition no : 1
------------------
        Boot Type : 2 (ENUM_BOOT_TYPE)
        Type : 3 (ENUM_PART_TYPE)
        Seek Pos : 5048892416 (Sector : 9861118)
        Size :  35.30 GB (37899731968 Bytes) (Sector : 74022914)

        [Extend]
        ---------------------------
        * Logical Partition no : 0
        ---------------------------
                Boot Type : 2 (ENUM_BOOT_TYPE)
                Type : 6 (ENUM_PART_TYPE)
                Seek Pos : 1024 (Sector : 2)
                Size :  34.80 GB (37363908608 Bytes) (Sector : 72976384)
                * Absolute Seek Pos : 5048893440 (Sector : 9861120)
        ---------------------------
        * Logical Partition no : 1
        ---------------------------
                Boot Type : 2 (ENUM_BOOT_TYPE)
                Type : 5 (ENUM_PART_TYPE)
                Seek Pos : 1048576 (Sector : 2048)
                Size : 510.00 MB (534773760 Bytes) (Sector : 1044480)
                * Absolute Seek Pos : 42413850624 (Sector : 82839552)

Partition은 크게 두개로 나눠져 있으며, 두번째는 Extended Partition으로 두개의 Logical Parrtition을 가지고 있습니다. Logical Partiton은 확장 Partition의 내부에 위치하는 Partition으로, 개수의 제한을 받지 않는 특징이 있습니다. Extended Partition은 내부의 Logical Partition의 Holder일 뿐, 의미있는 정보는 없습니다. 그리고 Logical Partition의 기본 정보는 Extended Partition으로 부터 상대적 위치를 나타내므로, Disk 자체의 절대 위치를 계산해야 하는데, 해당 정보를 전달해 주고 있습니다.

그리고, 끝에 /d 옵션을 사용할 수 있습니다. 이는 Disk의 전체 내용을 Hex로 볼 수 있도록 해줍니다. (stdout)
일부만 보고 싶으면, /d 0 1과 같이 하여, 0번 Sector부터 1개의 Sector만 표시하도록 해 줍니다.
만일 linux 환경이라면, # PartitionInfo /dev/sda | gzip -c > /tmp/sda.txt와 같이 압축할 수 있습니다.

Code에 있는 자세한 내용은 이후를 참고하시기 바랍니다.

MBR or GPT
이제부터 Disk의 Partition에 대해 알아 보도록 하겠습니다.

참고)
- Disk도 하나의 '파일'임. 즉, Read/Write/Seek가 가능함 (단, 그 단위는 Sector 크기(512)의 배수여야 함)
- Disk 상의 위치를 지정하기 위해서 다음의 정보가 활용됨(Seek)
  ; CHS 표기법 : C(cylinder)H(header)S(sector) (wiki)
  ; LBA 표기법 : Logical Block Addressing (wiki)
  ; CHS <--> LBA 가능.
- Disk 상의 논리적 위치 : LBA * Sector의 크기
- Sector의 크기
  ; 일반적으로 512bytes
  ; 2009년 12월부터 Harddisk 제조사는 4KByte Sector의 하드 디스크 소개시작 (wikiwiki)
  ; 512byte의 가정이 워낙 오래되어 있어 (BIOS, 부팅 로더, ...) 4KByte로 되어 있는 경우 부팅 불가일 수 있음
  ; Harddisk firmware로 부터 512byte로 emulate가능 (wiki)
  ; 본 블로그 포스트는 Sector의 크기를 512로 가정하고 작업함

위 (참고)에 따르면, Disk도 하나의 파일이기 때문에 readme.txt 같은 파일의 Data를 저장할 수 있습니다. 다만, 예를 들어, 200GB가 넘는 저장 공간을 작은 크기의 .txt 하나만 저장하기는 무척이나 아쉽죠. 그래서, 하나의 큰 저장 공간을 여러개의 파일 영역으로 나눠 저장하는 기능을 File system이 담당해 주는데 주로 NTFS나 FAT 그리고 ext등이 있습니다. 그래서, File system은 큰 공간을 파일 갯수만큼 조각을 만들어 처리하는 과정이 있을 것인데, 여기서 중요한 것은, File system은 Disk에 바로 올라가는 것이 아니라 Partition에서 올라간다는 것입니다. 즉, Disk는 몇개의 Partition으로 분리되고 하나의 Partition에 하나의 File system이 올라가게 됩니다. 이를 표시하면 다음과 같습니다. 

 

즉, Partition이 4개로 나눠져 있다면, 각 Partition의 정보(타입/시작위치/크기/...)가 저장된 공간이 필요한데 그것이 바로 MBR입니다. MBR에 대해서는 다음(Wiki)를 참조하시기 바랍니다. 간략히 정리하면 다음과 같습니다.
  • 1983년 IBM 호환 PC 시절때 제안되었다.
  • Disk의 첫 Sector(512Byte)에 위치함
  • BIOS에 의해 부팅될 때 로드되는 Boot strap 코드가 포함됨
  • 주(Primary) Partition의 정보 포함(Partition Table)
  • Boot signature (0x55 0xaa)

그리고 Patition Table은 다음의 4개의 Primary Partition 정보를 가지고 있습니다.
(이말은 한 Disk에는 4개의 Primary Partition만 가능하다는 뜻입니다.)

  • 상태 (부팅 가능한지/활성화 되어 있는지/비 활성화 되어 있는지)
  • 시작 위치 (CHS 값, LBA 값)
  • Partition Type (NTFS냐? FAT냐? Linux Ext냐? ...)
  • 종료 위치 (CHS 값, LBA 값)
  • 길이 (Sector 개수)

앞서서 표시된 /dev/sdb Disk의 MBR을 확인하면 다음과 같습니다.

# ./PartitionInfo /dev/sdb /d 0 1
[0000000000] 33 c0 8e d0 bc 00 7c fb 50 07 50 1f fc be 1b 7c  3.....|.P.P....|
[0000000016] bf 1b 06 50 57 b9 e5 01 f3 a4 cb bd be 07 b1 04  ...PW...........
[0000000032] 38 6e 00 7c 09 75 13 83 c5 10 e2 f4 cd 18 8b f5  8n.|.u..........
[0000000048] 83 c6 10 49 74 19 38 2c 74 f6 a0 b5 07 b4 07 8b  ...It.8,t.......
[0000000064] f0 ac 3c 00 74 fc bb 07 00 b4 0e cd 10 eb f2 88  ..<.t...........
[0000000080] 4e 10 e8 46 00 73 2a fe 46 10 80 7e 04 0b 74 0b  N..F.s*.F..~..t.
[0000000096] 80 7e 04 0c 74 05 a0 b6 07 75 d2 80 46 02 06 83  .~..t....u..F...
[0000000112] 46 08 06 83 56 0a 00 e8 21 00 73 05 a0 b6 07 eb  F...V...!.s.....
[0000000128] bc 81 3e fe 7d 55 aa 74 0b 80 7e 10 00 74 c8 a0  ..>.}U.t..~..t..
[0000000144] b7 07 eb a9 8b fc 1e 57 8b f5 cb bf 05 00 8a 56  .......W.......V
[0000000160] 00 b4 08 cd 13 72 23 8a c1 24 3f 98 8a de 8a fc  .....r#..$?.....
[0000000176] 43 f7 e3 8b d1 86 d6 b1 06 d2 ee 42 f7 e2 39 56  C..........B..9V
[0000000192] 0a 77 23 72 05 39 46 08 73 1c b8 01 02 bb 00 7c  .w#r.9F.s......|
[0000000208] 8b 4e 02 8b 56 00 cd 13 73 51 4f 74 4e 32 e4 8a  .N..V...sQOtN2..
[0000000224] 56 00 cd 13 eb e4 8a 56 00 60 bb aa 55 b4 41 cd  V......V.`..U.A.
[0000000240] 13 72 36 81 fb 55 aa 75 30 f6 c1 01 74 2b 61 60  .r6..U.u0...t+a`
[0000000256] 6a 00 6a 00 ff 76 0a ff 76 08 6a 00 68 00 7c 6a  j.j..v..v.j.h.|j
[0000000272] 01 6a 10 b4 42 8b f4 cd 13 61 61 73 0e 4f 74 0b  .j..B....aas.Ot.
[0000000288] 32 e4 8a 56 00 cd 13 eb d6 61 f9 c3 49 6e 76 61  2..V.....a..Inva
[0000000304] 6c 69 64 20 70 61 72 74 69 74 69 6f 6e 20 74 61  lid partition ta
[0000000320] 62 6c 65 00 45 72 72 6f 72 20 6c 6f 61 64 69 6e  ble.Error loadin
[0000000336] 67 20 6f 70 65 72 61 74 69 6e 67 20 73 79 73 74  g operating syst
[0000000352] 65 6d 00 4d 69 73 73 69 6e 67 20 6f 70 65 72 61  em.Missing opera
[0000000368] 74 69 6e 67 20 73 79 73 74 65 6d 00 00 00 00 00  ting system.....
[0000000384] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000400] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000416] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000432] 00 00 00 00 00 2c 44 63 32 73 d0 58 00 00 00 01  .....,Dc2s.X....
[0000000448] 01 00 07 cc bd 65 3f 00 00 00 57 76 96 00 00 d2  .....e?...Wv....
[0000000464] ac 65 05 fe ff ff fe 77 96 00 02 80 69 04 00 00  .e.....w....i...
[0000000480] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000496] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa  ..............U.
#
즉, 0~445 byte 까지는 부트 strap 코드입니다. 중간에 친숙한 에러 코드가 포함되어 있음을 확인할 수 있습니다. 그리고 446~509 byte 까지는 Partition Table로서 4개의 Partition 정보를 포함합니다. 끝의 510~511byte는 0x55, 0xaa로 구성된 magic code 입니다. MBR 영역의 유효성 검사에 사용될 수 있습니다.

이러한 전통적인 MBR 방식을 사용하다보면 다음의 한계에 직면하게 됩니다.

  • 2 TB 이상의 크기 ==> GPT(wiki)로 해결
    ; Partition Table에서 개당 Partition 크기에 사용되는 값은 4byte로 주어지는데, 이는 4GB 숫자만큼의 LBA 위치 지정이 가능하다는 뜻임. 4GB * 512 = 2TB, 즉 2TB 만큼의 Paritition 크기가 최대값이 됨. GPT는 기존 MBR 정보를 지원하면서, 수백(주로 128)개의 Paritition을 지원하고, LBA 단위가 8byte이므로 2TB 한계를 극복하게 된다. 단, BIOS에서 지원하지 않는 경우 부팅이 이뤄지지 않을 수 있으며, OS 마다 지원 범위가 다를 수 있다.
  • 4개 이상의 Paritition 나누기 ==> Extended Partition 혹은 GPT로 해결
    ; 전통적인 Primary Partition 대신, 확장(Extended) Partition으로 확장하고, 개별 Logical Paritition을 관리한다.

우선 GPT(GUID Partition Table)을 확인하기 전에 Extended Partition 부터 확인해 보도록 하겠습니다.

Extended Partition / Logical Partition (wiki) 테스트
기존의 Primary Partition의 개수(4개) 제한을 극복하기 위해 제안된 방식입니다. Extended Partition은 여러개의 Logical Partition을 포함하는 것으로 실제 Data는 없는 개념상의 Partition입니다. Extended Partition에는 여러개의 Logical Partition이 올 수 있는데, Linked-List 형태이므로 그 개수의 제한은 없는 것으로 알 고 있습니다. 허나, 본 PartitionInfo project에서는 64개로 제한하였습니다.
참고)
Disk의 Partition을 실험 작업하다 보면, Disk의 내용을 쉽게 날릴 수 있기 때문에, 되도록이면 VMWare등에서 테스트하는 것을 권장합니다. 혹은 Window같은 경우에는 vhd를 이용하면 보다 쉽게 테스트 할 수 있습니다.
(단, 모든 Windows OS에서 지원되지는 않습니다. 저는 Windows 7 Professional K 입니다.)


를 실행하고 "디스크 관리"를 실행합니다.

그리고 기타 작업을 선택하여 "VHD 만들기"를 실행합니다.

와 새로운 .vhd 경로를 선택하여 확인하면, 새로운 볼륨 Disk를 생성할 수 있습니다.
VMWare등의 가상 하드 디스크 파일과 유사하다고 보시면 됩니다.
테스트 용도로 적합하며, 부팅하면 자동으로 Mount되지 않습니다. 그래서 다음의 배치파일을 공유합니다.

그 안을 보면 _attach.bat와 _detach.bat가 있으며, 이는 mount와 unmount를 의미합니다. 그리고 각각 4개의 파일을 열어서 절대 경로를 입력하도록 합니다. 그다음 .bat를 오른쪽 클릭하여 "관리자 권한 실행"하면 손쇱게 mount와 unmount를 사용할 수 있습니다.


Window에서 위와 같이 "새 단순 볼륨"으로 Primary Partition을 만들 수 있습니다. 그리고 4번째 Partition을 제한 용량보다 작게 만들게 되면 아래와 같이 자동으로 Extended Partition을 만들어 주며, 그 안에 Logical Partition이 생성됩니다.

그럼 다음과 같이 됩니다.

즉, 4번째 Partition 부터는 Logical Partition으로 적용됩니다.
그리고 남아있는 206MB를 다시 아래와 같이 분리합니다.

그리고 PartitionInfo를 실행하여 동일한 결과를 비교해 봅니다.

E:\....>PartitionInfo.exe \\.\PhysicalDrive2
*******
* MBR *
*******
> Disk :
\\.\PhysicalDrivee2
------------------
* Partition no : 0
------------------
        Boot Type : 2 (ENUM_BOOT_TYPE)
        Type : 4 (ENUM_PART_TYPE)
        Seek Pos : 65536 (Sector : 128)
        Size : 111.00 MB (116391936 Bytes) (Sector : 227328)
------------------
* Partition no : 1
------------------
        Boot Type : 2 (ENUM_BOOT_TYPE)
        Type : 4 (ENUM_PART_TYPE)
        Seek Pos : 116457472 (Sector : 227456)
        Size : 222.00 MB (232783872 Bytes) (Sector : 454656)
------------------
* Partition no : 2
------------------
        Boot Type : 2 (ENUM_BOOT_TYPE)
        Type : 4 (ENUM_PART_TYPE)
        Seek Pos : 349241344 (Sector : 682112)
        Size : 333.00 MB (349175808 Bytes) (Sector : 681984)
------------------
* Partition no : 3
------------------
        Boot Type : 2 (ENUM_BOOT_TYPE)
        Type : 3 (ENUM_PART_TYPE)
        Seek Pos : 698417152 (Sector : 1364096)
        Size : 356.00 MB (373293056 Bytes) (Sector : 729088)

        [Extend]
        ---------------------------
        * Logical Partition no : 0
        ---------------------------
                Boot Type : 2 (ENUM_BOOT_TYPE)
                Type : 4 (ENUM_PART_TYPE)
                Seek Pos : 65536 (Sector : 128)
                Size : 150.00 MB (157286400 Bytes) (Sector : 307200)
                * Absolute Seek Pos : 698482688 (Sector : 1364224)
        ---------------------------
        * Logical Partition no : 1
        ---------------------------
                Boot Type : 2 (ENUM_BOOT_TYPE)
                Type : 4 (ENUM_PART_TYPE)
                Seek Pos : 65536 (Sector : 128)
                Size :  66.00 MB (69206016 Bytes) (Sector : 135168)
                * Absolute Seek Pos : 855834624 (Sector : 1671552)
        ---------------------------
        * Logical Partition no : 2
        ---------------------------
                Boot Type : 2 (ENUM_BOOT_TYPE)
                Type : 4 (ENUM_PART_TYPE)
                Seek Pos : 65536 (Sector : 128)
                Size :  88.00 MB (92274688 Bytes) (Sector : 180224)
                * Absolute Seek Pos : 925106176 (Sector : 1806848)

E:\....>

즉, 4번째 Partition은 Extended Partition이며, 해당 Partition은 총 3개의 Logical Partition으로 구성됨을 알 수 있습니다. 그리고, 각 Logical Partition의 Disk 상의 절대 위치도 알려주고 있습니다.

Linux에서는 gparted를 이용하여 구성할 수 있습니다. gparted가 없다면 $ sudo apt-get install gparted로 설치할 수 있습니다. gparted는 처음부터 Extended Partition으로 설정 가능하더군요.


그럼 다음과 같이 Extended Partition이 생성됩니다.

그럼 그 안쪽에 다음과 같이 Partition을 설정할 수 있습니다. (Partition 추가시 File system을 unformatted로 합니다). 그리고 마지막으로 툴바에 있는 v 버튼을 누르면 다음과 같이 적용 됩니다.


$ sudo ./PartitionInfo /dev/sdd
[sudo] password for greenfish:
*******
* MBR *
*******
> Disk : /dev/sdd
------------------
* Partition no : 0
------------------
        Boot Type : 2 (ENUM_BOOT_TYPE)
        Type : 3 (ENUM_PART_TYPE)
        Seek Pos : 1048576 (Sector : 2048)
        Size :  90.00 MB (94371840 Bytes) (Sector : 184320)

        [Extend]
        ---------------------------
        * Logical Partition no : 0
        ---------------------------
                Boot Type : 2 (ENUM_BOOT_TYPE)
                Type : 6 (ENUM_PART_TYPE)
                Seek Pos : 1048576 (Sector : 2048)
                Size :  11.00 MB (11534336 Bytes) (Sector : 22528)
                * Absolute Seek Pos : 2097152 (Sector : 4096)
                * Accurracy Size : 11534336 (Sector : 22528)
        ---------------------------
        * Logical Partition no : 1
        ---------------------------
                Boot Type : 2 (ENUM_BOOT_TYPE)
                Type : 6 (ENUM_PART_TYPE)
                Seek Pos : 1048576 (Sector : 2048)
                Size :  22.00 MB (23068672 Bytes) (Sector : 45056)
                * Absolute Seek Pos : 14680064 (Sector : 28672)
                * Accurracy Size : 23068672 (Sector : 45056)
        ---------------------------
        * Logical Partition no : 2
        ---------------------------
                Boot Type : 2 (ENUM_BOOT_TYPE)
                Type : 6 (ENUM_PART_TYPE)
                Seek Pos : 1048576 (Sector : 2048)
                Size :  33.00 MB (34603008 Bytes) (Sector : 67584)
                * Absolute Seek Pos : 38797312 (Sector : 75776)
                * Accurracy Size : 34603008 (Sector : 67584)
        ---------------------------
        * Logical Partition no : 3
        ---------------------------
                Boot Type : 2 (ENUM_BOOT_TYPE)
                Type : 6 (ENUM_PART_TYPE)
                Seek Pos : 421376 (Sector : 823)
                Size :  11.00 MB (11534336 Bytes) (Sector : 22528)
                * Absolute Seek Pos : 74448896 (Sector : 145408)
                * Accurracy Size : 11534336 (Sector : 22528)
$

linux(gparted)에서 표시되는 /dev/sdd8이 PartitionInfo의 3번 Logical Partition과 상관있다는것이 확인됩니다.
그럼 gparted에서 해당 항목을 더블클릭하면 다음과 같은 정보를 알려줍니다.


First sector에 145408이라고 나오는데, 이는 PartitionInfo에서 "* Absolute Seek Pos : 74448896 (Sector : 145408)"과 동일합니다. 즉, PartitionInfo의 절대값 계산이 올바르다는 것이 확인됩니다.

EBR(Extended Boot Record)
Extended Partition은 내부의 Logical Partition을 포함하는 실체가 없는 Partition 입니다. Extended Partition은 EBR이라는 MBR과 동일한 Format의 정보를 포함합니다. 위의 /dev/sdd의 첫 EBR의 위치를 dump하면 다음과 같습니다. (첫 EBR은 Extended Partition의 "Seek Pos : 1048576 (Sector : 2048)"를 통해 2048 sector 부터 시작함을 알 수 있습니다.)
$ sudo ./PartitionInfo /dev/sdd /d 2048 1
[0000000000] eb 52 90 4e 54 46 53 20 20 20 20 00 02 08 00 00  .R.NTFS    .....
[0000000016] 00 00 00 00 00 f8 00 00 20 00 40 00 00 08 00 00  ........ .@.....
[0000000032] 00 00 00 00 80 00 80 00 ff 07 01 00 00 00 00 00  ................
[0000000048] 04 00 00 00 00 00 00 00 7f 10 00 00 00 00 00 00  ...............
[0000000064] f6 00 00 00 01 00 00 00 3e 7c 22 0f 1b 78 6c 1c  ........>|"..xl.
[0000000080] 00 00 00 00 fa 33 c0 8e d0 bc 00 7c fb 68 c0 07  .....3.....|.h..
[0000000096] 1f 1e 68 66 00 cb 88 16 0e 00 66 81 3e 03 00 4e  ..hf......f.>..N
[0000000112] 54 46 53 75 15 b4 41 bb aa 55 cd 13 72 0c 81 fb  TFSu..A..U..r...
[0000000128] 55 aa 75 06 f7 c1 01 00 75 03 e9 d2 00 1e 83 ec  U.u.....u.......
[0000000144] 18 68 1a 00 b4 48 8a 16 0e 00 8b f4 16 1f cd 13  .h...H..........
[0000000160] 9f 83 c4 18 9e 58 1f 72 e1 3b 06 0b 00 75 db a3  .....X.r.;...u..
[0000000176] 0f 00 c1 2e 0f 00 04 1e 5a 33 db b9 00 20 2b c8  ........Z3... +.
[0000000192] 66 ff 06 11 00 03 16 0f 00 8e c2 ff 06 16 00 e8  f...............
[0000000208] 40 00 2b c8 77 ef b8 00 bb cd 1a 66 23 c0 75 2d  @.+.w......f#.u-
[0000000224] 66 81 fb 54 43 50 41 75 24 81 f9 02 01 72 1e 16  f..TCPAu$....r..
[0000000240] 68 07 bb 16 68 70 0e 16 68 09 00 66 53 66 53 66  h...hp..h..fSfSf
[0000000256] 55 16 16 16 68 b8 01 66 61 0e 07 cd 1a e9 6a 01  U...h..fa.....j.
[0000000272] 90 90 66 60 1e 06 66 a1 11 00 66 03 06 1c 00 1e  ..f`..f...f.....
[0000000288] 66 68 00 00 00 00 66 50 06 53 68 01 00 68 10 00  fh....fP.Sh..h..
[0000000304] b4 42 8a 16 0e 00 16 1f 8b f4 cd 13 66 59 5b 5a  .B..........fY[Z
[0000000320] 66 59 66 59 1f 0f 82 16 00 66 ff 06 11 00 03 16  fYfY.....f......
[0000000336] 0f 00 8e c2 ff 0e 16 00 75 bc 07 1f 66 61 c3 a0  ........u...fa..
[0000000352] f8 01 e8 08 00 a0 fb 01 e8 02 00 eb fe b4 01 8b  ................
[0000000368] f0 ac 3c 00 74 09 b4 0e bb 07 00 cd 10 eb f2 c3  ..<.t...........
[0000000384] 0d 0a 41 20 64 69 73 6b 20 72 65 61 64 20 65 72  ..A disk read er
[0000000400] 72 6f 72 20 6f 63 63 75 72 72 65 64 00 0d 0a 42  ror occurred...B
[0000000416] 4f 4f 54 4d 47 52 20 69 73 20 6d 69 73 73 69 6e  OOTMGR is missin
[0000000432] 67 00 0d 0a 42 4f 4f 54 4d 47 52 20 69 73 00 41  g...BOOTMGR is.A
[0000000448] 02 00 83 a7 26 01 00 08 00 00 00 58 00 00 00 a7  ....&......X....
[0000000464] 27 01 05 96 12 04 00 60 00 00 00 b8 00 00 00 00  '......`........
[0000000480] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000496] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa  ..............U.
$
즉, Partition 내부에도 MBR과 같은 구조가 있음이 확인됩니다. 앞서 설명드렸듯이 Logical Partition 정보는 Linked List 형태로 이뤄졌다고 했는데, 그 정보의 Node는 MBR의 Partition Table이 응용됩니다. 좀 특이하게도 MBR에서의 Partition Table은 총 4개의 Partition 정보가 포함된다고 하였는데, EBR에서는 총 2개가 사용됩니다. 물론 나머지 2개는 0으로 채워져 있습니다. 일단 그 2개의 정보를 가지고 다음 Next EBR의 위치를 계산하게 됩니다. 그리고 EBR당 2개의 Partition 정보가 유효하기 때문에 2개의 Logical Partition이 연결되는 것이 아니라, 하나의 Logical Partition만 표현됩니다. 즉, 1개의 EBR은 1개의 Logical Partition을 표현합니다.
(관련 부분은 PartitionInfo::CPartHelper::GetMbrExtPartInfoAlloc(...) 함수를 참고하시기 바랍니다.)

주요한 정보를 구하는 방법은 다음과 같습니다.

- 다음 EBR의 절대 위치 : 첫 EBR의 위치 + 두번째 Partition 정보의 시작 LBA
- Logical Partition의 절대 위치 : 첫번째 Partition 정보의 시작 LBA + 첫 EBR의 위치
- Linked List의 종료 조건 : 두번째 Partition 정보의 시작 LBA와 크기가 0

위 예에서, ... 00 60 00 00 ... 를 통해 두번째 Partition 정보의 시작 LBA가 0x6000임을 알 수 있습니다. 그리고 첫 EBR의 위치가 0x800(=2048)이기 때문에 0x6800(=26624)로 Jump하면 다음과 같이 또 하나의 다음 EBR이 등장합니다.
$ sudo ./PartitionInfo /dev/sdd /d 26624 1
[0000000000] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000016] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000032] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000048] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000064] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000080] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000096] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000112] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000128] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000144] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000160] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000176] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000192] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000208] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000224] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000240] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000256] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000272] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000288] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000304] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000320] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000336] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000352] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000368] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000384] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000400] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000416] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000432] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c8  ................
[0000000448] 08 01 83 96 12 04 00 08 00 00 00 b0 00 00 00 96  ................
[0000000464] 13 04 05 eb 23 08 00 18 01 00 00 10 01 00 00 00  ....#...........
[0000000480] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
[0000000496] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa  ..............U.
$
즉, 이곳이 두번째 EBR인데, 특이한건 boot strap 코드가 모두 0임을 알 수 있습니다. ... 00 18 01 00 ...를 통해 세번째 EBR의 위치는 0x11800+0x800의 위치입니다. 해당 부분은 생략하지만 역시 0x55 0xaa로 끝나는 EBR 영역으로 로드될 것입니다. 이러한 관계를 도식화하면 다음과 같습니다.


즉, 각 EBR의 첫번째 Partition 정보를 분홍색 화살표, 두번째 Partition 정보를 녹색 화살표로 생각하면, 첫번째 Partition 정보는 해당 Logical Partition의 위치를 알려주며, 두번째 Partition의 정보는 다음 EBR의 위치를 첫 EBR의 상대 위치로 표시한 값입니다. 이를 통해 Logical Partition의 Linked List를 Traverse할 수 있습니다.

GPT (GUID Partition Table)
앞서 설명드린바와 같이 GPT는 기존의 MBR 체계에서 2TB의 파티션 크기 제약을 벗어나기 위해 제안된 Partition 표현 방식입니다. 즉, 기존의 MBR 체계와 차이점이 발생하기 때문에, BIOS 제약(부팅 불가), OS 제약(지원 불가) 등의 이슈가 발생할 수 있으니 주의해야 합니다. GPT를 사용하여 눈에 띄는 큰 장점은 없기 때문에, 2TB 이하의 Harddisk인 경우에는 전통적인 MBR 사용을 추천합니다.
GPT는 다음과 같이 MBR에서 부터 전환할 수 있습니다.

즉 "디스크 관리자"에서 "GPT 디스크로 변환"을 실행하면 됩니다.
혹은 아래와 같이 gparted에서도 GPT로 변환할 수 있습니다.


Create Partition Table을 실행합니다.


gpt를 선택하고 Apply하면 바로 적용됩니다.


앞선 디스크 관리자를 통해 5개의 partition으로 나눠진 모습입니다. 더이상의 복잡하던 Extended Partition등은 보이지 않습니다.
gparted도 마찬가지로 다음과 같이 5개의 partition으로 나눌 수 있습니다.


그럼 위 /dev/sdd에 대한 PartitionInfo는 다음과 같습니다.
# ./PartitionInfo /dev/sdd
*******
* GPT *
*******
> Disk : /dev/sdd
------------------
* Partition no : 0
------------------
        Type : 6 (ENUM_GPT_PART_TYPE)
        Attrib : 1 (ENUM_PART_ATTRIB)
        Seek Pos : 1048576 (Sector : 2048)
        Size :  11.00 MB (11534336 Bytes) (Sector : 22528)
------------------
* Partition no : 1
------------------
        Type : 6 (ENUM_GPT_PART_TYPE)
        Attrib : 1 (ENUM_PART_ATTRIB)
        Seek Pos : 12582912 (Sector : 24576)
        Size :  22.00 MB (23068672 Bytes) (Sector : 45056)
------------------
* Partition no : 2
------------------
        Type : 6 (ENUM_GPT_PART_TYPE)
        Attrib : 1 (ENUM_PART_ATTRIB)
        Seek Pos : 35651584 (Sector : 69632)
        Size :  33.00 MB (34603008 Bytes) (Sector : 67584)
------------------
* Partition no : 3
------------------
        Type : 6 (ENUM_GPT_PART_TYPE)
        Attrib : 1 (ENUM_PART_ATTRIB)
        Seek Pos : 70254592 (Sector : 137216)
        Size :  44.00 MB (46137344 Bytes) (Sector : 90112)
------------------
* Partition no : 4
------------------
        Type : 6 (ENUM_GPT_PART_TYPE)
        Attrib : 1 (ENUM_PART_ATTRIB)
        Seek Pos : 116391936 (Sector : 227328)
        Size :   4.00 MB (4194304 Bytes) (Sector : 8192)
#
즉, ****** 부분에 GPT라고 표시해 줍니다. 이는 해당 Disk가 GPT로 이뤄졌음을 알려주는 것입니다.


앞서 마지막의 4MB의 Partition의 정보를 보면, 227328 sector 부터 시작인데, 위 PartitionInfo에서도 4번 Partition의 Seek Pos도 동일함이 확인됩니다. 그 크기도 8192 sector로 동일합니다. 즉, PartitionInfo를 통해 각 Partition 영역 계산이 올바르게 수행되었음을 확인할 수 있습니다.

그럼 GPT를 그림을 통해 알아 보면 다음과 같습니다.


앞선 MBR이나 EBR과 비교해 단순해 보입니다. 우선, 위 그림의 하나의 사각형은 하나의 Sector입니다. 즉, GPT Header는 512 byte이며, Partition Information Entry 하나는 128 byte입니다. 즉, 과거 MBR의 Partition Table의 개당 정보인 16 byte와 비교해보면 몇배로 커짐을 알 수 있습니다. 그런 이유로, GPT에서 Partition의 크기 정보는 8Byte로 표현되는데, 이는 4GB*4GB가 되며 여기에 512까지 곱하게 되면, 사실상 무한대에 가까운 크기를 표현할 수 있게 되었습니다.
그리고 GPT Header에 Partition Entry 개수가 정의되어 있습니다. 보통은 128로 되어 있으니 총 32개의 Entry Block이 있을 것입니다. 그리고 아래쪽의 Secondary 영역이 있는데, 이는 GPT 정보의 복사본이라 생각하면 됩니다. PartitionInfo에서는 Primary와 Secondary의 각 Entry 정보를 비교하는 검증 코드가 들어 있습니다. 단, 두개의 GPT Header는 내용상 동일하지 않아 지원하지는 않습니다. (GPT Header에 CRC 정보등등이 포함되는데 Primary와 Secondary에서 각각의 값이 다르게 나왔습니다. 향후 해당 CRC 값을 검증하는 코드가 들어가면 좋을 듯 합니다.)

CPartHelper class manual
이제 code 레벨의 설명을 하고자 합니다.
PartitionInfo에는 CPartHelper라는 class가 있는데 해당 class를 가지고 시스템의 Partition 정보를 gathering할 수 있습니다. class로 정의되어 C++일 듯 하지만, 실제로 class는 namespace 적인 용도로만 사용되었을 뿐, 모두 static 함수로 이뤄져 있습니다. 그렇기 때문에 class의 member 변수는 하나도 없습니다. 또한 malloc/free를 사용하였습니다. 결국, 손쉽게 C로 전환할 수 있다는 뜻이며, 이를 통해 시스템 Driver등으로 참조할 수 있음을 알려드립니다. 그리고 마지막으로 Windows Visual C와 Linux의 GCC 모두 Compile되도록 하였습니다.
CPartHelper::DUMP_DISK_INFO(...)를 보면 사용법을 쉽게 확인할 수 있습니다.

[ 기본 함수 군 ]

INT OpenDisk
IN LPCSTR lpszDiskName
; Disk Volume을 Open한다.
; lpszDiskName : Disk의 경로 (\\.\PhysicalDrive0, /dev/sda)
; File Descriptor를 리턴함 (-1은 에러)
BOOL SeekDisk
IN INT nFD
IN _OFF64_T offset
IN INT whence
; File Descriptor를 통해 Disk를 Seek한다.
; offset은 Sector 크기의 배수이여야 한다. 즉, 512의 배수이어야 한다.
; whence는 SEEK_SET과 같은 값이다.
; 성공 : TRUE / 실패 : FALSE

VOID CloseDisk
IN INT nFD
; Disk Volume을 Close한다.

[ MBR 관련 함수 군 ]

BOOL GetMbrPartInfo
IN INT nFD
OPTIONAL OUT LPST_MBR_PART_INFO pstArrInfoSize4
OPTIONAL OUT PBOOL pbGpt
; MBR의 Primary Partition 정보를 가져온다.
; pstArrInfoSize4 : ST_MBR_PART_INFO의 4개 Array여야 한다. NULL 전달 가능하다.
; pbGpt : 해당 Disk가 GPT 타입인지 전달한다. NULL 전달 가능하다.
; 성공 : TRUE / 실패 : FALSE

ENUM_BOOT_TYPE GetBootType
IN LPST_MBR_PART_INFO pstInfo
ENUM_PART_TYPE GetPartType
IN LPST_MBR_PART_INFO pstInfo
BOOL GetPartRange
IN LPST_MBR_PART_INFO pstInfo
OUT _OFF64_T* pnSeekPos
OUT _OFF64_T* pnSize
; GetMbrPartInfo로 얻어진 4개의 Partition 정보 하나의 세부 내용을 구한다.
; ENUM_BOOT_TYPE은 /부트 가능/활성화/비활성화 등의 값을 가진다.
; ENUM_PART_TYPE은 /NTFS/FAT/EXTEND/... 등의 값을 가진다.
; 해당 Partition의 Disk로 부터의 절대 위치를 pnSeekPos로 받고, 그 크기를 pnSize로 구한다.

[ Extended Partition / Logical Partition 함수 군 ]

LPST_MBR_LOGICAL_PART_INFO GetMbrExtPartInfoAlloc
IN INT nFD
IN UBYTE4 nStartLBA
OUT PINT pnCount
; GetPartType(...)를 통해 ENUM_PART_TYPE_EXTEND인 경우, 본 함수를 호출한다.
; 해당 Extend Partition의 StartLBA를 전달한다.
; pnCount를 통해 Logical Partition 개수를 구한다.
; ST_MBR_LOGICAL_PART_INFO의 배열을 전달 받는다. 그 개수는 pnCount이다.
; ST_MBR_LOGICAL_PART_INFO는 Primary Partition 정보에서 Disk상 절대 위치 값이 하나만 추가됨.

VOID GetMbrExtPartInfoFree
IN LPST_MBR_LOGICAL_PART_INFO pstInfo
; GetMbrExtPartInfoAlloc의 리턴값으로 받은 Array를 Free한다.

[ GPT 함수 군 ]

LPST_GPT_PART_ENTRY GetGptPartAlloc
IN INT nFD
OUT PINT pnAllocCount
; GetMbrPartInfo(...)를 통해 pbGpt가 TRUE인 경우 호출한다.
; pnAllocCount를 통해 GPT Partition의 개수를 전달 받는다.
; GPT Partition의 정보인 ST_GPT_PART_ENTRY의 배열을 전달 받는다.

VOID GetGptPartFree
IN LPST_GPT_PART_ENTRY pstEntry
; GetGptPartAlloc(...)를 통해 전달받는 배열을 Free한다.

ENUM_GPT_PART_TYPE GetGptPartType
IN UBYTE16 nGuid
BOOL GetPartRangeGpt
IN LPST_GPT_PART_ENTRY pstItem
OUT _OFF64_T* pnSeekPos
OUT _OFF64_T* pnSize
ENUM_PART_ATTRIB GetPartAttrib
IN LPST_GPT_PART_ENTRY pstItem
; ST_GPT_PART_ENTRY::llllPartTypeGuid를 가지고 ENUM_GET_PART_TYPE을 구한다.
; GPT partition 정보를 가지고 Disk 상의 절대 위치와 크기를 구한다. (pnSeekPos, pnSize)
; GPT partition 정보를 가지고 속성을 구한다. (시스템/Bootable/Readonly/Hidden/No automount)