mik 发表于 2009-06-09 00:11

对一个典型的 MBR 的详尽分析

这两天饶有兴趣地对 xp 的 MBR 详尽地分析了一番,当作是温故知新。若有不对,敬请指出

下面是摘自实际 Xp 的磁盘 image 里的 windows XP MBR。

----------------------------------------------------------------------------------------
0000000033C0            xor ax,ax
000000028ED0            mov ss,ax
00000004BC007C            mov sp,0x7c00
00000007FB                sti
0000000850                push ax
0000000907                pop es
0000000A50                push ax
0000000B1F                pop ds
0000000CFC                cld

0000000DBE1B7C            mov si,0x7c1b      /* 从 1b 开始到 1FF 区域 */
00000010BF1B06            mov di,0x61b
0000001350                push ax
0000001457                push di
00000015B9E501            mov cx,0x1e5
00000018F3A4            rep movsb            /* 将 1B ~ 1FF 复制到 0x061B */
0000001ACB                retf                   /* 跳到 061B 处执行 */

0000001BBDBE07            mov bp,0x7be         /* DPT1 */
0000001EB104            mov cl,0x4
00000020386E00            cmp ,ch      /* 测试 DPT1 是否为可启动分区 */
000000237C09            jl 0x2e                /* DPT1 = 80h, 是就跳转到 0x2e */
000000257513            jnz 0x3a               /* DPT1 != 80 && DPT1 != 0,即:出错 */

0000002783C510            add bp,byte +0x10
0000002AE2F4            loop 0x20
0000002CCD18            int 0x18

0000002E8BF5            mov si,bp
0000003083C610            add si,byte +0x10      /* next DPT,即:DPT2 */
0000003349                dec cx                   /* 判断下一个分区 */
000000347419            jz 0x4f
00000036382C            cmp ,ch            /* DPT2 */
0000003874F6            jz 0x30                  /* DPT2 == 0, goto 0x30 */
                                                   /* 循环判断每个分区 */
                                                   /* 如果 DPT2 != 0,转到出错处理 */


/*********** 下面这段是分区标志不符的出错处理 **************/

0000003AA0B507            mov al,         /* 取 mbr 数据区: mbr_data */
0000003DB407            mov ah,0x7               
0000003F8BF0            mov si,ax                /* ax = 0x072c, 即:mbr_str 区域 */


print_msg:          /* 打印信息,最终死循环 */

00000041AC                lodsb                  
000000423C00            cmp al,0x0
0000004474FC            jz 0x42                  
00000046BB0700            mov bx,0x7
00000049B40E            mov ah,0xe
0000004BCD10            int 0x10
0000004DEBF2            jmp short 0x41



/*通过了每个分区检查后的后续处理 */

0000004F884E10            mov ,cl          /* DPT2 = 0 */
00000052E84600            call word 0x9b            /* int13_read_sector_to_mem() */

00000055732A            jnc 0x81                  /* succssed */

/* 磁盘参数失败后 */
00000057FE4610            inc byte       /* DPT2++ */
0000005A807E040B          cmp byte ,0xb   /* 检查分区的 系统类型ID */
0000005E740B            jz 0x6b
00000060807E040C          cmp byte ,0xc
000000647405            jz 0x6b
00000066A0B607            mov al,
0000006975D2            jnz 0x3d               /* 打印 "Error loading operation system"*/

0000006B80460206          add byte ,0x6
0000006F83460806          add word ,byte +0x6
0000007383560A00          adc word ,byte +0x0
00000077E82100            call word 0x9b         /* int13_read_sector_to_mem() */
0000007A7305            jnc 0x81
0000007CA0B607            mov al,
0000007FEBBC            jmp short 0x3d         /* 打印 "Error loading operation system"*/


00000081813EFE7D55AA      cmp word ,0xaa55            /* 检查 mbr 标记 0xaa55 */
00000087740B            jz 0x94

00000089807E1000          cmp byte ,0x0
0000008D74C8            jz 0x57
0000008FA0B707            mov al,
00000092EBA9            jmp short 0x3d      /* 打印 "Error loading operation system"*/

000000948BFC            mov di,sp          /* sp = 0x7c00 */
000000961E                push ds
0000009757                push di
000000988BF5            mov si,bp
0000009ACB                retf               /* 回到 0x7c00 */


int13_read_sector_to_mem:

0000009BBF0500            mov di,0x5
0000009E8A5600            mov dl,             /* 硬盘 */
000000A1B408            mov ah,0x8                  
000000A3CD13            int 0x13                  /* 获取磁盘参数 */


000000A57223            jc 0xca   
000000A78AC1            mov al,cl
000000A9243F            and al,0x3f             /* max sector */
000000AB98                cbw
000000AC8ADE            mov bl,dh               /* max header */
000000AE8AFC            mov bh,ah               /* ah = 00 */
000000B043                inc bx
000000B1F7E3            mul bx                  /* sector * header */
000000B38BD1            mov dx,cx
000000B586D6            xchg dl,dh
000000B7B106            mov cl,0x6
000000B9D2EE            shr dh,cl
000000BB42                inc dx
000000BCF7E2            mul dx
000000BE39560A            cmp ,dx
000000C17723            ja 0xe6
000000C37205            jc 0xca
000000C5394608            cmp ,ax
000000C8731C            jnc 0xe6

000000CAB80102            mov ax,0x201
000000CDBB007C            mov bx,0x7c00            /* 读进 0x7c00 */
000000D08B4E02            mov cx,            /* 起始 cylinder,起始 sector */
000000D38B5600            mov dx,            /* 起始 header */
000000D6CD13            int 0x13                  
000000D87351            jnc 0x12b                  /* return */

000000DA4F                dec di
000000DB744E            jz 0x12b
000000DD32E4            xor ah,ah
000000DF8A5600            mov dl,
000000E2CD13            int 0x13
000000E4EBE4            jmp short 0xca

000000E68A5600            mov dl,
000000E960                pushaw
000000EABBAA55            mov bx,0x55aa
000000EDB441            mov ah,0x41
000000EFCD13            int 0x13
000000F17236            jc 0x129
000000F381FB55AA          cmp bx,0xaa55
000000F77530            jnz 0x129
000000F9F6C101            test cl,0x1
000000FC742B            jz 0x129
000000FE61                popaw
000000FF60                pushaw
000001006A00            push byte +0x0
000001026A00            push byte +0x0
00000104FF760A            push word
00000107FF7608            push word
0000010A6A00            push byte +0x0
0000010C68007C            push word 0x7c00
0000010F6A01            push byte +0x1
000001116A10            push byte +0x10
00000113B442            mov ah,0x42
000001158BF4            mov si,sp
00000117CD13            int 0x13
0000011961                popaw
0000011A61                popaw
0000011B730E            jnc 0x12b
0000011D4F                dec di
0000011E740B            jz 0x12b
0000012032E4            xor ah,ah
000001228A5600            mov dl,
00000125CD13            int 0x13
00000127EBD6            jmp short 0xff
0000012961                popaw
0000012AF9                stc
0000012BC3                ret



/********** 下面的区域是 mbr 常量符: mbr_str **************/

/*
char *mbr_str = "Invalid partition table."
               "Error loading operation system."
               "Missing operation system."
*/

0000012C496E7661 6C696420 70617274 6974696F
0000013C6E207461 626C6500 4572726F 72206C6F
0000014C6164696E 67206F70 65726174 696E6720
0000015C73797374 656D004D 69737369 6E67206F
0000016C70657261 74696E67 20737973 74656D



/********** 下面是空白区域 *****************/

0000017B00000000         
0000017F00000000         
0000018300000000
0000018700000000
0000018B00000000
0000018F00000000
0000019300000000
0000019700000000
0000019B00000000
0000019F00000000
000001A300000000
000001A700000000
000001AB00000000
000001AF00000000
000001B300000000
000001B400



/*** 下面这段是 MBR 用到的数据区:mbr_data ***/

000001B52C         /* 指向 "Invalid partition table." */
000001B6 44         /* 指向 "Error loading operation system." */
000001B763         /* 指向 "Missing operation system." */
000001B8 61
000001B9 A2
000001BA61
000001BBA2
000001BC00
000001BD00


/* 1BE - 1FD = disk partion table */

/* 1BE - 1CD = DPT1 */
000001BE80          /* boot indicator */
                      /* 00:不可启动分区 */
                      /* 80:可启动分区(只可有1个启动分区)*/

000001BF01         /* 起始 header 号 */
000001C001         /* 起始 sector 号 */
000001C100         /* 起始 cylinder 号 */
000001C207         /* 系统属性 ID 标记
                     00h:未知操作系统
                     01h:DOS FAT12(16位扇区数)
                     02h:XENIX
                     04h:DOS FAT16(16位扇区数)
                     05h:DOS 扩展分区(DOS 3.3+)
                     06h:DOS 4.0 (Compaq 3.31), 32位扇区数
                     07h:HPFS/NTFS
                     0ah:OS/2
                     0bh:win95 fat32
                     0ch:win95 fat32 (LBA)
                     ... ...
                     */
000001C3FE         /* 结束 header 号 */
000001C4FF         /* 结束 sector 号 */
000001C5FF         /* 结束 cylinder 号 */
000001C63F000000   /* 此分区前的扇区总数 */         
000001CAD9A63F01   /* 此分区的扇区总数 */


/* 1CE - 1DD = DPT2 */
000001CE00            
000001CF00
000001D000            
000001D100
000001D200
000001D300
000001D400
000001D500
000001D600000000
000001DA00000000


/* 1DE - 1ED = DPT3 */
000001DE00
000001DF00
000001E000
000001E100
000001E200
000001E300
000001E400
000001E500
000001E600000000
000001EA00000000


/* 1EE - 1FD = DPT4 */
000001EE00
000001EF00
000001F000
000001F100
000001F200
000001F300
000001F400
000001F500
000001F600000000
000001FA00000000


000001FE55AA            /* MBR 标记 */

mik 发表于 2009-06-09 00:50

整个 512 bytes 的 MBR 主要分三大部分:

1、0000h ~ 012Bh 是代码区域
2、012Ch ~ 01FDh 是数据区域
3、01FEh ~ 01FFh 是 2 bytes 的 MBR 标记 "0x55AA"



一、代码部分:主要又分 5 个部分


第1部分:

0000000033C0            xor ax,ax
000000028ED0            mov ss,ax
00000004BC007C            mov sp,0x7c00
00000007FB                sti
0000000850                push ax
0000000907                pop es
0000000A50                push ax
0000000B1F                pop ds
0000000CFC                cld

0000000DBE1B7C            mov si,0x7c1b
00000010BF1B06            mov di,0x61b
0000001350                push ax
0000001457                push di
00000015B9E501            mov cx,0x1e5
00000018F3A4            rep movsb            /* 将 1B ~ 1FF 复制到 0x061B */
0000001ACB                retf                   /* 跳到 061B 处执行 */

-----------------------------------------------------------------------------
  主要是设置环境:ds=ss=es=00,sp=0x7c00,将 sp 设 0x7c00 作为栈底,是与 0x7c00 代码区分割开。

  接下来,将 MBR 中的 01Bh ~ 1FFh 区域复制到 061B ~ 7FFh 区域。然后跳到 0x061b 继续执行下面的代码。跳到 0x061b 处执行,实际上就是接着执行 MBR 中的 01Bh 开始的代码。






第 2 部分:

0000001BBDBE07            mov bp,0x7be         /* DPT1 */
0000001EB104            mov cl,0x4
00000020386E00            cmp ,ch      /* 测试 DPT1 是否为可启动分区 */
000000237C09            jl 0x2e                /* DPT1 = 80h, 是就跳转到 0x2e */
000000257513            jnz 0x3a               /* DPT1 != 80 && DPT1 != 0,即:出错 */
0000002783C510            add bp,byte +0x10
0000002AE2F4            loop 0x20
0000002CCD18            int 0x18

0000002E8BF5            mov si,bp
0000003083C610            add si,byte +0x10      /* next DPT,即:DPT2 */
0000003349                dec cx                   /* 判断下一个分区 */
000000347419            jz 0x4f
00000036382C            cmp ,ch            /* DPT2 */
0000003874F6            jz 0x30                  /* DPT2 == 0, goto 0x30 */
                                                   /* 循环判断每个分区 */
                                                   /* 如果 DPT2 != 0,转到出错处理 */

---------------------------------------------------------------------------------------
  0x061b 也就是接下面的 01bh 处的代码。

* 0x7be 实际上就是 MBR 中的 1BEh,这是第1个 DPT(Disk Partition Table)的数据起始地方。
  在整个 MBR 代码中bp 都等于 0x7be(也即 1bEh)。

* mov cl, 4   是要对 4 个分区表进行检查。

* cmp , ch   是检查分区表 1是否为 80h 标志,若 DPT1 即不是 80h,也不是 00h,这样的结果是,打印出错信息,然后最终进入死循环。
  
* 如查 DPT1 是可启动分区,那么 MBR 还要检查其余的 3 个分区的合法性,其余 3 个分区必须是 00h 标志。






第 3 部分:

/*********** 下面这段是分区标志不符的出错处理 **************/
0000003AA0B507            mov al,         /* 取 mbr 数据区: mbr_data */
0000003DB407            mov ah,0x7               
0000003F8BF0            mov si,ax                /* ax = 0x072c, 即:mbr_str 区域 */

print_msg:          /* 打印信息,最终死循环 */
00000041AC                lodsb                  
000000423C00            cmp al,0x0
0000004474FC            jz 0x42                  
00000046BB0700            mov bx,0x7
00000049B40E            mov ah,0xe
0000004BCD10            int 0x10
0000004DEBF2            jmp short 0x41

------------------------------------------------------------------------------------------
  接下来的第3 部分,主要是检查了 4 个分区,发现都不合法后的处理例程。


下面的代码:

0000003AA0B507            mov al,         /* 取 mbr 数据区: mbr_data */
0000003DB407            mov ah,0x7               
0000003F8BF0            mov si,ax                /* ax = 0x072c, 即:mbr_str 区域 */

* 0x7b5 也就是 MBR 中的 1b5,这个地方实际上是放着一个 string 指针。这个指针指向出错信息字符串。下面会讲到。







第 4 部分:

/*通过了每个分区检查后的后续处理 */
0000004F884E10            mov ,cl          /* DPT2 = 0 */
00000052E84600            call word 0x9b            /* int13_read_sector_to_mem() */
00000055732A            jnc 0x81                  /* succssed */
/* 磁盘参数失败后 */
00000057FE4610            inc byte       /* DPT2++ */
0000005A807E040B          cmp byte ,0xb   /* 检查分区的 系统类型ID */
0000005E740B            jz 0x6b
00000060807E040C          cmp byte ,0xc
000000647405            jz 0x6b
00000066A0B607            mov al,
0000006975D2            jnz 0x3d               /* 打印 "Error loading operation system"*/
0000006B80460206          add byte ,0x6
0000006F83460806          add word ,byte +0x6
0000007383560A00          adc word ,byte +0x0
00000077E82100            call word 0x9b         /* int13_read_sector_to_mem() */
0000007A7305            jnc 0x81
0000007CA0B607            mov al,
0000007FEBBC            jmp short 0x3d         /* 打印 "Error loading operation system"*/

00000081813EFE7D55AA      cmp word ,0xaa55            /* 检查 mbr 标记 0xaa55 */
00000087740B            jz 0x94
00000089807E1000          cmp byte ,0x0
0000008D74C8            jz 0x57
0000008FA0B707            mov al,
00000092EBA9            jmp short 0x3d      /* 打印 "Error loading operation system"*/


000000948BFC            mov di,sp          /* sp = 0x7c00 */
000000961E                push ds
0000009757                push di
000000988BF5            mov si,bp
0000009ACB                retf               /* 回到 0x7c00 */

------------------------------------------------------------------------
  这部分稍长一些,主要是通过了 4 个分区的检查后,进行的后续处理。包括失败后仍将打印出错信息,进入死循环。
 
下面代码:
00000052E84600            call word 0x9b            /* int13_read_sector_to_mem() */
00000055732A            jnc 0x81                  /* succssed */
--------------------------------------------
  调用 MBR 的读磁盘例程,最终,它使用 int13 /02h 号功能进行读入 memory 中。

并转到:
00000081813EFE7D55AA      cmp word ,0xaa55            /* 检查 mbr 标记 0xaa55 */
00000087740B            jz 0x94
----------------------------------
  检查 MBR 的标记 0x55aa

最终:
000000948BFC            mov di,sp          /* sp = 0x7c00 */
000000961E                push ds
0000009757                push di
000000988BF5            mov si,bp
0000009ACB                retf               /* 回到 0x7c00 */
------------------------------------------------------------------
  重新返回到 0x7c00 执行,但是,这里的 0x7c00 已不是原来的 MBR 了,而是正确的 boot sector 代码,这个 boot sector 应该是分区1 的 bootsector。
  这个 bootsector 就是由刚刚的 int13 /02 读入 0x7c00 的。

* 上面的检查中,任何一个出错,最终都会进入死循环。






第 5 部分,是整段 MBR 代码中最复杂,最长的从磁盘读扇区代码。涉及到具体的 bios int13 使用。

  这个部分主要还是要参考 int13 的使用方法。知道大概流程就行了。具体自己分析。




:wink:

[ 本帖最后由 mik 于 2009-6-9 00:52 编辑 ]

mik 发表于 2009-06-09 01:03

第二大部分是:MBR 的数据区域。


又分为 3 个部分:
1、空白未用的区域和一个指针区域
2、出错信息字会串。
3、disk partition table 磁盘分区表





第1部分:


/*** 下面这段是 MBR 用到的数据区:mbr_data ***/

000001B52C         /* 指向 "Invalid partition table." */
000001B6 44         /* 指向 "Error loading operation system." */
000001B763         /* 指向 "Missing operation system." */
000001B8 61
000001B9 A2
000001BA61
000001BBA2
000001BC00
000001BD00
-----------------------------------------------------------------------------
  这相当于指针,或者说是索引,用来索引出错的字符串信息。






第2部分:  
 
/*
char *mbr_str = "Invalid partition table."
               "Error loading operation system."
               "Missing operation system."
*/

0000012C496E7661 6C696420 70617274 6974696F
0000013C6E207461 626C6500 4572726F 72206C6F
0000014C6164696E 67206F70 65726174 696E6720
0000015C73797374 656D004D 69737369 6E67206F
0000016C70657261 74696E67 20737973 74656D
------------------------------------------------------------------------------
  存放着出错信息字符串。





第3部分是最重要的 DPT:

/* 1BE - 1FD = disk partion table */

/* 1BE - 1CD = DPT1 */
000001BE80          /* boot indicator */
                      /* 00:不可启动分区 */
                      /* 80:可启动分区(只可有1个启动分区)*/

000001BF01         /* 起始 header 号 */
000001C001         /* 起始 sector 号 */
000001C100         /* 起始 cylinder 号 */
000001C207         /* 系统属性 ID 标记
                     00h:未知操作系统
                     01h:DOS FAT12(16位扇区数)
                     02h:XENIX
                     04h:DOS FAT16(16位扇区数)
                     05h:DOS 扩展分区(DOS 3.3+)
                     06h:DOS 4.0 (Compaq 3.31), 32位扇区数
                     07h:HPFS/NTFS
                     0ah:OS/2
                     0bh:win95 fat32
                     0ch:win95 fat32 (LBA)
                     ... ...
                     */
000001C3FE         /* 结束 header 号 */
000001C4FF         /* 结束 sector 号 */
000001C5FF         /* 结束 cylinder 号 */
000001C63F000000   /* 此分区前的扇区总数 */         
000001CAD9A63F01   /* 此分区的扇区总数 */


/* 1CE - 1DD = DPT2 */
000001CE00            
000001CF00
000001D000            
000001D100
000001D200
000001D300
000001D400
000001D500
000001D600000000
000001DA00000000


/* 1DE - 1ED = DPT3 */
000001DE00
000001DF00
000001E000
000001E100
000001E200
000001E300
000001E400
000001E500
000001E600000000
000001EA00000000


/* 1EE - 1FD = DPT4 */
000001EE00
000001EF00
000001F000
000001F100
000001F200
000001F300
000001F400
000001F500
000001F600000000
000001FA00000000

-----------------------------------------------------------------------
  在这个磁盘映象里,只使用了1个分区,故 DPT2~DPT4 是空白的。

* DPT1:可启动标志,只能是 00h 或 80h
* DPT1:这指示分区的系统文件类型,这里的 07 表示 NTFS 文件类型。
  所有的具体的文件类型,可以在 linux 下使用 fdisk 工具查看。

mik 发表于 2009-06-09 01:12

整个 MBR 流程是:


INT19h (BIOS 自检后,int19 读 MBR 到 0x7c00)
 |
 |
   |
   V

0x7c00----> 自我复制到 0x061b ------+
                                                       |
                                                       |
                                                       |
--------------------------------------+
|
|
V                                                      failure
检查 4 个分区----->检查标志 0x55aa   --------->死循环
                                     |
                                     |
                                     |
   ------------------------+
   |
   |
   V
bootsector 读进 0x7c00-----> 重新执行 0x7c00

www_xylove 发表于 2009-06-09 03:18

原帖由 mik 于 2009-6-9 00:11 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
这两天饶有兴趣地对 xp 的 MBR 详尽地分析了一番,当作是温故知新。若有不对,敬请指出

下面是摘自实际 Xp 的磁盘 image 里的 windows XP MBR。

------------------------------------------------------ ...
版主真是厉害
分析windows的MBR,
linux的MBR也是一样的吧。

goter 发表于 2009-06-09 07:40

汇编啊,先mark一下,等有毅力了再来学习

emmoblin 发表于 2009-06-09 09:48

我觉得写这个mbr的时候应该不是汇编吧,你这个是反汇编的吧。
不过这样理解比较清楚。

xuxd32 发表于 2009-08-25 16:55

vxasm 发表于 2009-08-26 15:07

原帖由 emmoblin 于 2009-6-9 09:48 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
我觉得写这个mbr的时候应该不是汇编吧,你这个是反汇编的吧。
不过这样理解比较清楚。


MBR运行时只有BIOS中断可调用,而且MBR中存放代码的空间有限,只能用汇编。
页: [1]
查看完整版本: 对一个典型的 MBR 的详尽分析