在win8以后,磁盘格式一般是GPT格式的,做实验是在winXP虚拟机上完成的,partition style是MBR。在虚拟机上分配了40GB的硬盘空间。三个主分区盘C、E、F,一个扩展分区里面有三个逻辑盘G、H、I。
HANDLE CreateFile(
LPCSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
BOOL DeviceIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
);
DWORD SetFilePointer(
HANDLE hFile,
LONG lDistanceToMove,
PLONG lpDistanceToMoveHigh,
DWORD dwMoveMethod
);
BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);
#include
#include //DDK驱动开发与控制
#include
#include
#include
#include
#define BufferLength 1024
//将四个连续字节存放的值转为int型
uint32_t transtoint(unsigned char a[])
{
uint32_t sum = 0UL;
uint32_t temp = 0UL;
for (int i = 3; i>=0; i--)
{
temp = a[i];//先赋值
temp = temp <<( 8 * (3-i));
sum = sum | temp;
}
return sum;
}
//十六进制输出
bool HexOutput(char* buf, size_t len,bool ismbr, ULONGLONG * baseaddr,ULONGLONG * nextaddr,int EBRnum)
{
bool mbrflag=1;//在读取MBR的时候判断条目是主分区还是扩展分区条目
unsigned char a = buf[0];
if(ismbr)
printf("-----------------------------------------------------");
else
printf("--------------------------第%d个EBR------------------",EBRnum);
//printf("第一字节是:%x\n\n", a);
if(ismbr)
printf(" 第一部分(MBR):\n\n");
else
printf(" 第一部分(EBR):\n\n");
int flag = 0;
for (size_t i = 0; i < len; ++i)
{
unsigned char c = buf[i]; // must use unsigned char to print >128 value
flag++;
if (c < 16)
printf("0%x ", c);
else
printf("%x ", c);
if (i == 445)
{
flag = 0;
printf("\n\n 第二部分(分区表):\n");
}
if (i == 509)
{
flag = 0;
printf("\n\n 第三部分(结束标志):\n");
}
if ((flag) % 16 == 0)
printf("\n");
}
printf("\n<-------------------分区表信息解析------------------->\n\n");
printf("\n\n分区地址和大小分别为: \n\n");
int limit=ismbr?509:477;//如果已经知道是扩展分区的EBR,不用读取那么多,rank=1是分区表,rank=2是指向下一个的
for (int m = 445, rank = 1; m < limit&&rank<=4; m += 16, rank++)
{
unsigned char fifth = buf[m + 5];//取得第五位文件系统标志位,十六进制为05H或者0FH是扩展分区
if(fifth==0x5||fifth==0xf)//是扩展分区条目,不用往后读了
{
printf("This is an extend patition!\n");
mbrflag=0;
rank = 4;
}//不是mbr是扩展分区EBR
if (fifth < 16) //调整输出格式
printf("第%d分区表标志位为: 0%x\n", rank, fifth);
else
printf("第%d分区表标志位为: %x\n", rank, fifth);
if (fifth == 0x00)//当第五位(标志位)是00时,代表分区表信息为空,无分区
{
printf(" 分区表为空\n\n");//也不用往后读了
}
else {//分区项
unsigned char offsetadd[4] = { 0 };
//倒着读,高字节在高地址处
for (int n = m + 12, t = 0; n > m + 8, t < 4; n--, t++)
{
unsigned char temp = buf[n];
if (temp < 16)
printf(" 0%x ", temp);
else
printf(" %x ", temp);
offsetadd[t] = buf[n];
}
//计算地址,转换为十进制扇区数LBA
printf("\n");
uint32_t tempadd = transtoint(offsetadd);
printf("\n开始地址为: %I64x", (ULONGLONG)tempadd * (ULONGLONG)512 + *baseaddr);
if(ismbr&&!mbrflag)// if in mbr and got a extend entry,the EBR at relsecor+nowbase(0)
{
*baseaddr=(ULONGLONG)tempadd* (ULONGLONG)512 + *baseaddr;//only change once
*nextaddr = (ULONGLONG)0UL;
}
else if (!mbrflag)//if it's the extend entry
{
*nextaddr = (ULONGLONG)tempadd * (ULONGLONG)512;
}
printf("\n\n");
printf("大小:");
for (int p = m + 16, w = 0; p > m + 12, w < 4; p--, w++)
{
unsigned char temp1 = buf[p];
if (temp1 < 16)
printf(" 0%x ", temp1);
else
printf(" %x ", temp1);
offsetadd[w] = buf[p];
}
//计算大小,转化为GB单位
printf("\n");
uint32_t tempsize = transtoint(offsetadd);
if(ismbr && !mbrflag)
printf("\n扩展盘总大小为: %lu 扇区 = %lf GB \n", tempsize, ((double)tempsize / 2.0 / 1024.0 / 1024.0));
else if(!ismbr)
{
printf("\n第%d逻辑盘大小为: %lu 扇区 = %lf GB \n",EBRnum, tempsize, ((double)tempsize / 2.0 / 1024.0 / 1024.0));
}
else
printf("\n该盘大小为: %lu 扇区 = %lf GB \n", tempsize, ((double)tempsize / 2.0 / 1024.0 / 1024.0));
}
}
printf("\n\n");
return (mbrflag);
}
//函数:对主分区表进行解析,分别得到每个分区的偏移地址以及分区大小
BOOL GetDriveGeometry(DISK_GEOMETRY *pdg, int addr)
{
HANDLE hDevice; // 设备句柄
BOOL bResult; // results flag
DWORD junk; // discard resultscc
char lpBuffer[BufferLength] = { 0 };
//通过CreateFile来获得设备的句柄
hDevice = CreateFile(TEXT("\\\\.\\PhysicalDrive0"), // 设备名称,这里指第一块硬盘
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) // cannot open the drive
{
printf("Creatfile error!May be no permission!ERROR_ACCESS_DENIED!\n");
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)addr * (ULONGLONG)512;//0
SetFilePointer(hDevice, offset.LowPart, &offset.HighPart, FILE_BEGIN);//从0开始读MBR
if(GetLastError())
printf("错误类型代号:%ld\n\n", GetLastError());//如果出错了
DWORD dwCB;
//从这个位置开始读
BOOL bRet = ReadFile(hDevice, lpBuffer, 512, &dwCB, NULL);
//如果不是MBR,
bool finished=0;
int EBRnum=0;
ULONGLONG *baseaddr=new ULONGLONG,*nextaddr= new ULONGLONG;//扩展分区起始地址,EBR地址
*baseaddr = (ULONGLONG)0;
*nextaddr = (ULONGLONG)0;
finished=HexOutput(lpBuffer, 512,true,baseaddr,nextaddr,EBRnum);//先是读取MBR
if(finished)
CloseHandle(hDevice);
else
{
//继续读
do{
EBRnum++;
memset(lpBuffer, 0, sizeof(lpBuffer));
offset.QuadPart = (ULONGLONG)(*baseaddr + *nextaddr);//find the EBR
SetFilePointer(hDevice, offset.LowPart, &offset.HighPart, FILE_BEGIN);//读EBR
if(GetLastError())
printf("错误类型代号:%ld\n\n", GetLastError());//如果出错了
ReadFile(hDevice, lpBuffer, 512, &dwCB, NULL);
}while(!HexOutput(lpBuffer, 512,false,baseaddr,nextaddr,EBRnum));
CloseHandle(hDevice);
}
delete baseaddr;
delete nextaddr;
return bResult;
}
extern int add[20];
extern int disknum;
int main()
{
DISK_GEOMETRY pdg; // 保存磁盘参数的结构体
BOOL bResult; // generic results flag
ULONGLONG DiskSize; // size of the drive, in bytes
printf("<-----------------欢迎使用分区读取程序----------------->\n\n");
bResult = GetDriveGeometry(&pdg, 0);
if (bResult)
{
DiskSize = pdg.Cylinders.QuadPart * (ULONG)pdg.TracksPerCylinder *
(ULONG)pdg.SectorsPerTrack * (ULONG)pdg.BytesPerSector;
printf("磁盘总大小 = %I64d (Bytes) = %I64d (Gb)\n", DiskSize, DiskSize / (1024 * 1024 * 1024));
}
else
{
printf("GetDriveGeometry failed. Error %ld.\n", GetLastError());
}
return ((int)bResult);
}
运行程序解析磁盘信息的结果,与使用Winhex查看计算是一致的。
分区地址和大小分别为:
第1分区表标志位为: 07
00 00 00 3f
开始地址为: 7e00
大小: 02 7f 8c b2
该盘大小为: 41913522 扇区 = 19.985925 GB
第2分区表标志位为: 07
02 7f 8c f1
开始地址为: 4ff19e200
大小: 00 3f fa c5
该盘大小为: 4192965 扇区 = 1.999362 GB
第3分区表标志位为: 07
02 bf 87 b6
开始地址为: 57f0f6c00
大小: 00 3f fa c5
该盘大小为: 4192965 扇区 = 1.999362 GB
This is an extend patition!
第4分区表标志位为: 0f
02 ff 82 7b
开始地址为: 5ff04f600
大小: 02 00 53 aa
扩展盘总大小为: 33575850 扇区 = 16.010213 GB
查看C盘信息,开始地址7E00,20G:
查看E盘信息,开始抵制4FF19E200,大小2G:
查看F盘信息,开始地址57F0F6C00,大小2G:
查看第4个DPT对应的扩展分区的EBR,扩展分区的EBR所在地址位5FF04F600:
第一个条目地址信息中3F 00 00 00是63,指向第一个逻辑盘。计算这个逻辑盘开始地址为63*512=7E00H,加上5FF04F600H得到5FF057400H,这就是G盘的起始位置。G盘的大小信息为0E 12 A0 00,也就是10490382个扇区,10490382*512/1024/1024/1024=5GB。
第2个条目是第5个字节内容是05H,表示是扩展分区。(之前MBR中的第4个扇区第5字节是0FH,也表示这是一个扩展分区)。扩展分区的地址信息为A0124DH,也就是相对于扩展分区开始位置5FF04F600H起的第10490445个扇区(换成字节是140249A00H)处存放着下一个扩展分区的MBR。这个位置是140249A00H+5FF04F600H =73F299000H。在这个位置,的确放着下一逻辑盘,也就是H盘的EBR。用同样方式可以计算。
第1分区表标志位为: 07
00 00 00 3f
开始地址为: 5ff057400
大小: 00 a0 12 0e
第1逻辑盘大小为: 10490382 扇区 = 5.002204 GB
This is an extend patition!
第2分区表标志位为: 05
00 a0 12 4d
开始地址为: 73f299000
大小: 00 a0 12 4d
第1逻辑盘大小为: 10490445 扇区 = 5.002234 GB
分区地址和大小分别为:
第1分区表标志位为: 07
00 00 00 3f
开始地址为: 5ff057400
大小: 00 a0 12 0e
第2逻辑盘大小为: 10490382 扇区 = 5.002204 GB
This is an extend patition!
第2分区表标志位为: 05
01 40 24 9a
开始地址为: 87f4e2a00
大小: 00 c0 2f 10
第2逻辑盘大小为: 12594960 扇区 = 6.005745 GB
MBR的四个分区表的组合有两种:全部是主分区;三个主分区+一个扩展分区。在阅读分区表条目的时候,(从第1字节开始)第5字节中存放的信息也比较重要,如果是05H或者是0FH,表示这是一个扩展分区条目,地址信息指向的是EBR,否则如果不是0,就是一个主分区条目,指向的是每个主分区的开始扇区。如果是在EBR中,在扩展分区表部分,只有前两个条目有效,最后两个是全为0的没有用到,第1个条目指向了目前已经发现了的逻辑盘,第2个条目如果不是全0,说明还有下一个逻辑盘,指向的就是下一个EBR。
地址信息Relative在主分区中,是0。在扩展分区中,是主扩展分区的开始位置所在扇区,可以从MBR中获取到。而Sectors指的是分区占的总扇区数,在MBR中,第4个如果是扩展分区的话,表示是整个扩展分区里面所有逻辑盘的大小。
自己电脑的分区逻辑图如图所示:
PS:程序写的时候有的显示BUG这种就没有改了。。。。。