没上过大学,纯野路子。不当之处请指证。
当然,如果有人在这方面比我还菜的话,有以下话想说。
如果你看完了什么都没记住,这很正常。如果想简单地看明白,并记住点什么的话,还是自己动手分析计算。
我用到两个工具: 二进制编辑器程序010EDIT,论坛有,可供下载。 BOCHS,可去官网下载,免费的。如果软件不会用,请看帮助文件。
FAT32简介
图1
这是硬盘0磁头0道1扇区的内容,这部分内容一般是由分区程序生成,与操作系统无关。共三部分:硬盘主引导记录MBR(MAIN BOOT RECORD),分区表DPT(DISK PARTITION TABLE),分区结束标志(55AA)。其中MBR共446B,DPT64B,结束标志2B。MBR是一段引导程序,主要功能是检查DPT的有效性,找到活动分区,并将活动分区的第一扇区内容读入内存,并跳转到这段程序继续执行。
那么我们来看MBR具体是怎么执行的:
<bochs:4> u/200
00007c00: (   mov si, 0x7c00 ; be007c
00007c03: (   cli ; fa
——初始化堆栈段前关中断
00007c04: (   xor ax, ax ; 33c0
00007c06: (   mov ss, ax ; 8ed0
00007c08: (   mov sp, si ; 8be6
00007c0a: (   sti ; fb
以下内容是将整个引导扇区内容复制到0:0600H开始的一段内存空间中,并跳转到061EH处继续执行MBR程序。
00007c0b: (   mov ds, ax ; 8ed8
00007c0d: (   mov es, ax ; 8ec0
00007c0f: (   push ax ; 50
00007c10: (   mov ax, 0x061e ; b81e06
00007c13: (   push ax ; 50
00007c14: (   mov di, 0x0600 ; bf0006
00007c17: (   mov cx, 0x0100 ; b90001
00007c1a: (   cld ; fc
00007c1b: (   rep movsw word ptr es:[di], word ptr ds:[si] ;
f3a5
00007c1d: (   retf ; cb
00007c1e: (   mov bx, 0x0763 ; bb6307
00007c21: (   mov si, 0x07be ; bebe07
00007c24: (   mov ax, 0x0031 ; b83100
00007c27: (   cmp byte ptr ds:[si], 0x80 ; 803c80
DS:[SI]相当于0x7be-0x600=0x1be。而从上图中看0x1be正是DPT开始的字节。
下面我们来看DPT的结构。DPT分为四个表项,各占16字节,每一表项描述一个分区,所以最多可以有四个分区。但现在的用法一般是:第一项描述主分区,第二项描述扩展分区,第三第四项为0。每一表项的不同字节意义如下:
第0字节 是否为活动分区,是则为80H,否则为00H
第1字节 该分区起始磁头号
第2字节 该分区起始扇区号(低6位)和起始柱磁头号(高2位)
第3字节 该分区起始柱面号的低8位
第4字节 系统标志,00H表该分区未使用,06H表高版本DOS系统,05H扩展DOS分区,65H表Netwear分区
第5字节 该分区结束磁头号
第6字节 该分区结束扇区号(低6位)和结束柱面号(高2位)
第7字节 该分区结束柱面号的低8位
第8~11字节 相对扇区号,该分区起始的相对逻辑扇区号,高位在后低位在前
第12~15字节 该分区所用扇区数,高位在后,低位在前
以上蓝字内容是复制的。我们只要知道0字节处,80H代表活动分区。那我们接着看MBR程序下面要做什么
00007c2a: (   jnz .+9 ; 7509
因为17BE处的内容为80H,所以第一次执行此程序段时这个跳转不会发生。但也说明活动分区并不一定要是第一个分区。跳转的位置为:00007C35
00007c2c: (   inc bx ; 43
原来BX内容为0X0763,而0X0764-0X600=0X0164(图1中),所以下条指令改写的是图1中0X0164中的内容
00007c2d: (   mov byte ptr ds:[bx], al ; 8807 AL=31H
00007c2f: (   inc bx ; 43
00007c30: (   mov byte ptr ds:[bx], 0x2f ; c6072f此时图1中字0X0164中的内容变为:2F31H
00007c33: (   inc ah ; fec4
00007c35: (   add si, 0x0010 ; 83c610
检查第二个表项
00007c38: (   inc al ; fec0
00007c3a: (   cmp al, 0x35 ; 3c35
00007c3c: (   jb .-23 ; 72e9
跳转到00007C27处,当AL=35H,四个表项全部检查完毕,向下执行
00007c3e: (   mov byte ptr ds:[bx], 0x3f ; c6073f
此时BX=0765。但如果有两个活动分区,或者四个活动分区则BX值增量。
00007c41: (   inc bx ; 43
00007c42: (   mov word ptr ds:[bx], 0x0020 ; c7072000
00007c46: (   or ah, ah ; 0ae4
AH记录活动分区的个数:这里为1
00007c48: (   jnz .+6 ; 7506
跳转至00007C50
00007c4a: (   mov bx, 0x06f8 ; bbf806
00007c4d: (   jmp .+137 ; e98900
如果没有活动分区,则跳转至00007CD9,调用一个过程显示一段:No partition bootable(在图1中地址0F8H处)的信息。
00007c50: (   mov al, byte ptr ds:0x764 ; a06407
00007c53: (   cmp ah, 0x02 ; 80fc02
00007c56: (   jb .+53 ; 7235
跳转至00007c8d,如果有两个以上活动分区则不跳转
00007c58: (   mov bx, 0x0744 ; bb4407
00007c5b: (   call .+128 ; e88000
跳至7CDE显示一段信息:Enter patrtition # to boot from, (后面的内容就是前面检查活动分区时修改的从0X164开始的一段数据)1/2/3/4?。并调用INT16H取得键盘输入,选择从哪里BOOT。
00007c5e: (   jmp .+5 ; eb05
00007c60: (   mov al, 0x07 ; b007
00007c62: (   call .+133 ; e88500
00007c65: (   xor ah, ah ; 32e4
00007c67: (   int 0x16 ; cd16
00007c69: (   mov byte ptr ds:0x782, al ; a28207
00007c6c: (   mov cl, 0x04 ; b104
00007c6e: (   sub al, 0x31 ; 2c31
00007c70: (   jb .-18 ; 72ee
00007c72: (   cmp al, cl ; 3ac1
00007c74: (   jnb .-22 ; 73ea
00007c76: (   shl al, cl ; d2e0
00007c78: (   xor ah, ah ; 32e4
00007c7a: (   add ax, 0x07be ; 05be07
00007c7d: (   mov si, ax ; 8bf0
00007c7f: (   cmp byte ptr ds:[si], 0x80 ; 803c80
00007c82: (   jnz .-36 ; 75dc
00007c84: (   mov bx, 0x0782 ; bb8207
00007c87: (   call .+84 ; e85400
00007c8a: (   mov al, byte ptr ds:0x782 ; a08207
00007c8d: (   sub al, 0x31 ; 2c31
00007c8f: (   mov cl, 0x04 ; b104
00007c91: (   shl al, cl ; d2e0
00007c93: (   xor ah, ah ; 32e4
00007c95: (   add ax, 0x07be ; 05be07
00007c98: (   mov word ptr ds:0x6f6, ax ; a3f606
00007c9b: (   mov cx, 0x0005 ; b90500
00007c9e: (   push cx ; 51
00007c9f: (   mov si, word ptr ds:0x6f6 ; 8b36f606
00007ca3: (   mov dx, word ptr ds:[si] ; 8b14
Ds[si]内容为图1中地址01BE处内容。也就是DPT的首字。
00007ca5: (   mov cx, word ptr ds:[si+2] ; 8b4c02
00007ca8: (   mov bx, 0x7c00 ; bb007c
00007cab: (   mov ax, 0x0201 ; b80102
00007cae: (   int 0x13 ; cd13
此时,DX=0180,CX=0001,BX=7C00,所以是将硬盘1磁头,0柱面,1扇区的内容读入7C00起始的内存中。我认为这段程序是存在BUG的。当活动分区的起始柱面号大于FFH时就会出错,什么时候柱面号会大于FFH,细看DPT后就会明了。
00007cb0: (   pop cx ; 59
00007cb1: (   jnb .+18 ; 7312
读盘成功跳转至00007cc5。否则执行磁盘复位,重复读盘或显示出错信息
00007cb3: (   push ax ; 50
00007cb4: (   xor ax, ax ; 33c0
00007cb6: (   int 0x13 ; cd13
00007cb8: (   pop ax ; 58
00007cb9: (   cmp ah, 0x80 ; 80fc80
00007cbc: (   jz .+2 ; 7402
00007cbe: (   loop .-34 ; e2de
00007cc0: (   mov bx, 0x0724 ; bb2407
00007cc3: (   jmp .+20 ; eb14
00007cc5: (   mov bx, 0x7c00 ; bb007c
00007cc8: (   cmp word ptr ds:[bx+510], 0xaa55 ; 81bffe0155aa
检查1磁头,0道,1扇区的分区表结束标志的有效性。
00007cce: (   jnz .+6 ; 7506
00007cd0: (   mov si, word ptr ds:0x6f6 ; 8b36f606
00007cd4: (   push bx ; 53
00007cd5: (   ret ; c3
00007cd6: (   mov bx, 0x070f ; bb0f07
00007cd9: (   call .+2 ; e80200
00007cdc: (   jmp .-2 ; ebfe
00007cde: (   mov al, byte ptr ds:[bx] ; 8a07
00007ce0: (   or al, al ; 0ac0
00007ce2: (   jz .+17 ; 7411
00007ce4: (   call .+3 ; e80300
00007ce7: (   inc bx ; 43
00007ce8: (   jmp .-12 ; ebf4
00007cea: (   push ax ; 50
00007ceb: (   push bx ; 53
00007cec: (   mov ah, 0x0e ; b40e
00007cee: (   mov bx, 0x0007 ; bb0700
00007cf1: (   int 0x10 ; cd10
00007cf3: (   pop bx ; 5b
00007cf4: (   pop ax ; 58
00007cf5: (   ret ; c3
00007cf6: (   add byte ptr ds:[bx+si], al ; 0000
<bochs:5>
现在,我们先放过MBR的内容,现在开始看分区表DPT。其实,没有注释部分的MBR内容,已经比较明了了。
我们将图1中主分区与扩展分区的描述项的内容写在下面:
80 01 01 00 0C FE FF FF 3F 00 00 00 02 AB 10 03
00 00 C1 FF 0F FE FF FF 41 AB 10 03 40 9A 0B 1A
先看主分区:80表示活动分区。01表示分区开始于1面或者1磁头。01 00,分区开始的扇区号和柱面号。这些在MBR程序中已做过介绍。
那么接下来0C,是系统标志,0C代表的含义是,FAT32,使用LBA模式和扩展的INT 13H。另外,FAT32还用0B表示,但0B的意义与0C是有区别的,分区格式小于等于8.4GB(实际可能是7.8);但无论是0B还是0C基本没什么意义,大容量磁盘都不再会使用CHS模式来访问磁盘,而使用LBA(LBA28/LBA48等),而更大容量的磁盘甚至不再使用MBR分区格式,而是使用GPT分区格式,每个分区可以大于2TB,但我没有64位机,完全不懂。这这也是我们在研究MBR程序时说的INT13H使用上存在BUG的原因。因为每个磁道只能有111111B-1个扇区(扇区数从1开始),每个柱面的磁头号有FE个。DPT格式中柱面号最多可以表示到1111111111B(1024)个,但现在的磁盘都会超过个数,我们经常会看到EF FF FF,但这里的柱面号可能已经超过了1024,但仍然这样表示。所以,通过此项来计算分区起始或分区大小是不准确的。
NTFC的话则为07。
FE FF FF 是分区结束的磁头,扇区和柱面。通过这一项可以算出分区的大小,但前面我们刚刚说过这个是不准确的。
3F 00 00 00这个是硬盘的稳含扇区,也就是此分区的到引导区之间的扇区数目。意思是这样的,双字0000003F个扇区占多少个字节:3F*200H=7E00H,于是我们找到硬盘数据7E00H地址,看一下这个扇区。
图2
我们再看一下C盘的数据,比较发现,C盘的数据地址00H起至200H的内容与这一扇区的内容是完全一样的。其实这就是主分区的扩展引导记录EBR(EXTAND BOOT RECORD),而活动磁盘上的EBR我们又称为:第二引导扇区或系统引导扇区DBR(DOS BOOT RECORD)。也是MBR程序要读取到内存7C00H地址处的内容。这也是C盘的开始扇区。所以上文称MBR为硬盘主引导记录,而不是C盘主引导记录。
02 AB 10 03表示分区的大小(扇区数)。分区的字节数是:0310AB02*200H=621560400H,那么,621560400H+7E00H处是什么内容呢?
图3
这是哪里呢?首先这不是D盘的第一扇区。但这个扇区却有一个DPT。根据我们计算的过程,主分区应该是在这一扇区的上一扇区结束。那么这里是扩展分区的开始扇区。我们看一下扩展分区与主引导扇区间的扇区数为:41 AB 10 03。字节数为:0310AB41*200H=621568200H,正好与图3中的地址吻合。那么扩展分区的大小:40 9A 0B 1A。1A0B9A40*200H=3417348000H,再加上621568200H,减1正好是硬盘结束的地址:3A388B01FFH。
再看主分区,将621568200H-1,是主分区结束的字节地址。但是C盘却是在620917FFFH处结束。C盘共620918000H个字节,这个数字与WINDOWS看到的C盘容量(26316210176)是相同的。比主分区少了0C48400H个字节,这些字节在C盘之后。那么主分区不是C盘,C盘包含在主分区里。
现在看图3,这里的DPT也有两个分区表项。这里的DPT是主引导扇区分区表中扩展分区的再分区表。暂称作第二分区
第二分区的主分区(第二主分区):00 01 C1 FF 0B FE FF FF 3F 00 00 00 00 4F D5 92 09 。非活动分区,开始磁头号为1,扇区号与柱面号为:C1 FF。结束磁头,扇区,柱面号为:FE FF FF。主分区距分区表的字节数是7E00,而这也与D盘的00H处的数据是相同的。D盘开始于第一扩展分区7E00字节处。在硬盘中的地址为621570000H。第二主分区的大小为:0992D54F*200H=1325AA9E00H。D盘大小为:1324780000H比第二主分区小 1329E00H字节,距第二扩展分区1331A00H字节。
我们看到不仅C盘比第一分区的主分区小,D盘比第二分区的主分区小。那么C盘前面的60几个扇区,D盘前面和后面的扇区做什么用了呢?其实这些都是系统隐藏扇区,系统是无法访问的,除分区表的空间外,其他可以存储一些我们要保密的数据,在这里的数据通常情况下是安全的。但也许我们并不想要这些隐藏扇区,毕竟这些扇区也不是绝对安全的,至少破坏MBR的病毒就能找到这些扇区。要简单地解决这个问题现在只有一个办法:不使用MBR格式的分区,而使用GPT分区格式。因为MBR分区格式,必须是以柱面为单位的。不能对柱面进行分割。
好了,我们再看DPT分区表。DPT分区表的数据结构好像是一个单链表,第一分区:主分区地址,扩展分区地址指向下一个DPT分区表,NULL,NULL。第二分区:第二主分区地址,第二扩展分区地址指向第三分区DPT表,NULL,NULL。于是我们得出结论:最后一个分区:最后一个主分区地址,NULL,NULL,NULL。DPT中只要使用两个表项就可以做到很多个分区。以前的许多资料也是这样介绍DPT数据结构的。但我们通过DPT表寻找分区时并不是一帆风顺的,比如我的硬盘,230G,分为CDEF四个区,而第三分区DPT表扩展分区的隐含扇区却不是相对于第三分区分区表所在扇区的,而是相对于第二分区分区表所在扇区。原因我还不知道,是不是因为第四字节?哪位大神知道告诉我。
那么我们继续,我们看图二。DBR中也包括三部分内容:DBR程序,本分区参数记录表BPB(BIOS PARAMETER BLOCK)和结束标记55AA。这部分内容由高级格式化程序生成(与操作系统相关)。DBR程序的主要功能是判断本分区根目录前的两个文件是否是系统引导文件,并把第一个文件读入内存,并将控制权交给该文件。DBR程序从扇区0字节开始,前三个字节利用一个短转移+NOP指令(NOP指令一般是由编译器生成的,具体请参照汇编编译器关于JMP指令的编译)跳过从03H——59H共57H字节的BPB。
下面是图二中的内容的反汇编
<bochs:4> u/280
00007c00: (   jmp .+88 ; eb58
跳转至7C5A
00007c02: (   nop ; 90
00007c5a: (   xor cx, cx ; 33c9
00007c5c: (   mov ss, cx ; 8ed1
00007c5e: (   mov sp, 0x7bf4 ; bcf47b
00007c61: (   mov es, cx ; 8ec1
00007c63: (   mov ds, cx ; 8ed9
00007c65: (   mov bp, 0x7c00 ; bd007c
00007c68: (   mov byte ptr ss:[bp+2], cl ; 884e02
00007c6b: (   mov dl, byte ptr ss:[bp+64] ; 8a5640
图2中偏移40H处的一个字节,是INT13H中断呼叫预设值,软盘为00H,硬盘为:80H
00007c6e: (   mov ah, 0x08 ; b408
00007c70: (   int 0x13 ; cd13
00007c72: (   jnb .+5 ; 7305
跳至00007C79。调用INT13H的8H号功能获取磁盘参数。INT13H的这一功能我不了解,听说,当磁盘大于8G时应当使用扩展的INT13H的48H号功能。如果使用8号功能则CF=1。但对于我的250G的硬盘,C盘24.5G,执行8号功能后,CF=0,而CX=FEFF,DX=FE01。所以这里肯定有没搞明白的地方。
00007c74: (   mov cx, 0xffff ; b9ffff
00007c77: (   mov dh, cl ; 8af1
00007c79: (   movzx eax, dh ; 660fb6c6
00007c7d: (   inc ax ; 40
00007c7e: (   movzx edx, cl ; 660fb6d1
00007c82: (   and dl, 0x3f ; 80e23f
00007c85: (   mul ax, dx ; f7e2
00007c87: (   xchg ch, cl ; 86cd
00007c89: (   shr ch, 0x06 ; c0ed06
00007c8c: (   inc cx ; 41
00007c8d: (   movzx ecx, cx ; 660fb7c9
00007c91: (   mul eax, ecx ; 66f7e1
将CHS换算成LBA线性地址
00007c94: (   mov dword ptr ss:[bp-8], eax ; 668946f8
00007c98: (   cmp word ptr ss:[bp+22], 0x0000 ; 837e1600
00007c9c: (   jnz .+56 ; 7538
图二中相对扇区起始偏移16 H地址处的一个字表示FAT16文件分配表的长度,FAT32位则为零
00007c9e: (   cmp word ptr ss:[bp+42], 0x0000 ; 837e2a00
00007ca2: (   jnbe .+50 ; 7732
图2相对扇区起始偏移2AH处的两个字节表示文件系统的主次版本。这里为0不跳转
00007ca4: (   mov eax, dword ptr ss:[bp+28] ; 668b461c
图2相对扇区起始偏移1C处的一个双字,表示分区前隐藏扇区数目,也就是分区表到EBR的扇区偏移。
00007ca8: (   add eax, 0x0000000c ; 6683c00c
至于这里为什么是0CH,可能是MS公司规定的。
00007cac: (   mov bx, 0x8000 ; bb0080
00007caf: (   mov cx, 0x0001 ; b90100
00007cb2: (   call .+43 ; e82b00
转移至00007ce0。7CE0处是一个过程,将会把相对分区表偏移EAX个扇区开始的共ECX个扇区内容装入内存ES:BX处。
00007cb5: (   jmp .+840 ; e94803
跳转至:0X:8000,也就是将系统控制权交给硬盘相对MBR扇区偏移3FH+CH扇区存放的程序。为了更深入理解BPB我们还要对这个程序进行分析。我们暂称这个程序为操作系统引导程序。
00007cb8: (   mov al, byte ptr ds:0x7dfa ; a0fa7d
由此可以看出图二中相对扇区起始偏移01F9至01FB存放的数据是出错信息相对扇区起始偏移地址的低八位。以下显示一段信息:Disk erro 换行Press any key to restart
00007cbb: (   mov ah, 0x7d ; b47d
00007cbd: (   mov si, ax ; 8bf0
00007cbf: (   lodsb al, byte ptr ds:[si] ; ac
00007cc0: (   test al, al ; 84c0
00007cc2: (   jz .+23 ; 7417
DBR中的出错信息共三行,用FFH间隔,用00H结束,当显示完Press any key to restart后则执行7CDB处的CBW命令。等待输入重新运行MBR。
00007cc4: (   cmp al, 0xff ; 3cff
00007cc6: (   jz .+9 ; 7409
00007cc8: (   mov ah, 0x0e ; b40e
00007cca: (   mov bx, 0x0007 ; bb0700
00007ccd: (   int 0x10 ; cd10
00007ccf: (   jmp .-18 ; ebee
跳至7CBF
00007cd1: (   mov al, byte ptr ds:0x7dfb ; a0fb7d
00007cd4: (   jmp .-27 ; ebe5
00007cd6: (   mov al, byte ptr ds:0x7df9 ; a0f97d
00007cd9: (   jmp .-32 ; ebe0
跳至7CBB
00007cdb: (   cbw ; 98
00007cdc: (   int 0x16 ; cd16
00007cde: (   int 0x19 ; cd19
过程开始
00007ce0: (   pushad ; 6660
00007ce2: (   cmp eax, dword ptr ss:[bp-8] ; 663b46f8
将EAX的值与前面利用INT13H获得的磁盘参数计算出的LBA线性扇区数比较,看是否超过此范围,。
00007ce6: (   jb .+74 ; 0f824a00
未超范围则跳转至7d34,否则调用扩展的INT13H。
00007cea: (   push 0x00000000 ; 666a00
00007ced: (   push eax ; 6650
00007cef: (   push es ; 06
00007cf0: (   push bx ; 53
00007cf1: (   push 0x00010010 ; 666810000100
00007cf7: (   cmp byte ptr ss:[bp+2], 0x00 ; 807e0200
00007cfb: (   jnz .+32 ; 0f852000
00007cff: (   mov ah, 0x41 ; b441
00007d01: (   mov bx, 0x55aa ; bbaa55
00007d04: (   mov dl, byte ptr ss:[bp+64] ; 8a5640
00007d07: (   int 0x13 ; cd13
检查INT13H扩展功能是否存在
00007d09: (   jb .+28 ; 0f821c00
00007d0d: (   cmp bx, 0xaa55 ; 81fb55aa
00007d11: (   jnz .+20 ; 0f851400
00007d15: (   test cl, 0x01 ; f6c101
00007d18: (   jz .+13 ; 0f840d00
00007d1c: (   inc byte ptr ss:[bp+2] ; fe4602
00007d1f: (   mov ah, 0x42 ; b442
00007d21: (   mov dl, byte ptr ss:[bp+64] ; 8a5640
00007d24: (   mov si, sp ; 8bf4
00007d26: (   int 0x13 ; cd13
扩展读
00007d28: (   mov al, 0xf9 ; b0f9
00007d2a: (   pop eax ; 6658
00007d2c: (   pop eax ; 6658
00007d2e: (   pop eax ; 6658
00007d30: (   pop eax ; 6658
00007d32: (   jmp .+42 ; eb2a
00007d34: (   xor edx, edx ; 6633d2
00007d37: (   movzx ecx, word ptr ss:[bp+24] ; 660fb74e18
图2扇区起始偏移18H处的两个字节表示:每磁道的扇区数。值为003F。
00007d3c: (   div eax, ecx ; 66f7f1
00007d3f: (   inc dl ; fec2
00007d41: (   mov cl, dl ; 8aca
00007d43: (   mov edx, eax ; 668bd0
00007d46: (   shr edx, 0x10 ; 66c1ea10
00007d4a: (   div ax, word ptr ss:[bp+26] ; f7761a
图2中相对扇区起始偏移1A处的两个字节表示磁盘的磁头数,值为FFH
00007d4d: (   xchg dh, dl ; 86d6
00007d4f: (   mov dl, byte ptr ss:[bp+64] ; 8a5640
40H处存放驱动器号
00007d52: (   mov ch, al ; 8ae8
00007d54: (   shl ah, 0x06 ; c0e406
00007d57: (   or cl, ah ; 0acc
以上将LBA转换为CHS
00007d59: (   mov ax, 0x0201 ; b80102
00007d5c: (   int 0x13 ; cd13
00007d5e: (   popad ; 6661
00007d60: (   jb .-172 ; 0f8254ff
如果CF=1则跳至00007cb8
00007d64: (   add bx, 0x0200 ; 81c30002
00007d68: (   inc eax ; 6640
00007d6a: (   dec cx ; 49
00007d6b: (   jnz .-143 ; 0f8571ff
00007d6f: (   ret ; c3
过程正常返回
00007d70: (   dec si ; 4e
00007d71: (   push sp ; 54
00007d72: (   dec sp ; 4c
00007d73: (   inc sp ; 44
00007d74: (   push dx ; 52
00007d75: (   and byte ptr ds:[bx+si], ah ; 2020
00007d77: (   and byte ptr ds:[bx+si], ah ; 2020
00007d79: (   and byte ptr ds:[bx+si], ah ; 2020
00007d7b: (   add byte ptr ds:[bx+si], al ; 0000
00007d7d: (   add byte ptr ds:[bx+si], al ; 0000
00007d7f: (   add byte ptr ds:[bx+si], al ; 0000
00007d81: (   add byte ptr ds:[bx+si], al ; 0000
00007d83: (   add byte ptr ds:[bx+si], al ; 0000
00007d85: (   add byte ptr ds:[bx+si], al ; 0000
00007d87: (   add byte ptr ds:[bx+si], al ; 0000
00007d89: (   add byte ptr ds:[bx+si], al ; 0000
00007d8b: (   add byte ptr ds:[bx+si], al ; 0000
00007d8d: (   add byte ptr ds:[bx+si], al ; 0000
00007d8f: (   add byte ptr ds:[bx+si], al ; 0000
00007d91: (   add byte ptr ds:[bx+si], al ; 0000
00007d93: (   add byte ptr ds:[bx+si], al ; 0000
00007d95: (   add byte ptr ds:[bx+si], al ; 0000
00007d97: (   add byte ptr ds:[bx+si], al ; 0000
00007d99: (   add byte ptr ds:[bx+si], al ; 0000
00007d9b: (   add byte ptr ds:[bx+si], al ; 0000
00007d9d: (   add byte ptr ds:[bx+si], al ; 0000
00007d9f: (   add byte ptr ds:[bx+si], al ; 0000
00007da1: (   add byte ptr ds:[bx+si], al ; 0000
00007da3: (   add byte ptr ds:[bx+si], al ; 0000
00007da5: (   add byte ptr ds:[bx+si], al ; 0000
00007da7: (   add byte ptr ds:[bx+si], al ; 0000
00007da9: (   add byte ptr ds:[bx+si], al ; 0000
00007dab: (   add byte ptr ds:[di], cl ; 000d
00007dad: (   or cl, byte ptr ss:[bp+84] ; 0a4e54
00007db0: (   dec sp ; 4c
00007db1: (   inc sp ; 44
00007db2: (   push dx ; 52
00007db3: (   and byte ptr ds:[bx+di+115], ch ; 206973
00007db6: (   and byte ptr ds:[di+105], ch ; 206d69
00007db9: (   jnb .+115 ; 7373
00007dbb: (   imul bp, word ptr ss:[bp+103], 0x0dff ; 696e67
ff0d
00007dc0: (   or al, byte ptr ds:[si+105] ; 0a4469
00007dc3: (   jnb .+107 ; 736b
00007dc5: (   and byte ptr ds:[di+114], ah ; 206572
00007dc8: (   jb .+111 ; 726f
00007dca: (   jb .-1 ; 72ff
00007dcc: (   or ax, 0x500a ; 0d0a50
00007dcf: (   jb .+101 ; 7265
00007dd1: (   jnb .+115 ; 7373
00007dd3: (   and byte ptr ds:[bx+di+110], ah ; 20616e
00007dd6: (   jns .+32 ; 7920
00007dd8: (   imul sp, word ptr ds:[di+121], 0x0020 ; 6b6579
20
00007ddc: (   jz .+111 ; 746f
00007dde: (   and byte ptr ss:[bp+si+101], dh ; 207265
00007de1: (   jnb .+116 ; 7374
00007de3: (   popa ; 61
00007de4: (   jb .+116 ; 7274
00007de6: (   or ax, 0x000a ; 0d0a00
00007de9: (   add byte ptr ds:[bx+si], al ; 0000
00007deb: (   add byte ptr ds:[bx+si], al ; 0000
00007ded: (   add byte ptr ds:[bx+si], al ; 0000
00007def: (   add byte ptr ds:[bx+si], al ; 0000
00007df1: (   add byte ptr ds:[bx+si], al ; 0000
00007df3: (   add byte ptr ds:[bx+si], al ; 0000
00007df5: (   add byte ptr ds:[bx+si], al ; 0000
00007df7: (   add byte ptr ds:[bx+si], al ; 0000
00007df9: (   lodsb al, byte ptr ds:[si] ; ac
00007dfa: (   mov di, 0x00cc ; bfcc00
00007dfd: (   add byte ptr ds:[di-86], dl ; 0055aa
下面是具体BPB各参数的意义:
FAT32系统:
0BH 2字节 每扇区的字节数一般为0200H。
0DH 1字节 每簇扇区数,取决于分区文件系统格式和分区大小。我的C盘上是20H
0EH 2字节 为操作系统保留的扇区数。也即逻辑盘引导区至文件分配表(FAT表)的偏移,以扇区为单位。图2中为26H
10H 1字节 FAT表的个数,一般为02H。
11H 2字节 FAT16中根目录中允许登记的目录项的个数。FAT32中为0。
13H 2字节 容量小于32MB时表示扇区数目,否则不使用,仅为兼容小磁盘,FAT32中为0
15H 1字节 磁盘介质类型,硬盘用F8表示。
16H 2字节 FAT16中文件分配表长度,FAT32中未使用,值为0
18H 2字节 每磁道扇区数。003F
1AH 2字节 磁盘磁头数。图二中0FFH表示255个磁头。
1CH 4字节 分区前隐藏的扇区数。即该分区分区表到引导扇区的扇区数目:0000003F
20H 4字节 逻辑盘的扇区总数。图二中为0310AAF0,即62155E000H字节。这个比C盘的实际容量:620918000H字节要大,而又比分区表DPT中所标识的字节数:621560400H要小。我们从硬盘上看,偏移0310AAF0+3F-1扇区之前都是有数据的。而从0310AAF0+3F至第一主分区结束则无数据存储,全部为0。
24H 4字节 每个FAT表所占扇区数目。
28H 2字节 FAT表镜像标志。0表示保存有两份FAT表互为备份,而1则只有一份。
2AH 2字节 文件系统主次版本(fat32系统保留未使用)
2CH 4字节 磁盘根目录起始簇号
30H 2字节 文件系统参数扇区号,通常为引导扇区的下一扇区。即01H
32H 2字节 备份分区引导扇区的扇区号
34H 12字节 保留未使用。
40H 1字节 磁盘号,为INT13H呼叫所做的预高值,80为硬盘,00为软盘
41H 1字节 用于中断13H呼叫
42H 1字节 磁盘读写参数扩展标志,值为29H
43H 4字节 格式化时随机产生的磁盘卷的序列号
47H 11字节 格式化时人工输入的磁盘卷标号
52H 8字节 文件系统标识号(FAT32)
而NTFC文件系统则不同,BPB的长度也不一样。
操作系统引导程序:
图4
00008000: (   movzx eax, byte ptr ss:[bp+16] ; 660fb64610
00008005: (   mov ecx, dword ptr ss:[bp+36] ; 668b4e24
00008009: (   mul eax, ecx ; 66f7e1
0000800c: (   add eax, dword ptr ss:[bp+28] ; 6603461c
00008010: (   movzx edx, word ptr ss:[bp+14] ; 660fb7560e
00008015: (   add eax, edx ; 6603c2
00008018: (   mov dword ptr ss:[bp-4], eax ; 668946fc
以上计算文件分配表结束后的第一个扇区相对硬盘0面0道1扇区的偏移。并存入内存7BFCH。
0000801c: (   mov dword ptr ss:[bp-12], 0xffffffff ; 66c746f
4ffffffff
00008024: (   mov eax, dword ptr ss:[bp+44] ; 668b462c
00008028: (   cmp eax, 0x00000002 ; 6683f802
0000802c: (   jb .-858 ; 0f82a6fc
如果根目录起始簇数小于2则跳至7CD6显示一段出错信息:“NTLDR is missing” 另起行:“Press any key to restart”。为什么要与2进行比较,这与文件分配表FAT有关。FAT表中前两个表项(表项00,01)一般不用,而用来存储磁盘的介质类型。FAT32系统硬盘一般为F8 FF FF 0F FF FF FF 0F。所以有效簇号是从2开始的。下面简介下FAT表
图6
图6是FAT表第一扇区的内容。FAT表的每一个表项是4字节。第0,1表项的值是F8 FF FF 0F FF FF FF FF。和我们上面说的不一样?看完后面再来理解:FAT表项值只要大于等0FFFFFF8就意味着文件在此簇号结束,而0FFFFFF7(F7 FF FF 0F)则意味着坏簇。而下一行程序的数字0FFFFFF8也就可以理解了。所有小于这个数字并且大于1的数字都表示文件的下一簇号——文件往往一个簇放不下,那么就将文件用到的第一个簇号存在PDT中,找到这一簇号对应的FAT表项,则以下是一个链表,这个链表只有一个数据项:一个指针,指向下一个链表的地址。一个文件在PAT表中的各表项就是这样一个链表。当链表指向大于0FFFFFF8时,链表结束。
00008030: (   cmp eax, 0x0ffffff8 ; 663df8ffff0f
00008036: (   jnb .-868 ; 0f839cfc FC9C+803A=7CD6
0000803a: (   push eax ; 6650
0000803c: (   sub eax, 0x00000002 ; 6683e802
00008040: (   movzx ebx, byte ptr ss:[bp+13] ; 660fb65e0d
00008045: (   mov si, bx ; 8bf3
00008047: (   mul eax, ebx ; 66f7e3
0000804a: (   add eax, dword ptr ss:[bp-4] ; 660346fc
0000804e: (   mov bx, 0x8200 ; bb0082
00008051: (   mov di, bx ; 8bfb
00008053: (   mov cx, 0x0001 ; b90100
00008056: (   call .-889 ; e887fc
调用7CE0过程。将相对0面0道1扇区偏移626FH扇区的一个扇区内容,读入内存8200H。
00008059: (   cmp byte ptr ds:[di], ch ; 382d
0000805b: (   jz .+30 ; 741e
1E+805D=807B
0000805d: (   mov cl, 0x0b ; b10b
0000805f: (   push si ; 56
00008060: (   mov si, 0x7d70 ; be707d
00008063: (   rep cmpsb byte ptr es:[di], byte ptr ds:[si] ;
f3a6
在这里应当是REPE CMPSB 应该是反汇编的问题。这个操作其实是在文件目录表FDT(File Directory Table)中查找名称为NTLDR的文件,这个文件在C盘根目录。请看下面关于FDT的介绍。
图5
FDT中长文件名与否决定了表项长度,32B或64B。如果是64B则前32B为长文件名链接说明,后32字节为文件属性说明:文件长度,起始地址,日期时间等。不支持长文件名的情况下,只有32B的文件属性说明。具体来说:
0-7字节 文件名
8-A字节 文件扩展名
0B字节 文件属性。对于FAT32来讲,0至5位分别是只读位、隐藏位、系统位、卷标位、子目录位、归档位。最高两位保留未用
0C-0D字节 短文件名字节校验和
0E-0F字节 文件创建时间
10-11字节 文件创建日期
12-13字节 文件最近访问日期
14-15字节 文件存放的起始簇号高16位
16-17字节 文件最近修改时间
18-19字节 文件最近修改日期
1A-1B字节 文件存放的起始簇号低16位
1C-1F字节 文件长度(字节)
其中文件名部分,字节不足则用空格的ASCII码20填充。而0字节还有特别的含义。00表示空目录。E5表示被删除的文件,2E表示特殊文件“.”或“..”。其他值才表示文件名的第一个字符ASCII码。
00008065: (   pop si ; 5e
00008066: (   jz .+27 ; 741b
8068+1B=8083。从图五可以看出当DI=8280H+0BH时跳转。
00008068: (   add di, cx ; 03f9
0000806a: (   add di, 0x0015 ; 83c715
从805D到这里,相当于DI=DI+20H。
0000806d: (   cmp di, bx ; 3bfb
0000806f: (   jb .-24 ; 72e8
E8+71=59
00008071: (   dec si ; 4e
00008072: (   jnz .-38 ; 75da
00008074: (   pop eax ; 6658
00008076: (   call .+101 ; e86500
00008079: (   jb .-65 ; 72bf
0000807b: (   add sp, 0x0004 ; 83c404
0000807e: (   jmp .-939 ; e955fc
FC55+8081=7CD6
00008081: (   add byte ptr ds:[bx+si], ah ; 0020
以下查找NTLDR的起始位置并将NTLDR文件完整地读入内存,并将系统控制仅交给NTLDR程序。
00008083: (   add sp, 0x0004 ; 83c404
00008086: (   mov si, word ptr ds:[di+9] ; 8b7509
SI=0
00008089: (   mov di, word ptr ds:[di+15] ; 8b7d0f
DI=F9A5
0000808c: (   mov ax, si ; 8bc6
0000808e: (   shl eax, 0x10 ; 66c1e010
00008092: (   mov ax, di ; 8bc7
00008094: (   cmp eax, 0x00000002 ; 6683f802
00008098: (   jb .-966 ; 0f823afc
0000809c: (   cmp eax, 0x0ffffff8 ; 663df8ffff0f
000080a2: (   jnb .-976 ; 0f8330fc
000080a6: (   push eax ; 6650
000080a8: (   sub eax, 0x00000002 ; 6683e802
000080ac: (   movzx ecx, byte ptr ss:[bp+13] ; 660fb64e0d
000080b1: (   mul eax, ecx ; 66f7e1
000080b4: (   add eax, dword ptr ss:[bp-4] ; 660346fc
000080b8: (   mov bx, 0x0000 ; bb0000
000080bb: (   push es ; 06
000080bc: (   mov es, word ptr ds:0x8081 ; 8e068180
ES=2000,BX=0,EAX=1F96CFH
000080c0: (   call .-995 ; e81dfc
FC1D+80C3=7CE0
000080c3: (   pop es ; 07
ES=0
000080c4: (   pop eax ; 6658
EAX=F9A5
000080c6: (   shr bx, 0x04 ; c1eb04
000080c9: (   add word ptr ds:0x8081, bx ; 011e8180
将刚分配的字节数右移四位加到8081H处做为下一次分配内存的段地址。
000080cd: (   call .+14 ; e80e00
80D0+0E=80DE
000080d0: (   jnb .+2 ; 0f830200
000080d4: (   jb .-48 ; 72d0
D0+80D6=8086,如果不是文件的最后一簇则重复读下一簇。NTLR共用10H个簇
000080d6: (   mov dl, byte ptr ss:[bp+64] ; 8a5640
000080d9: (   jmp far 2000:0000 ; ea00000020
000080de: (   shl eax, 0x02 ; 66c1e002
000080e2: (   call .+17 ; e81100
80E5+11=80F6
000080e5: (   mov eax, dword ptr es:[bx+di] ; 26668b01
ES=0,DI=7E00,所以此时EAX的值等于NTLR文件起始簇号所对应FAT表项的值。
000080e9: (   and eax, 0x0fffffff ; 6625ffffff0f
000080ef: (   cmp eax, 0x0ffffff8 ; 663df8ffff0f
而FAT表项的值,如果大于0FFFFFF8则表示此簇为文件的最后一簇,否则为文件存放的下一簇号。
000080f5: (   ret ; c3
下面的过程计算NTLR文件起始簇号所对应FAT表项的扇区(EAX)和相对扇区起始的偏移(EDX),将表项所在扇区相对0道0面1扇区的相对偏移,并将其读入内存7E00H处。而最终BX值等于FAT表中NTLR文件起始簇号所对应表项相对扇区开始的偏移。
000080f6: (   mov di, 0x7e00 ; bf007e
000080f9: (   movzx ecx, word ptr ss:[bp+11] ; 660fb74e0b
000080fe: (   xor edx, edx ; 6633d2
00008101: (   div eax, ecx ; 66f7f1
00008104: (   cmp eax, dword ptr ss:[bp-12] ; 663b46f4
00008108: (   jz .+58 ; 743a
0000810a: (   mov dword ptr ss:[bp-12], eax ; 668946f4
0000810e: (   add eax, dword ptr ss:[bp+28] ; 6603461c
00008112: (   movzx ecx, word ptr ss:[bp+14] ; 660fb74e0e
00008117: (   add eax, ecx ; 6603c1
0000811a: (   movzx ebx, word ptr ss:[bp+40] ; 660fb75e28
0000811f: (   and bx, 0x000f ; 83e30f
00008122: (   jz .+22 ; 7416
16+8124=813A
00008124: (   cmp bl, byte ptr ss:[bp+16] ; 3a5e10
00008127: (   jnb .-1109 ; 0f83abfb
FBAB+812B=7CD6H
0000812b: (   push dx ; 52
0000812c: (   mov ecx, eax ; 668bc8
0000812f: (   mov eax, dword ptr ss:[bp+36] ; 668b4624
00008133: (   mul eax, ebx ; 66f7e3
00008136: (   add eax, ecx ; 6603c1
00008139: (   pop dx ; 5a
0000813a: (   push dx ; 52
0000813b: (   mov bx, di ; 8bdf
0000813d: (   mov cx, 0x0001 ; b90100
00008140: (   call .-1123 ; e89dfb
FB9D+8143=7CE0H
00008143: (   pop dx ; 5a
00008144: (   mov bx, dx ; 8bda
00008146: (   ret ; c3
00008147: (   add byte ptr ds:[bx+si], al ; 0000
00008149: (   add byte ptr ds:[bx+si], al ; 0000
000081f9: (   add byte ptr ds:[bx+si], al ; 0000
000081fb: (   add byte ptr ds:[bx+si], al ; 0000
000081fd: (   add byte ptr ds:[di-86], dl ; 0055aa
接下来跳至2000:00H处执行的程序就有点大了,20H个扇区,而实际上这个程序的有效字节大约在30000H多。这就可以做很多事情,比如:进入保护模式等,离主题越来越远了。
好了,我们回到正题。上面程序中是如何计算表项地址的?
FAT表项所在扇区相对FAT表起始扇区偏移扇区数(对应扇区)=对应簇号*4/200,因为每个FAT表项占4个字节。
FAT表项相对对应扇区偏移字节=对应簇号*4%200。
操作系统就是通过这种方法利用FDT和FAT查找并将文件读入内存。
紧接FDT的就是罗辑盘的DATA区。具体存放各种文件(以簇为单位)。当EBR,FAT,甚至FDT损坏后,其实,我们仍然可以通过搜索DATA区的办法来恢复文件。
注:本帖由看雪论坛志愿者PEstone 重新将DOC整理排版,若和原文有出入,以原作者附件为准
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)