MBR原理 · 2021年12月29日 0

MBR原理学习

MBR原理学习。

主引导记录

主引导记录(Master Boot Record,缩写:MBR),又叫做主引导扇区,是计算机开机后访问硬盘时所必须要读取的首个扇区,它在硬盘上的三维地址为(柱面,磁头,扇区)=(0,0,1)。在深入讨论主引导扇区内部结构的时候,有时也将其开头的446字节内容特指为“主引导记录”(MBR),其后是4个16字节的“磁盘分区表”(DPT),以及2字节的结束标志(55AA)。因此,在使用“主引导记录”(MBR)这个术语的时候,需要根据具体情况判断其到底是指整个主引导扇区,还是主引导扇区的前446字节。

对于硬盘而言,一个扇区可能的字节数为128×2n(n=0,1,2,3)。大多情况下,取n=2,即一个扇区(sector)的大小为512字节。

标准MBR结构

image-20211229101442692

详细:

地址描述长度 (bytes)
0x0000 (0)Bootstrap 代码区(part 1)218
0x00DA (218)0x0000硬盘时间戳(可选: Windows 95B/98/98SE/ME (MS-DOS 7.1–8.0). 或者, can serve as OEM loader signature with NEWLDR)2
0x00DC (220)原始物理驱动器 (0x800xFF)硬盘时间戳1
0x00DD (221)Seconds (0–59)硬盘时间戳1
0x00DE (222)Minutes (0–59)硬盘时间戳1
0x00DF (223)Hours (0–23)硬盘时间戳1
0x00E0 (224)Bootstrap 代码区(part 2, code entry at 0x0000)216 (or 222)
0x01B8 (440)32-bit disk signature硬盘签名 (可选: UEFI, Linux, Windows NT family and other OSes)4
0x01BC (444)0x0000 (0x5A5A if copy-protected)硬盘签名2
0x01BE (446)Partition entry №1分区表 (用于主分区)16
0x01CE (462)Partition entry №2分区表 (用于主分区)16
0x01DE (478)Partition entry №3分区表 (用于主分区)16
0x01EE (494)Partition entry №4分区表 (用于主分区)16
0x01FE (510)0x55引导签名[a]2
0x01FF (511)0xAA引导签名[a]
总大小: 218 + 6 + 216 + 6 + 4×16 + 2512

成员结构:

1. BootCode(主引导程序代码): [0:440]
2. DiskSignature(磁盘签名): [440: 444]: 主引导程序代码之后的4字节
      self.BootableFlag = struct.unpack("<c", data[:1])[0]
      self.StartCHS0 = struct.unpack("<B", data[1:2])[0]
      self.StartCHS1 = struct.unpack("<B", data[2:3])[0]
      self.StartCHS2 = struct.unpack("<B", data[3:4])[0]
3. Unused: [444:445]
4. PartitionTables(4个硬盘分区表DPT): [445: 509],占64字节,每个分区表占16字节
      self.Entry0 = PartitionEntry(data[6:22])
      self.Entry1 = PartitionEntry(data[22:38])
      self.Entry2 = PartitionEntry(data[38:54])
      self.Entry3 = PartitionEntry(data[54:70])
5. Signature(主引导扇区结束标志55AAH): [509: 511]

引导流程

  1. BIOS加电自检(Power On Self Test — POST)。BIOS执行内存地址为FFFF:0000H处的跳转指令,跳转到固化在ROM中的自检程序处,对系统硬件(包括内存)进行检查。
  2. 读取主引导记录(MBR)。当BIOS检查到硬件正常并与CMOS中的设置相符后,按照CMOS中对启动设备的设置顺序检测可用的启动设备。BIOS将相应启动设备的第一个扇区(也就是MBR扇区)读入内存地址为0000:7C00H处。
  3. 检查0000:01FEH-0000:01FFH(MBR的结束标志位)是否等于55AAH,若不等于则转去尝试其他启动设备,如果没有启动设备满足要求则显示”NO ROM BASIC”然后死机。
  4. 当检测到有启动设备满足要求后,BIOS将控制权交给相应启动设备。启动设备的MBR将自己复制到0000:0600H处,然后继续执行。
  5. 根据MBR中的引导代码启动引导程序

主引导程序代码

打开指定硬盘设备,读取前512字节数据:

with open(open_disk, 'rb') as disk:
       data = disk.read(512)

       with open("mbr.bin", "wb+") as f:
           f.write(data)

前440字节为主引导程序,汇编代码如下:

0x00000000: 33c0                             XOR AX, AX
0x00000002: 8ed0                             MOV SS, AX
0x00000004: bc007c                           MOV SP, 0x7c00    ; 当前栈区在0x7c00
0x00000007: fb                               STI
0x00000008: 50                               PUSH AX
0x00000009: 07                               POP ES
0x0000000a: 50                               PUSH AX
0x0000000b: 1f                               POP DS
0x0000000c: fc                               CLD
0x0000000d: be1b7c                           MOV SI, 0x7c1b
0x00000010: bf1b06                           MOV DI, 0x61b
0x00000013: 50                               PUSH AX
0x00000014: 57                               PUSH DI
0x00000015: b9e501                           MOV CX, 0x1e5    ; 区块初始化
0x00000018: f3a4                             REP MOVSB        ; 复制引导扇区内容到DI所在位置
0x0000001a: cb                               RETF             ; 远返回指令,相当于跳转到0:DI
0x0000001b: bdbe07                           MOV BP, 0x7be    ; 栈底 7be 即指向DPT表
0x0000001e: b104                             MOV CL, 0x4
0x00000020: 386e00                           CMP [BP+0x0], CH    ; 对介质类型判断
0x00000023: 7c09                             JL 0x2e
0x00000025: 7513                             JNZ 0x3a
0x00000027: 83c510                           ADD BP, 0x10    ; 继续判断下一个分区表
0x0000002a: e2f4                             LOOP 0x20
0x0000002c: cd18                             INT 0x18
0x0000002e: 8bf5                             MOV SI, BP
0x00000030: 83c610                           ADD SI, 0x10
0x00000033: 49                               DEC CX
0x00000034: 7419                             JZ 0x4f
0x00000036: 382c                             CMP [SI], CH
0x00000038: 74f6                             JZ 0x30
0x0000003a: a0b507                           MOV AL, [0x7b5]
0x0000003d: b407                             MOV AH, 0x7
0x0000003f: 8bf0                             MOV SI, AX
0x00000041: ac                               LODSB
0x00000042: 3c00                             CMP AL, 0x0
0x00000044: 74fc                             JZ 0x42
0x00000046: bb0700                           MOV BX, 0x7
0x00000049: b40e                             MOV AH, 0xe
0x0000004b: cd10                             INT 0x10
0x0000004d: ebf2                             JMP 0x41
0x0000004f: 884e10                           MOV [BP+0x10], CL
0x00000052: e84600                           CALL 0x9b
0x00000055: 732a                             JAE 0x81
0x00000057: fe4610                           INC BYTE [BP+0x10]
0x0000005a: 807e040b                         CMP BYTE [BP+0x4], 0xb
0x0000005e: 740b                             JZ 0x6b
0x00000060: 807e040c                         CMP BYTE [BP+0x4], 0xc
0x00000064: 7405                             JZ 0x6b
0x00000066: a0b607                           MOV AL, [0x7b6]
0x00000069: 75d2                             JNZ 0x3d
0x0000006b: 80460206                         ADD BYTE [BP+0x2], 0x6
0x0000006f: 83460806                         ADD WORD [BP+0x8], 0x6
0x00000073: 83560a00                         ADC WORD [BP+0xa], 0x0
0x00000077: e82100                           CALL 0x9b
0x0000007a: 7305                             JAE 0x81
0x0000007c: a0b607                           MOV AL, [0x7b6]
0x0000007f: ebbc                             JMP 0x3d
0x00000081: 813efe7d55aa                     CMP WORD [0x7dfe], 0xaa55    ; 检测signature
0x00000087: 740b                             JZ 0x94
0x00000089: 807e1000                         CMP BYTE [BP+0x10], 0x0
0x0000008d: 74c8                             JZ 0x57            ; if(支持 API位图) {
0x0000008f: a0b707                           MOV AL, [0x7b7]
0x00000092: eba9                             JMP 0x3d
0x00000094: 8bfc                             MOV DI, SP
0x00000096: 1e                               PUSH DS
0x00000097: 57                               PUSH DI
0x00000098: 8bf5                             MOV SI, BP
0x0000009a: cb                               RETF
0x0000009b: bf0500                           MOV DI, 0x5
0x0000009e: 8a5600                           MOV DL, [BP+0x0]
0x000000a1: b408                             MOV AH, 0x8
0x000000a3: cd13                             INT 0x13
0x000000a5: 7223                             JB 0xca
0x000000a7: 8ac1                             MOV AL, CL
0x000000a9: 243f                             AND AL, 0x3f
0x000000ab: 98                               CBW
0x000000ac: 8ade                             MOV BL, DH
0x000000ae: 8afc                             MOV BH, AH
0x000000b0: 43                               INC BX
0x000000b1: f7e3                             MUL BX
0x000000b3: 8bd1                             MOV DX, CX
0x000000b5: 86d6                             XCHG DH, DL
0x000000b7: b106                             MOV CL, 0x6
0x000000b9: d2ee                             SHR DH, CL
0x000000bb: 42                               INC DX
0x000000bc: f7e2                             MUL DX
0x000000be: 39560a                           CMP [BP+0xa], DX
0x000000c1: 7723                             JA 0xe6
0x000000c3: 7205                             JB 0xca
0x000000c5: 394608                           CMP [BP+0x8], AX
0x000000c8: 731c                             JAE 0xe6
0x000000ca: b80102                           MOV AX, 0x201
0x000000cd: bb007c                           MOV BX, 0x7c00
0x000000d0: 8b4e02                           MOV CX, [BP+0x2]
0x000000d3: 8b5600                           MOV DX, [BP+0x0]
0x000000d6: cd13                             INT 0x13
0x000000d8: 7351                             JAE 0x12b
0x000000da: 4f                               DEC DI
0x000000db: 744e                             JZ 0x12b
0x000000dd: 32e4                             XOR AH, AH
0x000000df: 8a5600                           MOV DL, [BP+0x0]
0x000000e2: cd13                             INT 0x13
0x000000e4: ebe4                             JMP 0xca
0x000000e6: 8a5600                           MOV DL, [BP+0x0]
0x000000e9: 60                               PUSHA
0x000000ea: bbaa55                           MOV BX, 0x55aa
0x000000ed: b441                             MOV AH, 0x41
0x000000ef: cd13                             INT 0x13      
0x000000f1: 7236                             JB 0x129
0x000000f3: 81fb55aa                         CMP BX, 0xaa55
0x000000f7: 7530                             JNZ 0x129
0x000000f9: f6c101                           TEST CL, 0x1
0x000000fc: 742b                             JZ 0x129
0x000000fe: 61                               POPA
0x000000ff: 60                               PUSHA                ; 寄存器保护
0x00000100: 6a00                             PUSH 0x0             ; BlockNum_H4
0x00000102: 6a00                             PUSH 0x0
0x00000104: ff760a                           PUSH WORD [BP+0xa]
0x00000107: ff7608                           PUSH WORD [BP+0x8]   ; BlockNum_L4
0x0000010a: 6a00                             PUSH 0x0             ; BufferAddr_H2
0x0000010c: 68007c                           PUSH WORD 0x7c00     ; BufferAddr_L2
0x0000010f: 6a01                             PUSH 0x1             ; BlockCount=1
0x00000111: 6a10                             PUSH 0x10            ; PacketSize=16 PReserved=0
0x00000113: b442                             MOV AH, 0x42         ; 磁盘地址数据包
0x00000115: 8bf4                             MOV SI, SP
0x00000117: cd13                             INT 0x13             ; 扩展读
0x00000119: 61                               POPA
0x0000011a: 61                               POPA
0x0000011b: 730e                             JAE 0x12b
0x0000011d: 4f                               DEC DI
0x0000011e: 740b                             JZ 0x12b
0x00000120: 32e4                             XOR AH, AH
0x00000122: 8a5600                           MOV DL, [BP+0x0]
0x00000125: cd13                             INT 0x13
0x00000127: ebd6                             JMP 0xff
0x00000129: 61                               POPA
0x0000012a: f9                               STC
0x0000012b: c3                               RET

分区表

偏移 (bytes)字段长度描述
0x001 byteBootable Flag:可引导分区标志,0x80表示活动分区
0x013 bytesCHS 起始地址(0x01、0x02、0x03)
0x011 byte磁头[Heads]
0x021 byte扇区[Sector]
0x031 byte柱面[Cylinder]
0x041 byte分区类型
0x053 bytesCHS 结束地址(0x05、0x06、0x07)
0x051 byte磁头[Heads]
0x061 byte扇区[Sector]
0x071 byte柱面[Cylinder]
0x084 bytes该分区中第一个绝对扇区的LBA,指从该磁盘开始到该分区开始之间的偏移量,以扇区数为单位
0x0C4 bytes该分区包含的扇区数

分区信息探测脚本:

""" MBR data structure starts on page 88 of FSFA.
Table 5.1 Data structures for the DOS partition table.
Byte range       Description                     Essential
0-445           Boot Code                       No
446-461           Partition Table Entry #1       Yes
462-477           Partition Table Enrty #2       Yes
478-493           Partition Table Enrty #3       Yes
494-509           Partition Table Enrty #4       Yes
510-511           Signature value (0xAA55)       No
Table 5.2 Data structure for DOS partition entries.
Byte Range     Description                     Essential
0-0             Bootable Flag                   No
1-3             Starting CHS Address           Yes
4-4             Partition Type (see table 5.3) No
5-7             Ending CHS Address             Yes
8-11           Starting LBA Address           Yes
12-15           Size in Sectors                 Yes
"""
import struct
import json


# Table 5.3 Some of the type values for DOS partitions.
# More partition values can be found here:
# http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
DOS_PARTITIONS = {
   0x00: "Empty",
   0x01: "FAT12, CHS",
   0x04: "FAT16, 16-32 MB, CHS",
   0x05: "Microsoft Extended, CHS",
   0x06: "FAT16, 32 MB-2GB, CHS",
   0x07: "NTFS",
   0x0b: "FAT32, CHS",
   0x0c: "FAT32, LBA",
   0x0e: "FAT16, 32 MB-2GB, LBA",
   0x0f: "Microsoft Extended, LBA",
   0x11: "Hidden Fat12, CHS",
   0x14: "Hidden FAT16, 16-32 MB, CHS",
   0x16: "Hidden FAT16, 32 MB-2GB, CHS",
   0x1b: "Hidden FAT32, CHS",
   0x1c: "Hidden FAT32, LBA",
   0x1e: "Hidden FAT16, 32 MB-2GB, LBA",
   # On MBR disks, type 0x27. On GPT disks, GUID: DE94BBA4-06D1-4D40-A16A-BFD50179D6AC.
   # A hidden version of a Windows RE type 0x7 partition with NTFS.
   # When this is installed, reboot and press F8 in order to boot into this Recovery Environment.
   0x27: "Windows RE hidden partition",
   0x39: "Plan 9 partition",
   0x42: "Microsoft MBR, Dynamic Disk",
   0x82: "Solaris x86 -or- Linux Swap",
   0x83: "Linux",
   0x84: "Hibernation",
   0x85: "Linux Extended",
   0x86: "NTFS Volume Set",
   0x87: "NTFS Volume SET",
   0xa0: "Hibernation",
   0xa1: "Hibernation",
   0xa5: "FreeBSD",
   0xa6: "OpenBSD",
   0xa8: "Mac OSX",
   0xa9: "NetBSD",
   0xab: "Mac OSX Boot",
   0xb7: "BSDI",
   0xb8: "BSDI swap",
   # FIXME: I'm pretty sure 0xdb is a recovery partition
   0xdb: "Recovery Partition",
   0xde: "Dell Diagnostic Partition",
   0xee: "EFI GPT Disk",
   0xef: "EFI System Partition",
   0xfb: "Vmware File System",
   0xfc: "Vmware swap",
   # FIXME Add flag for VirtualBox Partitions
}

# FIXME find way to determine sector size
SECTOR_SIZE = 512


class Partition(object):
   """
  Object for storing Partition Data
  """

   def __init__(self, data, parent=None):
       """
      To get the correct lba value for extended partitions, we need to add
      the lba value from the extended partition. For example, if you read the
      first 4 partitions and the fourth is an extended partition with an lba
      of 1000, we seek to the 1000th sector. Then we read the next mbr,
      adding the 1000 from the extended partition to each lba.
      """
       self.parent = parent
       self.bootable_flag = struct.unpack("<B", data[0])[0]
       self.start_chs_address = struct.unpack("<BH", data[1:4])[0]
       self.partition_type = struct.unpack("<B", data[4])[0]
       self.end_chs_address = struct.unpack("<BH", data[5:8])[0]
       # FIXME Check to see how the lba address bytes are used
       if self.get_type() == 'Empty':
           self.lba = 0
       else:
           self.lba = struct.unpack("<L", data[8:12])[0]

       self.size = struct.unpack("<L", data[12:16])[0]


   def get_type(self):
       """
      Returns the text value of the partition type
      """
       return DOS_PARTITIONS[self.partition_type]


   def __repr__(self):
       return self.get_type()

   def is_bootable(self):
       """
      Returns True if this partition is bootable
      """
       return self.bootable_flag == 0x80

   def is_extended(self):
       """
      Returns True if the partition is an extended partition
      """
       return 'Extended' in self.get_type()


class Mbr(object):
   """
  Parses the Master Boot Record
  """

   def __init__(self, data, parent=None):
       self.boot_code = struct.unpack("<446B", data[0:446])
       self.partitions = []
       self.partitions.append(Partition(data[446:462], parent))
       self.partitions.append(Partition(data[462:478], parent))
       self.partitions.append(Partition(data[478:494], parent))
       self.partitions.append(Partition(data[494:510], parent))
       self.signature = struct.unpack("<H", data[510:])[0]
       with open("out.json", "w+") as f:
           out = self.json()
           f.write(out)
           print(out)

   @property
   def extended_partitions(self):
       return [i for i in self.partitions if 'Extended' in i.get_type()]

   def validate_signature(self):
       """
      Returns True if signature = 0xAA55 (a valid MBR signature)
      """
       return self.signature == 0xAA55

   def add_partitions(self, disk):
       """
      Adds partitions from extended partitions to the MBR class
      """
       for partition in self.partitions:
           if 'Extended' in partition.get_type():
               with open(disk, 'rb') as hd:
                   hd.seek(partition.read_start)
                   new_mbr = Mbr(hd.read(512), lba_offset=partition.lba)
                   self.partitions.extend(new_mbr.partitions)

               new_mbr.add_partitions(disk)

   def json(self):
       mbr_dict = {'Signature': self.signature}
       mbr_dict['Partitions'] = []
       for number, partition in enumerate(self.partitions):
           part_name = "Partition%s" % (number + 1)
           mbr_dict['Partitions'].append(
              {part_name: {'Type': partition.get_type(),
                            'Bootable': partition.is_bootable(),
                            'CHS start': partition.start_chs_address,
                            'CHS end': partition.end_chs_address,
                            'Logical block address': partition.lba,
                            'Size': partition.size,}})

       return json.dumps(['Master Boot Record', mbr_dict], indent=4)

"""
ABOUT EXTENDED PARTITIONS
The starting address for a secondary File System partition is relative to the
current partition table.
The starting address for a secondary extended partition entry is relative to
the primary extended partition.
"""

def get_extended_tables(primary_lba, extended_lba, disk):
   disk.seek(0)
   disk.seek((primary_lba + extended_lba) * SECTOR_SIZE)
   mbr = Mbr(disk.read(512))
   yield mbr
   for partition in mbr.partitions:
       if partition.is_extended():
           for mbr in get_extended_tables(primary_lba, partition.lba, disk):
               yield mbr

def get_partition_tables(open_disk):
   with open(open_disk, 'rb') as disk:
       data = disk.read(512)

       with open("mbr.bin", "wb+") as f:
           f.write(data)

       mbr = Mbr(data)
       yield mbr
       disk.seek(0)
       for partition in mbr.partitions:
           if partition.is_extended():
               primary_lba = partition.lba
               mbrs = get_extended_tables(primary_lba, 0, disk)
               for mbr in mbrs:
                   yield mbr

if __name__=='__main__':
   import sys
   args = sys.argv
   partition_tables = get_partition_tables(args[1])
   for pt in partition_tables:
       for partition in pt.partitions:
           print partition

探测结果:

[
   "Master Boot Record",
  {
       "Partitions": [
          {
               "Partition1": {
                   "Bootable": true,
                   "Logical block address": 2048,
                   "CHS start": 32,
                   "CHS end": 239,
                   "Type": "NTFS",
                   "Size": 1185792
              }
          },
          {
               "Partition2": {
                   "Bootable": false,
                   "Logical block address": 1187840,
                   "CHS start": 239,
                   "CHS end": 254,
                   "Type": "NTFS",
                   "Size": 1496099377
              }
          },
          {
               "Partition3": {
                   "Bootable": false,
                   "Logical block address": 1497288704,
                   "CHS start": 254,
                   "CHS end": 254,
                   "Type": "Windows RE hidden partition",
                   "Size": 1220608
              }
          },
          {
               "Partition4": {
                   "Bootable": false,
                   "Logical block address": 1498511360,
                   "CHS start": 254,
                   "CHS end": 254,
                   "Type": "NTFS",
                   "Size": 455008256
              }
          }
      ],
       "Signature": 43605
  }
]