[磁盘数据分析] 实现解析特定分区体系(DOS分区体系)的主引导记录扇区

  近期学习了硬盘的结构以及分区体系,以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的职责,实践出真知,这个过程中也是学到了不少其他的东西,注意到了很多细节的地方。真的是受益匪浅!

posted @ 2015-11-28 17:36  Kirai  阅读(896)  评论(0编辑  收藏  举报