GPT分区结构

  • 2025-12-08 14:31:07

最近作业需要读取MBR和GPT磁盘信息,上次读了MRB,这次读GPT

文章目录

GPT分区结构PMBRGPT HeaderGPT表项源代码结果分析磁盘信息读取PMBR读取GPT Header读取第1个分区表项第2个分区表项第3个分区表项第4个分区表项备份GPT Header备份分区表项

GPT分区结构

GPT的分区格式,比MBR的要简明扼要不少。一开始第一扇区是PMBR,格式与MBR相同,但是作用不同,在GPT中,PMBR没有太多实质性作用,只有第一个分区表项中有内容。 第1个扇区,是GPT表头GPT Header,有512字节的大小。GPT表头与MBR作用类似,标志着分区格式的开始。第2个扇区开始,一般连着32个扇区,里面存放的内容是GPT的分区表项。分区表项的作用与MBR中的分区表类似,都是指明了分区在哪里,有多大或者分区范围是多少。与MBR不同的地方是,MBR中每个MBR中只有4个分区条目,要么有4个主分区,要么3个主分区加1个主扩展分区;在EBR中4个分区条目只有前两个用上了,形成类似链表一样的结构,前一个逻辑分区指向后一个,前者说明后者在哪里。GPT中,在windows中可以有128个分区项,就像数组一样,可以直接索引,不需要像链表一样,一个个遍历。 GPT分区表项之后有一部分没有用上的区域,然后开始是GPT的第1个分区,一直到最后一个分区。最后一个分区之后,是备份的GPT分区表,最后最后一个扇区,是备份的GPT表头。

PMBR

protective MBR的作用和MBR是不一样的,内容也不一样,PMBR中,分区表中只有第一个表项有内容。PMBR内容如下,主要关注从第1字节起,第5字节的分区标志和Relative这4字节的内容:

GPT Header

其中GUID顺序在磁盘中顺序是从低高,而 GUID读取出来需要是一种特殊的顺序 磁盘GUID:B9 8D F9 E9 13 4A 4D 49 99 8F B3 19 FD B7 60 30 读取出来是:E9F98DB9-4A13-494D-998F-B319FDB76030

GPT表项

其中GUID的顺序和之前第三部分中磁盘GUID的顺序是一致的。表明分区类型的PartitionType微软文档中提供了如下几种: 分区的属性标志GPT attributes bits的含义微软做了如下规定,需要注意的是,磁盘中是小端的顺序,高位在高地址,需要转换成数字中的顺序做比较。一旦某个二进制位置位上去了,该分区就具有了该种属性。置位可以置多个位。

源代码

使用的打开设备和I/O的函数与上一篇中读取MBR格式磁盘信息是一致的。

#include

#include //DDK驱动开发与控制

#include

#include

#include

#include

struct gpt_header //GPT表头512字节

{

uint8_t signature[8];//无符号8字节签名

uint8_t version[4];//4字节版本号

uint8_t headersize[4];//GPT表头大小

uint8_t headercrc32[4];//GPT表头的CRC-32校验

uint8_t reserve[4];//保留,为0

uint8_t header_lba[8];//表头的扇区号

uint8_t backup_lba[8];//备份表头的扇区号

uint8_t pation_first_lba[8];//GPT分区起始扇区号

uint8_t pation_last_lba[8];//GPT分区结束扇区号

uint8_t guid[16];//磁盘的GUID

uint8_t pation_table_first[8];//分区表起始扇区号

uint8_t pation_table_entries[4];//分区表总项数

uint8_t pation_table_size[4];//单个分区表占用字节数

uint8_t pation_table_crc[4];//分区表的CRC校验

uint8_t notuse[420];//保留的420字节

};//GPT表头结构

struct partition_table//分区表是128字节

{

uint8_t pationtype[16];//分区类型,全0是未使用

uint8_t pationid[16];//分区唯一标识符

uint8_t pation_start[8];//分区起始扇区号

uint8_t pation_end[8];//分区结束扇区号

uint8_t pation_attr[8];//分区属性标志,区分分区是什么类型的

uint8_t pation_name[72];//分区名

};

struct MBR_disk_entry

{

uint8_t bootflag;//引导标志

uint8_t citouhao;//磁头号

uint8_t shanquhao;//扇区号

uint8_t zhumianhao;//柱面号

uint8_t disk_flag;//分区类型标志,如果是05H/0FH是扩展分区;GPT是0xEE

uint8_t someinfo[3];

uint8_t relative[4];//相对起始扇区

uint8_t sectors[4];//总扇区数

};

struct PMBR //不是真正的MBR

{

uint8_t boot_code[446];//引导代码

MBR_disk_entry pation_table_entry[4];//4个分区表,每个16字节,只有一个分区表有内容,对应的标志是0xEE,

uint8_t endflag[2];//55AA

};

//PartitionType

uint8_t PARTITION_BASIC_DATA_GUID[16] = {0xeb,0xd0,0xa0,0xa2,0xb9,0xe5,0x44,0x33,

0x87,0xc0,0x68,0xb6,0xb7,0x26,0x99,0xc7 };

uint8_t PARTITION_SYSTEM_GUID[16] = {0xc1,0x2a,0x73,28,0xf8,0x1f,0x11,0xd2,0xba,

0x4b,0x00,0xa0,0xc9,0x3e,0xc9,0x3b };

uint8_t PARTITION_MSFT_RESERVED_GUID[16] = {0xe3,0xc9,0xe3,0x16,0x0b,0x5c,0x4d,0xb8,

0x81,0x7d,0xf9,0x2d,0xf0,0x02,0x15,0xae};

uint8_t PARTITION_MSFT_RECOVERY_GUID[16] = {0xde,0x94,0xbb,0xa4,0x06,0xd1,0x4d,0x40,0xa1,

0x6a,0xbf,0xd5,0x01,0x79,0xd6,0xac };

uint8_t PARTITION_ENTRY_UNUSED_GUID[16] = { 0 };

uint8_t * partitiontype[5] = { PARTITION_BASIC_DATA_GUID,PARTITION_SYSTEM_GUID ,

PARTITION_MSFT_RESERVED_GUID ,PARTITION_MSFT_RECOVERY_GUID,PARTITION_ENTRY_UNUSED_GUID };

const char * partition_type_info[] = { "这是一个基本数据分区","这是一个EFI系统分区","这是一个微软保留分区",

"这是一个微软恢复分区","这是一个空分区"};

//GPT表项的attributes bits的最高位,也就是最左边的1位,索引是[0]

//相与不为0说明置位了

uint64_t read_only =0x1000000000000000;

uint64_t shadow_copy = 0x2000000000000000;//其它分区的影像0x200000....

uint64_t hide_partition = 0x4000000000000000; //Hides a partition's volume.

uint64_t no_letter = 0x8000000000000000;//不自动挂载,没有盘符的

uint64_t EFI_hide = 0x0000000000000010;//EFI不可见分区

uint64_t system_partition = 0x0000000000000001;//系统分区

uint64_t attribute_bits[6] = {read_only,shadow_copy,hide_partition,no_letter,EFI_hide,system_partition};

const char * attribute_bits_info[] = {"这是一个只读分区","这是一个其它分区的shadow copy\n","这是一个隐藏分区",

"这是一个不自动挂载、不自动分配盘符的分区","这是一个EFI不可见分区",

"这是一个系统分区"};

uint32_t uint8to32(uint8_t fouruint8[4]) {

return *(uint32_t*)fouruint8;

//return((uint32_t)fouruint8[3] << 24) | ((uint32_t)fouruint8[2] << 16) | ((uint32_t)fouruint8[1] << 8) | ((uint32_t)fouruint8[0]);

}

uint64_t uint8to64(uint8_t fouruint8[8]) {

return *(uint64_t*)fouruint8;

//return((uint64_t)fouruint8[7] << 56) | ((uint64_t)fouruint8[6] << 48) | ((uint64_t)fouruint8[5] << 40) | ((uint64_t)fouruint8[4] << 32) |

//((uint64_t)fouruint8[3] << 24) | ((uint64_t)fouruint8[2] << 16) | ((uint64_t)fouruint8[1] << 8) | ((uint64_t)fouruint8[0]);;

}

int compareuint8(uint8_t * a, uint8_t *b)

{

if (sizeof(*a) != sizeof(*b))

return 0;

for (int i = 0; i < sizeof(*a); i++)

{

if (a[i] != b[i])

return 0;

}

return 1;

}

void changeseqGUID(uint8_t *GUID, uint8_t *seqGUID)

{

//最左边4位,是大端,转过来

seqGUID[0] = GUID[3]; seqGUID[1] = GUID[2]; seqGUID[2] = GUID[1]; seqGUID[3] = GUID[0];

//交叉顺序

seqGUID[4] = GUID[5]; seqGUID[5] = GUID[4]; seqGUID[6] = GUID[7]; seqGUID[7] = GUID[6];

//顺序

for (int i = 8; i < 16; i++)

seqGUID[i] = GUID[i];

}

void show_partion_name(uint8_t*beginchar,int length) {

int j = 0;

for (int i = 0; i < length; i++) {

if (beginchar[i] == 0)

j++;

else

j = 0;

if (j > 2)

return;//后面都是0

else if (j == 0)

printf("%c", beginchar[i]);

}

}

void show_gpt_header(struct gpt_header* the_gpt_header) {

printf("GPT头签名为:");

for (int i = 0; i < 8; i++)

printf("%c", the_gpt_header->signature[i]);

printf("\n");

printf("版本号为:");

for (int i = 0; i < 4; i++)

printf("%0X", the_gpt_header->version[i]);

printf("\n");

printf("GPT头大小为 %u 字节\n", uint8to32(the_gpt_header->headersize));

printf("GPT头CRC校验值为:");

for (int i = 0; i < 4; i++)

printf("%0X", the_gpt_header->headercrc32[i]);

printf("\n");

printf("GPT表头起始扇区号为 %I64X\n", uint8to64(the_gpt_header->header_lba));

//备份表头在最后一个EFI扇区,可以得知整个磁盘的大小,扇区数*512/1024/1024/1024

printf("GPT备份表头扇区号为 %I64X\n", uint8to64(the_gpt_header->backup_lba));

printf("GPT分区区域的起始扇区号为 %I64X\n", uint8to64(the_gpt_header->pation_first_lba));

printf("GPT分区区域结束扇区号为 %I64X\n", uint8to64(the_gpt_header->pation_last_lba));

printf("磁盘GUID为:");

uint8_t GUID[16];

changeseqGUID(the_gpt_header->guid, GUID);

for (int i = 0; i < 16; i++)

{

printf("%0X", GUID[i]);

if (i == 3 || i == 5 || i == 7 || i == 9)

printf("-");

}

printf("\n");

printf("GPT分区表起始扇区号为 %I64X\n", uint8to64(the_gpt_header->pation_table_first));

printf("GPT分区表总项数为 %I32X\n", uint8to32(the_gpt_header->pation_table_entries));

printf("每个分区表占用字节数为 %I32X\n", uint8to32(the_gpt_header->pation_table_size));

printf("分区表CRC校验值为 %I32X\n", uint8to32(the_gpt_header->pation_table_crc));

}

void showPMBR(struct PMBR*the_pmbr)

{

printf("引导标志为%X\n", the_pmbr->pation_table_entry[0].bootflag);

printf("磁头号为%X\n", the_pmbr->pation_table_entry[0].citouhao);

printf("扇区号为%X\n", the_pmbr->pation_table_entry[0].shanquhao);

printf("柱面号为%X\n", the_pmbr->pation_table_entry[0].zhumianhao);

printf("分区类型标志为 %X\n", the_pmbr->pation_table_entry[0].disk_flag);

printf("第一个扇区为 %u\n", uint8to32(the_pmbr->pation_table_entry[0].relative));

printf("扇区数为 %u\n", uint8to32(the_pmbr->pation_table_entry[0].sectors));

}

uint8_t show_partition_table(struct partition_table * the_partition_table)

{

uint8_t GUID[16];

printf("分区类型值为:");

uint8_t flag = 0;

changeseqGUID(the_partition_table->pationtype, GUID);

for (int i = 0; i < 16; i++)

{

flag = flag | GUID[i];

printf("%0X", GUID[i]);

if (i == 3 || i == 5 || i == 7 || i == 9)

printf("-");

}

printf("\n");

for (int i = 0; i < 5; i++) {

if (compareuint8(GUID, partitiontype[i]))

printf("***%s***\n", partition_type_info[i]);

}

printf("分区GUID为:");

changeseqGUID(the_partition_table->pationid, GUID);

for (int i = 0; i < 16; i++)

{

printf("%0X", GUID[i]);

if (i == 3 || i == 5 || i == 7 || i == 9)

printf("-");

}

printf("\n该分区起始扇区号为%I64X\n", uint8to64(the_partition_table->pation_start));

printf("该分区结束扇区号为%I64X\n", uint8to64(the_partition_table->pation_end));

printf("该分区属性标志为%I64X\n", uint8to64(the_partition_table->pation_attr));

uint64_t attr = uint8to64(the_partition_table->pation_attr);

for (int i = 0; i < 6; i++)

{

if ((attr&attribute_bits[i])!=0)

printf("从attributes-bits中可知:%s\n", attribute_bits_info[i]);

}

printf("该分区名为:");

show_partion_name(the_partition_table->pation_name, 72);

uint64_t bytes = (uint8to64(the_partition_table->pation_end) - uint8to64(the_partition_table->pation_start)) * (uint64_t)512;

double MB = bytes / 1024.0/1024.0;

double GB = MB / 1024.0;

printf("\n该分区大小为%I64X字节,%lf GB",bytes,GB);

printf("\n\n\n");

return flag;

}

int read_partition_table(struct gpt_header * the_gpt_header, HANDLE hDevice, ULONGLONG baseaddr)

{

int entrynum = 0;

DWORD dwCB;

LARGE_INTEGER offset;

partition_table the_partition_tables[4];

ULONGLONG nextaddr = ((ULONGLONG)0 + (ULONGLONG)baseaddr) *(ULONGLONG)512;

offset.QuadPart = nextaddr;//找到下一个要读取的地址

SetFilePointer(hDevice, offset.LowPart, &offset.HighPart, FILE_BEGIN);//设置偏移准备读取

ReadFile(hDevice, &the_partition_tables, 512, &dwCB, NULL);

if (GetLastError())

{

return 0;

}

int endflag = 1;

int j = 0;//如果j=4,才重新读,因为某种限制,一次必须读512字节整数倍

while (endflag > 0) {

printf("\n第%d个分区表:\n", ++entrynum);

if (j == 4)

{

nextaddr = nextaddr + (ULONGLONG)512;

offset.QuadPart = nextaddr;//找到下一个要读取的地址

SetFilePointer(hDevice, offset.LowPart, &offset.HighPart, FILE_BEGIN);//设置偏移准备读取

if (GetLastError())

{

return 0;

}

memset(&the_partition_tables, 0, 512);

ReadFile(hDevice, &the_partition_tables, 512, &dwCB, NULL);

j = 0;

}

endflag = show_partition_table(&the_partition_tables[j]);

j++;

}

return 1;

}

int main()

{

DISK_GEOMETRY pdg; // 保存磁盘参数的结构体

HANDLE hDevice; // 设备句柄

BOOL bResult; // results flag

DWORD junk; // discard resultscc

int disk = 0;

const char *diskname[] = { "\\\\.\\PhysicalDrive0" ,"\\\\.\\PhysicalDrive1" };

printf("请输入要打开的硬盘号(一般为0,有2个硬盘可以输入0或1)\n");

scanf("%d", &disk);

if (disk != 0 && disk != 1)

{

disk = 0;

printf("输入无效,打开磁盘0\n");

}

//通过CreateFile来获得设备的句柄

hDevice = CreateFile(diskname[disk], // 设备名称,PhysicalDriveX表示打开第X个设备

GENERIC_READ, // no access to the drive

FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode

NULL, // default security attributes

OPEN_EXISTING, // disposition

0, // file attributes

NULL); // do not copy file attributes

if (hDevice == INVALID_HANDLE_VALUE) //没能打开,可能是没有用管理员权限运行

{

printf("Creatfile error!May be no permission!ERROR_ACCESS_DENIED!\n");

system("pause");

return (FALSE);

}

//通过DeviceIoControl函数与设备进行IO

bResult = DeviceIoControl(hDevice, // 设备的句柄

IOCTL_DISK_GET_DRIVE_GEOMETRY, // 控制码,指明设备的类型

NULL,

0, // no input buffer

&pdg,

sizeof(pdg),

&junk, // # bytes returned

(LPOVERLAPPED)NULL); // synchronous I/O

LARGE_INTEGER offset;//long long signed

offset.QuadPart = (ULONGLONG)0 * (ULONGLONG)512;//0

SetFilePointer(hDevice, offset.LowPart, &offset.HighPart, FILE_BEGIN);//从0开始读PMBR

if (GetLastError())

printf("错误类型代号:%ld\n\n", GetLastError());//如果出错了

DWORD dwCB;

struct PMBR the_pmbr;

//从这个位置开始读512字节PMBR

//读取PMBR的512字节,里面的分区表第一项才有用,从1数,第5字节是0xEE

//相对起始扇区值是GPT Header的位置

BOOL bRet = ReadFile(hDevice, &the_pmbr, 512, &dwCB, NULL);

printf("----------------读取PMBR部分:---------------\n");

showPMBR(&the_pmbr);

if (the_pmbr.pation_table_entry[0].disk_flag == 0xEE)//如果的确是GPT格式分区

{

printf("PMBR中分区表第一项的标志位为 0xEE,是GPT格式,跳转到%u扇区\n",

uint8to32(the_pmbr.pation_table_entry[0].relative));

printf("GPT表头在第%u扇区", uint8to32(the_pmbr.pation_table_entry[0].relative));

}

else {

printf("PMBR中分区表第一项标志位为 %X,不是GPT格式,结束分析\n",

the_pmbr.pation_table_entry[0].disk_flag);

CloseHandle(hDevice);

system("pause");

return 0;

}

//读取分析GPT Header

//下一个要读取的线性地址=要读取的扇区号*512字节

printf("\n\n----------------读取GPT Header部分:---------------\n\n");

ULONGLONG nextaddr= (ULONGLONG)1*(ULONGLONG)512;

offset.QuadPart = nextaddr;//找到下一个要读取的地址

SetFilePointer(hDevice, offset.LowPart, &offset.HighPart, FILE_BEGIN);//设置偏移准备读取

if (GetLastError())

{

printf("读取GPT Header出错。错误类型代号:%ld\n\n", GetLastError());//如果出错了

CloseHandle(hDevice);

system("pause");

return 0;

}

//读取GPT Header

gpt_header the_gpt_header;

ReadFile(hDevice, &the_gpt_header, 512, &dwCB, NULL);

show_gpt_header(&the_gpt_header);

//读取主GPT分区表项,分区表项前16个字节如果全0,表示未用,后面都没有了,可以去读备份

printf("\n\n-------------读取分区表项:-------------\n\n");

ULONGLONG baseaddr = (ULONGLONG)uint8to64(the_gpt_header.pation_table_first);//GPT分区表起始位置

if (!read_partition_table(&the_gpt_header, hDevice, baseaddr))//如果出错

{

CloseHandle(hDevice);

system("pause");

return 0;

}

printf("\n\n---------------读取备份的GPT Header:---------------\n\n");

nextaddr = (ULONGLONG)uint8to32(the_gpt_header.backup_lba)*(ULONGLONG)512;

offset.QuadPart = nextaddr;//找到下一个要读取的地址

SetFilePointer(hDevice, offset.LowPart, &offset.HighPart, FILE_BEGIN);//设置偏移准备读取

if (GetLastError())

{

printf("读取备份GPT Header出错。错误类型代号:%ld\n\n", GetLastError());//如果出错了

CloseHandle(hDevice);

system("pause");

return 0;

}

//读取备份GPT Header

gpt_header backup_gpt_header;

ReadFile(hDevice, &backup_gpt_header, 512, &dwCB, NULL);

show_gpt_header(&backup_gpt_header);

//读取备份GPT分区表项,分区表项前16个字节如果全0,表示未用,后面都没有了,可以去读备份

printf("\n\n-------------读取备份分区表项:-------------\n\n");

baseaddr = (ULONGLONG)uint8to64(backup_gpt_header.pation_table_first);//GPT分区表起始位置

if (!read_partition_table(&backup_gpt_header, hDevice, baseaddr))//如果出错

{

CloseHandle(hDevice);

system("pause");

return 0;

}

printf("\n\n这块硬盘大小为 %lf GB\n", (double)uint8to64(the_gpt_header.backup_lba) * 512 / 1024 / 1024 / 1024);

CloseHandle(hDevice);

system("pause");

return 0;

}

结果分析

磁盘信息

读取PMBR

读取GPT Header

读取第1个分区表项

第2个分区表项

第3个分区表项

第4个分区表项

备份GPT Header

备份分区表项