近期学习了硬盘的结构以及分区体系,以DOS分区体系为例。磁盘的第一个扇区(0-512字节)被称为引导扇区(Boot Sector)。内含有主引导记录(MBR)。ji计算机启动并完成自检后,首先会寻找磁盘的MBR扇区并读取其中的引导记录,然后将系统控制权交给它。
我的任务是初步解析MBR的内容、判断分区类型、定位所有主分区以及它们的大小。
通过阅读数据取证入门名著"File System Forensic Analysis"获取DOS分区体系下的MBR的数据结构:
数据结构以及类的声明如下:
1 typedef struct $DOS { 2 3 __uint32_t Starting_CHS_Address; 4 __uint32_t Ending_CHS_Address; 5 __uint64_t Starting_LBA_Address; 6 __uint64_t Size_In_Sectors; 7 8 9 __uint16_t Bootable_Flag; 10 __uint16_t Partition_Type; 11 12 $DOS() { Starting_CHS_Address = 0; Ending_CHS_Address = 0; Starting_LBA_Address =0; 13 Size_In_Sectors = 0; Bootable_Flag = 0; Partition_Type = 0; } 14 15 }$DOS; 16 17 typedef class $MBR { 18 19 public: 20 $MBR() { Partition_Table_Number = 0; }; 21 ~$MBR() = default; 22 23 public: 24 void GO(char*); 25 26 protected: 27 void Analyse(); 28 void Get_Row_MBR(const char*); 29 void Convert_MBR(); 30 31 protected: 32 void $_Convert_MBR(std::vector<__uint64_t>&); 33 char* $_Type_Judge(const __uint16_t); 34 35 private: 36 __int32_t Partition_Table_Number; 37 std::string Disk_ISO; 38 std::vector<$DOS> DOS_Partition; 39 40 }MBR;
获取信息通过dd命令,并将返回值通过管道给xxd命令进行处理,获得想要的数据格式:
如:
80 20 21 00 07 fe ff ff 00 08 00 00 00 00 40 06 00 fe ff ff 0f fe ff ff fe 0f 40 06 02 18 eb 2b 00 fe ff ff 83 fe ff ff 00 28 2b 32 00 30 0d 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa
Data
实现此功能的函数如下:
1 void MBR::Get_Row_MBR(const char* tmp) { 2 using namespace std; 3 4 Disk_ISO = tmp; 5 char cmd[1024]; 6 char buffer[1024]; 7 8 sprintf(cmd,"sudo dd if=‘%s‘ bs=1 skip=446 count=66 2> log | xxd -p -c 66 > MBR.dat", tmp); 9 system(cmd); 10 11 ifstream read_dat("MBR.dat"); 12 if(!read_dat) { 13 cerr << "Open MBR.dat Error" <<endl; 14 system("echo ‘Open MBR.dat Error‘ > MBR_Error"); 15 exit(1); 16 } 17 18 while(!read_dat.eof()) { 19 read_dat >> buffer; 20 } 21 read_dat.close(); 22 23 ofstream write_dat("MBR.dat"); 24 if(!write_dat) { 25 cerr << "Open MBR.dat Error" <<endl; 26 system("echo ‘Open MBR.dat Error‘ > MBR_Error"); 27 exit(1); 28 } 29 30 for(int i = 0; buffer[i]; i++) { 31 if(i % 2 == 0 && i != 0) { 32 write_dat << " "; 33 } 34 write_dat << buffer[i]; 35 } 36 write_dat.close(); 37 }
Get_Row_MBR
首先可以确定的是,前446个字节是Boot Code,这是用于引导系统启动的一小段代码。我们在这里不做深入的研究。我们关注的是446-511这66字节所包含的信息。
Partition Table Entry不仅标识出分区表的入口(每个分区表的第一个字节在硬盘中所处的位置)。从书中也获取到了相应的数据结构:
接下来的工作很简单,那就是逐条解析信息了。由于书作者推荐了(自己写的)开源软件“The Sleuth Kit",所以拿来运行并对照结果。
因为CHS对地址的标记方式局限性过大(仅能寻址8GB),因此后来又有了LBA。且对兼容问题进行了很好的处理(换算公式可根据LBA和CHS不同寻址模式的特点轻易推出:LBA=(C–CS)×PH×PS+(H–HS)×PS+(S–SS)),所以解析过程中不再列出CHS的起始和终止地址(但也做分析,目的也是为了与The Sleuth Kit软件进行清晰对比)。
此外,Partition Type需要有额外的信息来识别,书中给出了表格。因此有了如下识别函数:
1 char* MBR::$_Type_Judge(const __uint16_t type) { 2 std::string ans; 3 switch(type) { 4 case 0x00: ans = "Empty"; break; 5 case 0x01: ans = "FAT12, CHS"; break; 6 case 0x04: ans = "FAT16, 16-32MB, CHS"; break; 7 case 0x05: ans = "Microsoft Extended, CHS"; break; 8 case 0x06: ans = "FAT16, 32MB-2GB, CHS"; break; 9 case 0x07: ans = "NTFS"; break; 10 case 0x0b: ans = "FAT32, CHS"; break; 11 case 0x0c: ans = "FAT32, LBA"; break; 12 case 0x0e: ans = "FAT16, 32MB-2GB, LBA"; break; 13 case 0x0f: ans = "Microsoft Extended, LBA"; break; 14 case 0x11: ans = "Hidden FAT12, CHS"; break; 15 case 0x14: ans = "Hidden FAT16, 16-32MB, CHS"; break; 16 case 0x16: ans = "Hidden FAT16, 32MB-2GB, CHS"; break; 17 case 0x1b: ans = "Hidden FAT32, CHS"; break; 18 case 0x1c: ans = "Hidden FAT32, LBA"; break; 19 case 0x1e: ans = "Hidden FAT16, 32MB-2GB, LBA"; break; 20 case 0x42: ans = "Microsoft MBR. Dynamic Disk"; break; 21 case 0x82: ans = "Solaris x86"; break; 22 case 0x83: ans = "Linux"; break; 23 case 0x84: ans = "Hibernation"; break; 24 case 0x85: ans = "Linux Extended"; break; 25 case 0x86: ans = "NTFS Volume Set"; break; 26 case 0x87: ans = "NTFS Volume Set"; break; 27 case 0xa0: ans = "Hibernation"; break; 28 case 0xa1: ans = "Hibernation"; break; 29 case 0xa5: ans = "FreeBSD"; break; 30 case 0xa6: ans = "OpenBSD"; break; 31 default : ans = "No Match"; 32 } 33 return const_cast<char*>(ans.c_str()); 34 }
Type_Judge
扇区的大小也给出,因此直接读就可以了。
特别要注意的是,我使用的是Intel的处理器,该处理器系统采用小端方式进行数据存放。所以要进行大小端的转换处理。
1 void MBR::Convert_MBR() { 2 using namespace std; 3 4 freopen("MBR.dat", "r", stdin); 5 vector<__uint64_t> data; 6 __uint64_t Get_Data_From_MBR; 7 int Time = 4; 8 9 while(~scanf("%x", &Get_Data_From_MBR)) { 10 data.push_back(Get_Data_From_MBR); 11 } 12 13 while(Time--) { 14 $_Convert_MBR(data); 15 } 16 }
Convert_MBR
以及它的辅助函数:
1 void MBR::$_Convert_MBR(std::vector<__uint64_t>& d) { 2 using namespace std; 3 4 const __int32_t Offset = 16 * Partition_Table_Number; 5 $DOS tmp; 6 7 tmp.Starting_CHS_Address = d[Offset+3] << 16 | d[Offset+2] << 8 | d[Offset+1]; 8 if(tmp.Starting_CHS_Address == 0) { //Does not exist 9 return ; 10 } 11 12 tmp.Bootable_Flag = d[Offset+0]; 13 tmp.Partition_Type = d[Offset+4]; 14 tmp.Ending_CHS_Address = d[Offset+7] << 16 | d[Offset+6] << 8 | d[Offset+5]; 15 tmp.Starting_LBA_Address = d[Offset+11] << 24 | d[Offset+10] << 16 | d[Offset+9] << 8 | d[Offset+8]; 16 tmp.Size_In_Sectors = d[Offset+15] << 24 | d[Offset+14] << 16 | d[Offset+13] << 8 | d[Offset+12]; 17 18 Partition_Table_Number++; 19 DOS_Partition.push_back(tmp); 20 }
$_Convert_MBR
至此已经可以初步分析这一块DOS分区体系的硬盘了,完整mbr_analyse.hpp代码如下:
1 #pragma once 2 #include <iostream> 3 #include <cstdio> 4 #include <vector> 5 #include <string> 6 #include <cstring> 7 #include <fstream> 8 9 typedef struct $DOS { 10 // essential 11 __uint32_t Starting_CHS_Address; 12 __uint32_t Ending_CHS_Address; 13 __uint64_t Starting_LBA_Address; 14 __uint64_t Size_In_Sectors; 15 16 // not essential 17 __uint16_t Bootable_Flag; 18 __uint16_t Partition_Type; 19 20 $DOS() { Starting_CHS_Address = 0; Ending_CHS_Address = 0; Starting_LBA_Address =0; 21 Size_In_Sectors = 0; Bootable_Flag = 0; Partition_Type = 0; } 22 23 }$DOS; 24 25 typedef class $MBR { 26 27 public: 28 $MBR() { Partition_Table_Number = 0; }; 29 ~$MBR() = default; 30 31 public: 32 void GO(char*); 33 34 protected: 35 void Analyse(); 36 void Get_Row_MBR(const char*); 37 void Convert_MBR(); 38 39 protected: 40 void $_Convert_MBR(std::vector<__uint64_t>&); 41 char* $_Type_Judge(const __uint16_t); 42 43 private: 44 __int32_t Partition_Table_Number; 45 std::string Disk_ISO; 46 std::vector<$DOS> DOS_Partition; 47 48 }MBR; 49 50 void MBR::$_Convert_MBR(std::vector<__uint64_t>& d) { 51 using namespace std; 52 53 const __int32_t Offset = 16 * Partition_Table_Number; 54 $DOS tmp; 55 56 tmp.Starting_CHS_Address = d[Offset+3] << 16 | d[Offset+2] << 8 | d[Offset+1]; 57 if(tmp.Starting_CHS_Address == 0) { //Does not exist 58 return ; 59 } 60 61 tmp.Bootable_Flag = d[Offset+0]; 62 tmp.Partition_Type = d[Offset+4]; 63 tmp.Ending_CHS_Address = d[Offset+7] << 16 | d[Offset+6] << 8 | d[Offset+5]; 64 tmp.Starting_LBA_Address = d[Offset+11] << 24 | d[Offset+10] << 16 | d[Offset+9] << 8 | d[Offset+8]; 65 tmp.Size_In_Sectors = d[Offset+15] << 24 | d[Offset+14] << 16 | d[Offset+13] << 8 | d[Offset+12]; 66 67 Partition_Table_Number++; 68 DOS_Partition.push_back(tmp); 69 } 70 71 void MBR::Get_Row_MBR(const char* tmp) { 72 using namespace std; 73 74 Disk_ISO = tmp; 75 char cmd[1024]; 76 char buffer[1024]; 77 78 sprintf(cmd,"sudo dd if=‘%s‘ bs=1 skip=446 count=66 2> log | xxd -p -c 66 > MBR.dat", tmp); 79 system(cmd); 80 81 ifstream read_dat("MBR.dat"); 82 if(!read_dat) { 83 cerr << "Open MBR.dat Error" <<endl; 84 system("echo ‘Open MBR.dat Error‘ > MBR_Error"); 85 exit(1); 86 } 87 88 while(!read_dat.eof()) { 89 read_dat >> buffer; 90 } 91 read_dat.close(); 92 93 ofstream write_dat("MBR.dat"); 94 if(!write_dat) { 95 cerr << "Open MBR.dat Error" <<endl; 96 system("echo ‘Open MBR.dat Error‘ > MBR_Error"); 97 exit(1); 98 } 99 100 for(int i = 0; buffer[i]; i++) { 101 if(i % 2 == 0 && i != 0) { 102 write_dat << " "; 103 } 104 write_dat << buffer[i]; 105 } 106 write_dat.close(); 107 } 108 109 void MBR::Convert_MBR() { 110 using namespace std; 111 112 freopen("MBR.dat", "r", stdin); 113 vector<__uint64_t> data; 114 __uint64_t Get_Data_From_MBR; 115 int Time = 4; 116 117 while(~scanf("%x", &Get_Data_From_MBR)) { 118 data.push_back(Get_Data_From_MBR); 119 } 120 121 while(Time--) { 122 $_Convert_MBR(data); 123 } 124 } 125 126 char* MBR::$_Type_Judge(const __uint16_t type) { 127 std::string ans; 128 switch(type) { 129 case 0x00: ans = "Empty"; break; 130 case 0x01: ans = "FAT12, CHS"; break; 131 case 0x04: ans = "FAT16, 16-32MB, CHS"; break; 132 case 0x05: ans = "Microsoft Extended, CHS"; break; 133 case 0x06: ans = "FAT16, 32MB-2GB, CHS"; break; 134 case 0x07: ans = "NTFS"; break; 135 case 0x0b: ans = "FAT32, CHS"; break; 136 case 0x0c: ans = "FAT32, LBA"; break; 137 case 0x0e: ans = "FAT16, 32MB-2GB, LBA"; break; 138 case 0x0f: ans = "Microsoft Extended, LBA"; break; 139 case 0x11: ans = "Hidden FAT12, CHS"; break; 140 case 0x14: ans = "Hidden FAT16, 16-32MB, CHS"; break; 141 case 0x16: ans = "Hidden FAT16, 32MB-2GB, CHS"; break; 142 case 0x1b: ans = "Hidden FAT32, CHS"; break; 143 case 0x1c: ans = "Hidden FAT32, LBA"; break; 144 case 0x1e: ans = "Hidden FAT16, 32MB-2GB, LBA"; break; 145 case 0x42: ans = "Microsoft MBR. Dynamic Disk"; break; 146 case 0x82: ans = "Solaris x86"; break; 147 case 0x83: ans = "Linux"; break; 148 case 0x84: ans = "Hibernation"; break; 149 case 0x85: ans = "Linux Extended"; break; 150 case 0x86: ans = "NTFS Volume Set"; break; 151 case 0x87: ans = "NTFS Volume Set"; break; 152 case 0xa0: ans = "Hibernation"; break; 153 case 0xa1: ans = "Hibernation"; break; 154 case 0xa5: ans = "FreeBSD"; break; 155 case 0xa6: ans = "OpenBSD"; break; 156 default : ans = "No Match"; 157 } 158 return const_cast<char*>(ans.c_str()); 159 } 160 161 void MBR::Analyse() { 162 using namespace std; 163 cout << "There are " << Partition_Table_Number << " partition(s) in this disk iso" << endl << endl; 164 cout << "------------------MBR------------------" << endl; 165 cout << "-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-" << endl << endl; 166 cout << endl << endl; 167 int cnt = 1; 168 for(vector<$DOS>::iterator it = DOS_Partition.begin(); it != DOS_Partition.end(); it++, cnt++) { 169 cout << "------------- Partition " << cnt << " -------------" << endl << endl; 170 cout << "Partition Type: " << $_Type_Judge((*it).Partition_Type) << endl; 171 // cout << "Starting_CHS_Address : " << (*it).Starting_CHS_Address << endl; 172 // cout << "Ending_CHS_Address : " << (*it).Ending_CHS_Address << endl; 173 cout << "Starting_LBA_Address : " << (*it).Starting_LBA_Address << endl; 174 cout << "Size_In_Sectors : " << (*it).Size_In_Sectors << endl; 175 cout << "Bootable_Flag : " << (*it).Bootable_Flag << endl; 176 cout << endl << endl; 177 } 178 } 179 180 181 void MBR::GO(char* nm) { 182 Get_Row_MBR((nm)); 183 Convert_MBR(); 184 Analyse(); 185 }
对比分析结果,首先是从TSK官网上获取的镜像:
mmls命令获取的信息:
Slot Start End Length Description 00: Meta 0000000000 0000000000 0000000001 Primary Table (#0) 01: ----- 0000000000 0000000062 0000000063 Unallocated 02: 00:00 0000000063 0000052415 0000052353 DOS FAT16 (0x04) 03: 00:01 0000052416 0000104831 0000052416 DOS FAT16 (0x04) 04: 00:02 0000104832 0000157247 0000052416 DOS FAT16 (0x04) 05: Meta 0000157248 0000312479 0000155232 DOS Extended (0x05) 06: Meta 0000157248 0000157248 0000000001 Extended Table (#1) 07: ----- 0000157248 0000157310 0000000063 Unallocated 08: 01:00 0000157311 0000209663 0000052353 DOS FAT16 (0x04) 09: ----- 0000209664 0000209726 0000000063 Unallocated 10: 01:01 0000209727 0000262079 0000052353 DOS FAT16 (0x04) 11: Meta 0000262080 0000312479 0000050400 DOS Extended (0x05) 12: Meta 0000262080 0000262080 0000000001 Extended Table (#2) 13: ----- 0000262080 0000262142 0000000063 Unallocated 14: 02:00 0000262143 0000312479 0000050337 DOS FAT16 (0x06)
接着是我的分析结果:
There are 4 partition(s) in this disk iso ------------------MBR------------------ -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_- ------------- Partition 1 ------------- Partition Type: FAT16, 16-32MB, CHS Starting_LBA_Address : 63 Size_In_Sectors : 52353 Bootable_Flag : 0 ------------- Partition 2 ------------- Partition Type: FAT16, 16-32MB, CHS Starting_LBA_Address : 52416 Size_In_Sectors : 52416 Bootable_Flag : 0 ------------- Partition 3 ------------- Partition Type: FAT16, 16-32MB, CHS Starting_LBA_Address : 104832 Size_In_Sectors : 52416 Bootable_Flag : 0 ------------- Partition 4 ------------- Partition Type: Microsoft Extended, CHS Starting_LBA_Address : 157248 Size_In_Sectors : 155232 Bootable_Flag : 0
两个结果吻合。
再对本机的/dev/sda进行分析:
mmls命令获取的信息:
Slot Start End Length Description 00: Meta 0000000000 0000000000 0000000001 Primary Table (#0) 01: ----- 0000000000 0000002047 0000002048 Unallocated 02: 00:00 0000002048 0104859647 0104857600 NTFS (0x07) 03: ----- 0104859648 0104861695 0000002048 Unallocated 04: Meta 0104861694 0841689087 0736827394 Win95 Extended (0x0F) 05: Meta 0104861694 0104861694 0000000001 Extended Table (#1) 06: 01:00 0104861696 0396365823 0291504128 NTFS (0x07) 07: ----- 0396365824 0396369919 0000004096 Unallocated 08: Meta 0396367872 0687876095 0291508224 DOS Extended (0x05) 09: Meta 0396367872 0396367872 0000000001 Extended Table (#2) 10: 02:00 0396369920 0687876095 0291506176 NTFS (0x07) 11: Meta 0687876096 0837783551 0149907456 DOS Extended (0x05) 12: Meta 0687876096 0687876096 0000000001 Extended Table (#3) 13: ----- 0687876096 0687878143 0000002048 Unallocated 14: 03:00 0687878144 0837783551 0149905408 NTFS (0x07) 15: Meta 0837783552 0841689087 0003905536 DOS Extended (0x05) 16: Meta 0837783552 0837783552 0000000001 Extended Table (#4) 17: ----- 0837783552 0837785599 0000002048 Unallocated 18: 04:00 0837785600 0841689087 0003903488 Linux Swap / Solaris x86 (0x82) 19: 00:02 0841689088 0976771071 0135081984 Linux (0x83) 20: ----- 0976771072 0976773167 0000002096 Unallocated
我的分析结果:
There are 3 partition(s) in this disk iso ------------------MBR------------------ -_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_- ------------- Partition 1 ------------- Partition Type: NTFS Starting_LBA_Address : 2048 Size_In_Sectors : 104857600 Bootable_Flag : 128 ------------- Partition 2 ------------- Partition Type: Microsoft Extended, LBA Starting_LBA_Address : 104861694 Size_In_Sectors : 736827394 Bootable_Flag : 0 ------------- Partition 3 ------------- Partition Type: Linux Starting_LBA_Address : 841689088 Size_In_Sectors : 135081984 Bootable_Flag : 0
结果也是一致的。
这件事请很简单,但是可以辅助我更好地理解磁盘的工作原理和MBR的职责,实践出真知,这个过程中也是学到了不少其他的东西,注意到了很多细节的地方。真的是受益匪浅!