Win10 64位专业版安装镜像启动过程分析

为了搞明白FAT32文件系统中的操作系统是如何启动的,花了两天时间分析了一下Win10安装镜像的启动过程。

UltraISO可以制作Win10的安装U盘,制作完毕后U盘就被格式化成了FAT32文件系统。然后可以用WinHex打开U盘,将扇区导出到文件,并用ndisasm.exe反汇编,便可以得到指令和地址的对应关系。通过逆向分析,正好可以用来学习FAT32的启动过程,这比只看书或者用Bochs模拟真实多了。

计算机上电自检后,将硬盘中的Master Boot Reocrd(第0扇区)加载到0000:0x7C00处,然后就从这里开始执行。

MBR首先将自己移动到了0000:0x0600处,然后扫描主分区表,将可启动分区的Dos Boot Record(第256扇区)加载到了0000:0x7C00处。在安装了int 13h的hook程序(将CHS读取转换为LBA读取)后,最终将控制权交给DBR。

DBR是标准的FAT32启动扇区,但是受只有一个扇区的限制,DBR只是将U盘上的第268扇区读取到了0000:8000处并跳转到该处运行,此外还提供了显示字符和读取扇区的例程。

268扇区在FAT32文件系统根目录中查找BOOTMGR,并将其加载到2000:0000处。

BOOTMGR可以在U盘中看见,应该是UEFI的引导程序,足足有400KB,逆向分析太难了,也没什么意义。

;ndisasm.exe -k 0x1d,0x17 -k 0x187,0x79 FAT32MBR

00000000  FA                cli
00000001  31C0              xor ax,ax
00000003  8ED8              mov ds,ax
00000005  8EC0              mov es,ax
00000007  8ED0              mov ss,ax
00000009  BC007C            mov sp,0x7c00
0000000C  FB                sti
0000000D  FC                cld

;将代码移动到0x0600处,并跳转到相对地址0xdc
0000000E  89E6              mov si,sp
00000010  BF0006            mov di,0x600
00000013  B90001            mov cx,0x100
00000016  F3A5              rep movsw
00000018  EADC060000        jmp 0x0:0x6dc

;int 13h的hook数据
;struct DiskAddressPacket
;{
;   BYTE PacketSize = 0x10;
;   BYTE Reserved = 0x00;
;   WORD BlockCount = 0x0001;
;   DWORD BufferAddr = 0000:7c00;
;   QWORD BlockNo = 0x00000000_00000000
;}
;16字节int 13h使用的Disk Address Packet
;1字节驱动器号 = 0x80
;2字节每磁道扇区数 = 0x3f
;2字节磁头数 = 0xff
;2字节柱面数 = 0x03d3
0000001D  skipping 0x17 bytes

;int 13h的hook代码
00000034  1E                push ds
00000035  0E                push cs
00000036  1F                pop ds
00000037  3A161000          cmp dl,[0x10]
0000003B  7406              jz 0x43             ;如果int 13h操作的驱动器是启动U盘,则需要hook
0000003D  1F                pop ds              ;恢复ds
0000003E  EA36E700F0        jmp 0xf000:0xe736   ;调用原始int 13h

;如果ax为0x54fb则直接返回,不知道这是干嘛用的
00000043  3DFB54            cmp ax,0x54fb
00000046  7505              jnz 0x4d
00000048  8CD8              mov ax,ds
0000004A  FB                sti
0000004B  EB1D              jmp short 0x6a

;08号功能
0000004D  80FC08            cmp ah,0x8
00000050  751B              jnz 0x6d
00000052  E88100            call 0xd6       ;调用原始int 13h
00000055  8A361300          mov dh,[0x13]
00000059  FECE              dec dh          ;dh = ds:[0x13] - 1 = 0xfe = 254
0000005B  8B0E1500          mov cx,[0x15]
0000005F  86CD              xchg cl,ch      ;ch = 柱面数低8位,
00000061  C0E106            shl cl,byte 0x6 ;cl = 柱面数的高2位
00000064  0A0E1100          or cl,[0x11]    ;组合cl
00000068  31C0              xor ax,ax       ;设置ah = 0
0000006A  F8                clc             ;复位carry位
0000006B  EB65              jmp short 0xd2  ;恢复ds,返回

;非0x2~0x4号功能则调用原始int 13h
0000006D  80FC02            cmp ah,0x2
00000070  72CB              jc 0x3d
00000072  80FC04            cmp ah,0x4
00000075  77C6              ja 0x3d
00000077  60                pusha
00000078  80CC40            or ah,0x40          ;将0x2~0x4号功能修改为0x42~0x44号功能
0000007B  50                push ax
0000007C  BE0000            mov si,0x0          ;si指向ds:0000处的DiskAddressPacket
0000007F  C7041000          mov word [si],0x10  ;si.PacketSize = 0x10
00000083  30E4              xor ah,ah
00000085  894402            mov [si+0x2],ax     ;si.BlockCount = 0x00al,int 13h入口数据
00000088  895C04            mov [si+0x4],bx     ;si.BufferAddr = es:bx,int 13h入口数据
0000008B  8C4406            mov [si+0x6],es
0000008E  6631C0            xor eax,eax

;si.BlockNo = 0x00000000_XXXXXXXX,接下来计算X部分
00000091  6689440C          mov [si+0xc],eax    ;ax = 磁头编号 * 扇区数
00000095  88F0              mov al,dh
00000097  F6261100          mul byte [0x11]
0000009B  88CF              mov bh,cl       ;bh = cl = 柱面号高2位和扇区数全6位
0000009D  88EB              mov bl,ch       ;bl = ch = 柱面号低8位
0000009F  C0EF06            shr bh,byte 0x6 ;在bx中保存柱面号全10位
000000A2  81E13F00          and cx,0x3f     ;只保留扇区数全6位

;di = ax = 在目标柱面上的扇区偏移,扇区数从1~63
000000A6  01C8              add ax,cx
000000A8  48                dec ax
000000A9  89C7              mov di,ax

;ax = 磁头数 * 扇区数 * 柱面编号
000000AB  A11300            mov ax,[0x13]
000000AE  F7261100          mul word [0x11]
000000B2  F7E3              mul bx

;dx:ax中位CHS转换后的LBA
000000B4  01F8              add ax,di
000000B6  81D20000          adc dx,0x0
000000BA  894408            mov [si+0x8],ax
000000BD  89540A            mov [si+0xa],dx

;恢复0x0000007B处保存的ax,ah为新的功能号,al为读取扇区数目
000000C0  58                pop ax
000000C1  30C0              xor al,al
000000C3  8A161000          mov dl,[0x10]
000000C7  E80C00            call 0xd6       ;调用原始int 13h

;恢复0x00000077处的pusha,但是保持ah的值
000000CA  88260300          mov [0x3],ah
000000CE  61                popa
000000CF  A10200            mov ax,[0x2]
000000D2  1F                pop ds
000000D3  CA0200            retf 0x2        ;hook代码的出口,弹出EFLAGS

;调用0x0000003F处的程序,即原始int 13h的入口地址
000000D6  9C                pushf           ;int 13h是中断返回,会弹出EFLAGS
000000D7  FF1E2200          call far [0x22]
000000DB  C3                ret

;在MBR执行之前,dl中会保存驱动器号,这好像是一个默认的约定
;BIOS也是用int 13h加载的MBR,或许从那会就保留下来了
;if(dl > 0x8f);
;else [0x062d] = dl
000000DC  80FA8F            cmp dl,0x8f
000000DF  7F04              jg 0xe5
000000E1  88162D06          mov [0x62d],dl

;显示'Start boot from USB device...' 0x0d 0x0a
000000E5  BE8707            mov si,0x787
000000E8  E88D00            call 0x178

;扫描主分区表,查找启动分区
;si = 0x7be, ax = 0, cx = 4
;do{
;   if([si] == 0x80){
;       ax++;
;       bp = si;
;   }
;   si += 0x10;
;   cx--;
;}while(cx != 0);
;if(--ax != 0){
;   int 18h;
;}
000000EB  BEBE07            mov si,0x7be            ;主分区表在偏移0x1be处
000000EE  31C0              xor ax,ax
000000F0  B90400            mov cx,0x4              ;主分区表个数
000000F3  F60480            test byte [si],0x80
000000F6  7403              jz 0xfb
000000F8  40                inc ax
000000F9  89F5              mov bp,si
000000FB  81C61000          add si,0x10
000000FF  E2F2              loop 0xf3
00000101  48                dec ax
00000102  7402              jz 0x106
00000104  CD18              int 0x18

;读取可启动分区的第1扇区,即FAT32的DBR
;di = 5;
;while(1){
;   si = &DiskAddressPacket;
;   si.BlockCount = 1;
;   si.BlockNo = bp.LBAStart;
;   ax = 0x42;
;   dl = [0x062d];
;   int 13h;
;   if(carry){
;       if(di-- == 0) goto 0x0770;
;       ah = 0;
;       dl = [0x062d];
;       int 13h;
;   }
;   else goto 0x0731
;}
00000106  BF0500            mov di,0x5              ;重试次数
00000109  BE1D06            mov si,0x61d            ;DiskAddressPacket地址
0000010C  C744020100        mov word [si+0x2],0x1
00000111  668B4608          mov eax,[bp+0x8]
00000115  66894408          mov [si+0x8],eax
00000119  B80042            mov ax,0x4200
0000011C  8A162D06          mov dl,[0x62d]
00000120  CD13              int 0x13
00000122  730D              jnc 0x131
00000124  4F                dec di
00000125  7449              jz 0x170
00000127  30E4              xor ah,ah
00000129  8A162D06          mov dl,[0x62d]
0000012D  CD13              int 0x13
0000012F  EBD8              jmp short 0x109

;校验0x55aa
00000131  A1FE7D            mov ax,[0x7dfe]
00000134  3D55AA            cmp ax,0xaa55
00000137  7537              jnz 0x170

;安装int 13h的hook代码
00000139  FA                cli
0000013A  66A14C00          mov eax,[0x4c]
0000013E  66A33F06          mov [0x63f],eax     ;将int 13h入口存储在0x0000003E处的jmp指令处

;0x0413属于BIOS Data Area,Bochs模拟值为0x027F(639)
;表示0xA0000以下1kb内存的数量(从0开始计算)
;也是Extended BIOS Data Area的起始地址
;EBDA在0x80000~0xA0000,最长128kb
;0x040E保存EBDA的段基址,Bochs模拟值为0x9FC0
;639kb = 0x9FC00
00000142  BE1304            mov si,0x413
00000145  8B04              mov ax,[si]
00000147  48                dec ax
00000148  8904              mov [si],ax

;左移10位转换为字节,右移4位转换为段地址,最终得到0x9f80
;0x60字的hook代码和数据0000:061d ==> 0x9f80:0000
0000014A  C1E006            shl ax,byte 0x6
0000014D  8EC0              mov es,ax
0000014F  31FF              xor di,di
00000151  BE1D06            mov si,0x61d
00000154  B96000            mov cx,0x60
00000157  FC                cld
00000158  F3A5              rep movsw

;修改int 13h的入口地址,偏移17为0x00000034处的push cs指令
0000015A  C7064C001700      mov word [0x4c],0x17
00000160  A34E00            mov [0x4e],ax
00000163  FB                sti

;将dl设置为驱动器号,跳转到DBR
00000164  8A162D06          mov dl,[0x62d]
00000168  89EE              mov si,bp
0000016A  FA                cli
0000016B  EA007C0000        jmp 0x0:0x7c00

;显示Boot failed
00000170  BEAA07            mov si,0x7aa
00000173  E80200            call 0x178
00000176  EBFE              jmp short 0x176

;显示以空字符结尾的字符串
;一次显示一个字符,si中是字符串地址
;while((ax = [si++]) != 0){
;   ah = 0x0e;
;   bx = 0x07;
;   int 10h;
;}
;return;
00000178  AC                lodsb
00000179  20C0              and al,al
0000017B  7409              jz 0x186
0000017D  B40E              mov ah,0xe
0000017F  BB0700            mov bx,0x7
00000182  CD10              int 0x10
00000184  EBF2              jmp short 0x178
00000186  C3                ret
00000187  skipping 0x79 bytes

;ndisasm.exe -k 3,0x57 -k 0x169,0x97 FAT32DBR

;DBR引导扇区
00000000  EB58              jmp short 0x5a
00000002  90                nop
00000003  skipping 0x57 bytes
0000005A  33C9              xor cx,cx
0000005C  8ED1              mov ss,cx
0000005E  BCF47B            mov sp,0x7bf4   ;栈后12字节的空间留作它用
00000061  8EC1              mov es,cx
00000063  8ED9              mov ds,cx
00000065  BD007C            mov bp,0x7c00

;原始的nop指令清0
00000068  884E02            mov [bp+0x2],cl

;dl设置为DBR中保存的驱动器号0x80
;调用0x41功能检测是否支持int 13h的扩展
0000006B  8A5640            mov dl,[bp+0x40]
0000006E  B441              mov ah,0x41
00000070  BBAA55            mov bx,0x55aa
00000073  CD13              int 0x13

;检测carry、bx和cl
00000075  7210              jc 0x87
00000077  81FB55AA          cmp bx,0xaa55
0000007B  750A              jnz 0x87
0000007D  F6C101            test cl,0x1
00000080  7405              jz 0x87

;在nop指令处记录是否支持int 13h扩展
00000082  FE4602            inc byte [bp+0x2]
00000085  EB2D              jmp short 0xb4

;不支持,调用0x08功能查询驱动器信息
00000087  8A5640            mov dl,[bp+0x40]
0000008A  B408              mov ah,0x8
0000008C  CD13              int 0x13
0000008E  7305              jnc 0x95

;失败则按照柱面编号最大1024、磁头编号最大256、扇区编号最大63
00000090  B9FFFF            mov cx,0xffff
00000093  8AF1              mov dh,cl

;计算总扇区数目,保存在[bp - 0x8] = [0x7bf8]
00000095  660FB6C6          movzx eax,dh
00000099  40                inc ax
0000009A  660FB6D1          movzx edx,cl
0000009E  80E23F            and dl,0x3f
000000A1  F7E2              mul dx              ;eax = 磁头数 * 扇区数
000000A3  86CD              xchg cl,ch
000000A5  C0ED06            shr ch,byte 0x6
000000A8  41                inc cx
000000A9  660FB7C9          movzx ecx,cx        ;ecx = 柱面数
000000AD  66F7E1            mul ecx
000000B0  668946F8          mov [bp-0x8],eax    ;eax = 总扇区数

;一些检查
000000B4  837E1600          cmp word [bp+0x16],byte +0x0    ;FAT12/16共用的字段
000000B8  7538              jnz 0xf2
000000BA  837E2A00          cmp word [bp+0x2a],byte +0x0    ;版本号大于0
000000BE  7732              ja 0xf2

;读取第268扇区
000000C0  668B461C          mov eax,[bp+0x1c]   ;隐藏扇区数目,DBR相对MBR的偏移
000000C4  6683C00C          add eax,byte +0xc   ;12为下一需要读取扇区的偏移
000000C8  BB0080            mov bx,0x8000       ;加载到0000:8000
000000CB  B90100            mov cx,0x1
000000CE  E82B00            call 0xfc           ;读取1个扇区
000000D1  E92C03            jmp 0x400           ;跳转到0000:8000

;显示0x0d 0x0a 'Disk error'
000000D4  A0FA7D            mov al,[0x7dfa]

;while((al = [si++] != 0x00)){
;if(al == 0xff) goto 0x7ced
;   ah = 0xe;
;   bx = 0x7;
;   int 10h;
;}
;goto 0x7cf7
000000D7  B47D              mov ah,0x7d
000000D9  8BF0              mov si,ax
000000DB  AC                lodsb
000000DC  84C0              test al,al
000000DE  7417              jz 0xf7
000000E0  3CFF              cmp al,0xff
000000E2  7409              jz 0xed
000000E4  B40E              mov ah,0xe
000000E6  BB0700            mov bx,0x7
000000E9  CD10              int 0x10
000000EB  EBEE              jmp short 0xdb

;显示0x0d 0x0a 'Press any key to restart' 0x0d 0x0a
000000ED  A0FB7D            mov al,[0x7dfb]
000000F0  EBE5              jmp short 0xd7

;显示0x0d 0x0a 'Remove disks or other media'
000000F2  A0F97D            mov al,[0x7df9]
000000F5  EBE0              jmp short 0xd7

;任意键重启
000000F7  98                cbw
000000F8  CD16              int 0x16
000000FA  CD19              int 0x19

;从eax扇区开始,读取cx个扇区至es:bx
000000FC  6660              pushad
000000FE  807E0200          cmp byte [bp+0x2],0x0
00000102  0F842000          jz near 0x126

;LBA读取方式,在栈上构造DAP
00000106  666A00            o32 push byte +0x0
00000109  6650              push eax
0000010B  06                push es
0000010C  53                push bx
0000010D  666810000100      push dword 0x10010
00000113  B442              mov ah,0x42
00000115  8A5640            mov dl,[bp+0x40]
00000118  8BF4              mov si,sp
0000011A  CD13              int 0x13
0000011C  6658              pop eax
0000011E  6658              pop eax
00000120  6658              pop eax
00000122  6658              pop eax
00000124  EB33              jmp short 0x159

;非LBA(CHS)读取方式
00000126  663B46F8          cmp eax,[bp-0x8]
0000012A  7203              jc 0x12f
0000012C  F9                stc ;大于总扇区数目则设置出错
0000012D  EB2A              jmp short 0x159

;合理的LBA
0000012F  6633D2            xor edx,edx
00000132  660FB74E18        movzx ecx,word [bp+0x18]    ;每磁道扇区数
00000137  66F7F1            div ecx                     ;eax = 磁道号
0000013A  FEC2              inc dl
0000013C  8ACA              mov cl,dl                   ;cl = dl = 扇区号,从1开始
0000013E  668BD0            mov edx,eax
00000141  66C1EA10          shr edx,byte 0x10
00000145  F7761A            div word [bp+0x1a]          ;磁头数
00000148  86D6              xchg dl,dh                  ;dh = 磁头号
0000014A  8A5640            mov dl,[bp+0x40]
0000014D  8AE8              mov ch,al                   ;ch = 柱面号低8位
0000014F  C0E406            shl ah,byte 0x6
00000152  0ACC              or cl,ah                    ;cl组合柱面号和扇区号
00000154  B80102            mov ax,0x201
00000157  CD13              int 0x13

;检查是否读取成功
00000159  6661              popad
0000015B  0F8275FF          jc near 0xd4

;继续读取下一扇区
0000015F  81C30002          add bx,0x200
00000163  6640              inc eax
00000165  49                dec cx
00000166  7594              jnz 0xfc
00000168  C3                ret
00000169  skipping 0x97 bytes

;ndisasm.exe -k 0x81,2 -k 0x147,0xb9 FAT32268

;[bp - 0x4] = [0x7bfc] = 第2簇的起始地址
00000000  660FB64610        movzx eax,byte [bp+0x10]    ;FAT表数目
00000005  668B4E24          mov ecx,[bp+0x24]           ;FAT表所占扇区数目
00000009  66F7E1            mul ecx 
0000000C  6603461C          add eax,[bp+0x1c]   ;隐藏扇区
00000010  660FB7560E        movzx edx,word [bp+0xe] ;保留扇区
00000015  6603C2            add eax,edx
00000018  668946FC          mov [bp-0x4],eax    ;第2簇的起始地址

;[0x7bf4] = 上次读取的FAT表扇区
0000001C  66C746F4FFFFFFFF  mov dword [bp-0xc],0xffffffff

;0xfcf2的代码在DBR扇区偏移0x000000f2处,地址0x7cf2
00000024  668B462C          mov eax,[bp+0x2c]   ;根目录起始簇号
00000028  6683F802          cmp eax,byte +0x2
0000002C  0F82C2FC          jc near 0xfcf2      ;根目录起始簇号 < 2
00000030  663DF8FFFF0F      cmp eax,0xffffff8
00000036  0F83B8FC          jnc near 0xfcf2     ;根目录起始簇号 >= 0x0ffffff8

;根据eax中的簇号读取扇区
0000003A  6650              push eax
0000003C  6683E802          sub eax,byte +0x2   ;转换为从0开始
00000040  660FB65E0D        movzx ebx,byte [bp+0xd]

;si = 每簇扇区数
00000045  8BF3              mov si,bx
00000047  66F7E3            mul ebx
0000004A  660346FC          add eax,[bp-0x4]    ;簇的起始扇区

;0xfcfc的代码在DBR扇区偏移0x000000fc处,地址0x7cfc
;bx = 0x8200
0000004E  BB0082            mov bx,0x8200
00000051  8BFB              mov di,bx           ;di = 目录项地址
00000053  B90100            mov cx,0x1
00000056  E8A3FC            call 0xfcfc         ;读取一个扇区

;扫描刚读取的扇区目录项,查找"BOOTMGR    "
;未找到跳转到0x7b,找到跳转到0x83
00000059  382D              cmp [di],ch     ;第1字节全0表示最后目录项
0000005B  741E              jz 0x7b
0000005D  B10B              mov cl,0xb      ;11字节的短文件名目录项名称长度
0000005F  56                push si         ;si中已经保存每簇扇区数
00000060  BE697D            mov si,0x7d69   ;"BOOTMGR    "
00000063  F3A6              repe cmpsb
00000065  5E                pop si
00000066  741B              jz 0x83         ;找到BOOTMGR
00000068  03F9              add di,cx
0000006A  83C715            add di,byte +0x15

;下一个目录项
0000006D  3BFB              cmp di,bx
0000006F  72E8              jc 0x59

;下一个扇区
00000071  4E                dec si
00000072  75DA              jnz 0x4e

;计算下一簇,再进行查找
00000074  6658              pop eax
00000076  E86500            call 0xde       
00000079  72BF              jc 0x3a

;在根目录中没有找到"BOOTMGR    "
0000007B  83C404            add sp,byte +0x4    ;0000003A栈平衡
0000007E  E971FC            jmp 0xfcf2

00000081  skipping 0x2 bytes                    ;加载BOOTMGR时的es

;找到BOOTMGR
00000083  83C404            add sp,byte +0x4    ;0000003A栈平衡
00000086  8B7509            mov si,[di+0x9]     ;BOOTMGR起始簇号高2字节
00000089  8B7D0F            mov di,[di+0xf]     ;BOOTMGR起始簇号低2字节
0000008C  8BC6              mov ax,si
0000008E  66C1E010          shl eax,byte 0x10
00000092  8BC7              mov ax,di           ;eax = BOOTMGR起始簇号
00000094  6683F802          cmp eax,byte +0x2
00000098  0F8256FC          jc near 0xfcf2      ;BOOTMGR起始簇号 < 2
0000009C  663DF8FFFF0F      cmp eax,0xffffff8
000000A2  0F834CFC          jnc near 0xfcf2     ;BOOTMGR起始簇号 >= 0x0ffffff8

;根据eax中的簇号加载BOOTMGR
000000A6  6650              push eax
000000A8  6683E802          sub eax,byte +0x2
000000AC  660FB64E0D        movzx ecx,byte [bp+0xd]
000000B1  66F7E1            mul ecx
000000B4  660346FC          add eax,[bp-0x4]    ;簇的起始扇区
000000B8  BB0000            mov bx,0x0
000000BB  06                push es
000000BC  8E068180          mov es,[0x8081]     ;es
000000C0  E839FC            call 0xfcfc         ;BOOTMGR会加载到0x20000
000000C3  07                pop es
000000C4  6658              pop eax

;更新下一簇的加载地址
000000C6  C1EB04            shr bx,byte 0x4
000000C9  011E8180          add [0x8081],bx     ;更新段地址
000000CD  E80E00            call 0xde           ;计算BOOTMGR的下一簇
000000D0  0F830200          jnc near 0xd6       ;>= 0x0F FF FF F8则加载完毕
000000D4  72D0              jc 0xa6             ;< 加载下一簇
000000D6  8A5640            mov dl,[bp+0x40]    ;在dl中保存驱动器号,MBR将控制权交给DBR时有相同的操作
000000D9  EA00000020        jmp 0x2000:0x0      ;跳转到BOOTMGR

;根据eax中的簇号,读取FAT表扇区得到下一簇号
000000DE  66C1E002          shl eax,byte 0x2    ;FAT项偏移
000000E2  E81100            call 0xf6           ;根据FAT项偏移读取相应扇区
000000E5  26668B01          mov eax,[es:bx+di]  ;bx中保存FAT项在扇区中的偏移
000000E9  6625FFFFFF0F      and eax,0xfffffff   ;取低28位
000000EF  663DF8FFFF0F      cmp eax,0xffffff8   ;和0x0F FF FF F8进行比较
000000F5  C3                ret

;根据eax中的FAT项偏移,读取对应的扇区
;如果新的FAT项所在扇区已经在内存中,则直接读取
;否者需要读取新的扇区
000000F6  BF007E            mov di,0x7e00
000000F9  660FB74E0B        movzx ecx,word [bp+0xb] ;每扇区字节数
000000FE  6633D2            xor edx,edx
00000101  66F7F1            div ecx                 ;eax = FAT项所在扇区, edx = 扇区中偏移
00000104  663B46F4          cmp eax,[bp-0xc]
00000108  743A              jz 0x144                ;新的FAT项所在扇区已经在内存中

;读取新扇区
0000010A  668946F4          mov [bp-0xc],eax        ;已经读取的FAT表扇区的LBA
0000010E  6603461C          add eax,[bp+0x1c]       ;隐藏扇区数
00000112  660FB74E0E        movzx ecx,word [bp+0xe] ;ecx = 保留扇区数
00000117  6603C1            add eax,ecx             ;第1个FAT表中该FAT项的LBA

;根据FAT表工作方式读取正确的FAT表
0000011A  660FB75E28        movzx ebx,word [bp+0x28]    ;FAT表工作方式
0000011F  83E30F            and bx,byte +0xf
00000122  7416              jz 0x13a            ;互为镜像

;只有一个活动FAT表
00000124  3A5E10            cmp bl,[bp+0x10]
00000127  0F83C7FB          jnc near 0xfcf2     ;活动FAT表编号 >= FAT表数目
0000012B  52                push dx
0000012C  668BC8            mov ecx,eax         ;ecx = 第1个FAT表中该FAT项的LBA
0000012F  668B4624          mov eax,[bp+0x24]   ;FAT表所占扇区大小
00000133  66F7E3            mul ebx             ;跳过不活动的FAT表
00000136  6603C1            add eax,ecx
00000139  5A                pop dx

;FAT表互为镜像
0000013A  52                push dx
0000013B  8BDF              mov bx,di       ;将FAT项所在扇区读取到0x7e00
0000013D  B90100            mov cx,0x1
00000140  E8B9FB            call 0xfcfc     ;读取一个扇区
00000143  5A                pop dx          ;该FAT项在该扇区中的偏移

;直接读取
00000144  8BDA              mov bx,dx
00000146  C3                ret
00000147  skipping 0xB9 bytes

你可能感兴趣的:(操作系统)