关于MBR的调试,论坛里也有好几篇帖子了,这个帖子也只是把我的学习过程分享出来,如果可以,我会从MBR开始,一步一步的去调试,去学习Windows操作系统,同时也把这个过程分享出来。闲话不多说了,开始进入主题。
相关帖子链接:http://www.52pojie.cn/thread-173889-1-1.html
一、MBR调试
目前用到的工具:
虚拟机:VMware
系统:ReactOS.0.3.15(安装win7 32位或者XP系统也可以用这个方法调试,这里选择ReactOS是为了后面的调试做准备)
调试工具:IDA 首先设置虚拟机。在VMware创建的系统文件夹下找到.vmx文件,用记事本打开,添加如下配置信息
[Asm] 纯文本查看 复制代码debugStub.listen.guest32 = "TRUE"debugStub.hideBreakpoints = "TRUE"bios.bootDelay = "3000"
然后设置IDA。打开IDA,Debugger-->Attach-->Remote GDB debugger,在弹出的对话框进行如下设置
启动虚拟机后,再点击IDA弹出的对话框的"OK"按钮,选择第一个进程,点"OK",如下图
进入调试窗口后,转到0x7c00处,按"F2"下断点,然后按"F9"运行程序,当程序断下来之后,按"Alt+s",选择16位编码,这时反汇编可能会变成一堆数据,我们只要将其选中(446个字节),然后按“C”,选择“Force”,将数据强行转换成代码即可。
接下来就开始我们的调试了,先看下面的代码
[Asm] 纯文本查看 复制代码MEMORY:7C00 loc_7C00: ; CODE XREF: MEMORY:loc_7CAEJMEMORY:7C00 cliMEMORY:7C01 cldMEMORY:7C02 xor ax, axMEMORY:7C04 mov ss, axMEMORY:7C06 mov ds, axMEMORY:7C08 mov bp, 7C00hMEMORY:7C0B lea sp, [bp-20h]MEMORY:7C0E stiMEMORY:7C0F mov ax, 1FE0hMEMORY:7C12 mov es, axMEMORY:7C14 assume es:nothingMEMORY:7C14 mov si, bpMEMORY:7C16 mov di, bpMEMORY:7C18 mov cx, 100hMEMORY:7C1B rep movsw ; 把MBR移动到1FE0h:7C00h处MEMORY:7C1D jmp far ptr 1FE0h:7C22h
这段代码很简单,就是把MBR拷贝到1FE0h:7C00h处,然后跳转到1FE0h:7C22h继续执行。接下来是在分区表中查找活动分区,看代码
[Asm] 纯文本查看 复制代码MEMORY:27A22 loc_27A22: ; CODE XREF: MEMORY:7C1DJMEMORY:27A22 mov ds, axMEMORY:27A24 assume ds:MEMORYMEMORY:27A24 mov ss, axMEMORY:27A26 assume ss:MEMORYMEMORY:27A26 xor ax, axMEMORY:27A28 mov es, axMEMORY:27A2A lea di, [bp+1BEh] ; 获取分区表地址MEMORY:27A2E test byte ptr [di], 80h ; 是否是活动分区MEMORY:27A31 jnz short loc_7AA0MEMORY:27A33 add di, 10h ; 每个分区表项大小为16个字节MEMORY:27A36 cmp di, 7DFEh ; 分区表搜索是否结束MEMORY:27A3A jb short loc_7A2E
这里需要对MBR有所了解才行。MBR是磁盘的第一个扇区,占512个字节(每个扇区都是512个字节)。前446个字节是引导代码,接下来的64个字节是分区表,共四个表项,最后两个字节是结束标志0xAA55。分区表每个表项16个字节,第一个字节是分区标记,表示这个分区是不是活动分区(0x80为活动分区,否则为0)。上面这段代码就是检测每个分区表项的第一个字节是不是0x80,如果是就跳转。我们接下来看跳转过去的代码
[Asm] 纯文本查看 复制代码EMORY:27AA0 call loc_7AB3MEMORY:27AA3 jb short loc_7A5BMEMORY:27AA5 cmp es:word_7DFE, 0AA55hMEMORY:27AAC jnz short loc_7A7FMEMORY:27AAE jmp far ptr loc_7C00
跳转过来就是一个call,我们跟进去看看。
[Asm] 纯文本查看 复制代码27AB3 mov bx, 55AAhMEMORY:27AB6 mov ah, 41h ; 'A'MEMORY:27AB8 int 13h ; DISK - Check for INT 13h ExtensionsMEMORY:27AB8 ; BX = 55AAh, DL = drive numberMEMORY:27AB8 ; Return: CF set if not supportedMEMORY:27AB8 ; AH = extensions versionMEMORY:27AB8 ; BX = AA55hMEMORY:27AB8 ; CX = Interface support bit mapMEMORY:27ABA jb short loc_7AEEMEMORY:27ABC cmp bx, 0AA55hMEMORY:27AC0 jnz short loc_7AEEMEMORY:27AC2 test cl, 1MEMORY:27AC5 jz short loc_7AEEMEMORY:27AC7 jmp short loc_7AD9
这里其实是检测int 13h的拓展功能可不可以用,如果可用,则跳转到1FE0h:7AD9h处,否则跳转到1FE0h:7AEEh处。我们先来看1FE0h:7AEEh处的代码
[Asm] 纯文本查看 复制代码MEMORY:27AEE mov ax, 204hMEMORY:27AF1 mov bx, 7C00hMEMORY:27AF4 mov cx, [di+2] ;di为分区表首地址MEMORY:27AF7 mov dh, [di+1]MEMORY:27AFA int 13h ; DISK - READ SECTORS INTO MEMORYMEMORY:27AFA ; AL = number of sectors to read, CH = track, CL = sectorMEMORY:27AFA ; DH = head, DL = drive, ES:BX -> buffer to fillMEMORY:27AFA ; Return: CF set on error, AH = status, AL = number of sectors readMEMORY:27AFC retn
这里其实是使用了int 13h来读取扇区数据(不是拓展功能),先来看看介绍:
int 13h,ah=02h 读扇区说明:
调用此功能将从磁盘上把一个或更多的扇区内容读进存贮器。因为这是一个
低级功能,在一个操作中读取的全部扇区必须在同一条磁道上(磁头号和磁道号
相同)。BIOS不能自动地从一条磁道末尾切换到另一条磁道开始,因此用户必须
把跨多条磁道的读操作分为若干条单磁道读操作。
入口参数:
AH=02H 指明调用读扇区功能。
AL 置要读的扇区数目,不允许使用读磁道末端以外的数值,也不允许
使该寄存器为0。
DL 需要进行读操作的驱动器号。
DH 所读磁盘的磁头号。
CH 磁道号的低8位数。
CL 低5位放入所读起始扇区号,位7-6表示磁道号的高2位。
ES:BX 读出数据的缓冲区地址。
返回参数:
如果CF=1,AX中存放出错状态。读出后的数据在ES:BX区域依次排列。
详情请参见磁盘错误状态返回码一文。
由此可知,上面的代码是读取4个扇区的数据到1FE0h:7C00h处,读取的偏移由分区表的起始CHS确定。接下来我们来看1FE0h:7AD9h处的代码
[Asm] 纯文本查看 复制代码MEMORY:27AC9 db 10hMEMORY:27ACA db 0MEMORY:27ACB db 4MEMORY:27ACC db 0MEMORY:27ACD db 0MEMORY:27ACE db 7Ch ; |MEMORY:27ACF db 0MEMORY:27AD0 db 0MEMORY:27AD1 word_27AD1 dw 0 ; DATA XREF: MEMORY:7CDCwMEMORY:27AD3 word_27AD3 dw 0 ; DATA XREF: MEMORY:loc_7CE2wMEMORY:27AD5 db 0MEMORY:27AD6 db 0MEMORY:27AD7 db 0MEMORY:27AD8 db 0MEMORY:27AD9 ; ---------------------------------------------------------------------------MEMORY:27AD9 mov ax, [di+8]MEMORY:27ADC mov word_7CD1, axMEMORY:27ADF mov ax, [di+0Ah]MEMORY:27AE2 mov word_7CD3, axMEMORY:27AE5 mov ax, 4200hMEMORY:27AE8 mov si, 7CC9hMEMORY:27AEB int 13h ; DISK - IBM/MS Extension - EXTENDED READ (DL - drive, DS:SI - disk address packet)MEMORY:27AED retn
这里其实也是读取扇区数据,不过使用的是int 13h的拓展功能来读取。我们先来了解下int 13h的拓展功能怎么读取数据
入口:
AH = 42h
DL = 驱动器号
DS:SI = 磁盘地址数据包(Disk Address Packet)
返回:
CF = 0,AH = 0 成功
CF = 1,AH = 错误码
这里涉及到一个结构体,我们先看这个结构体:
struct DAP{
BYTE PacketSize; //数据包尺寸,固定为0x10
BYTE Reserved; //保留
WORD BlockCount; //要传输的扇区数
WORD BufferOffset; //传输缓冲区偏移地址
WORD BufferSegment; //传输缓冲区段地址
DWORD LBNLow; //要读取数据的起始扇区号低位
DWORD LBNHigh; //要读取数据的起始扇区号高位
};
这个结构体就是上面MEMORY:27AC9开始的数据,在读取的时候,把这个结构体的地址传给si,然后调用int 13h来读取。上面这两段代码其实读取的是活动分区的引导扇区数据(每个分区的第一个扇区都分区引导扇区,这里要和MBR区分开来)。读完之后,程序反回继续前面的call下面的代码
[Asm] 纯文本查看 复制代码MEMORY:27AA3 jb short loc_7A5BMEMORY:27AA5 cmp es:word_7DFE, 0AA55hMEMORY:27AAC jnz short loc_7A7FMEMORY:27AAE jmp far ptr loc_7C00
这里其实是检测分区引导扇区是否正确读取,如果正确读取则跳转去执行分区引导扇区的代码(这里是jmp far ptr loc_7C00)。
二、PBR调试
我们先来认识下PBR
下面我们再来看下BPB和EBPB的相关介绍BPB结构:
0x0b~0x0c: 每扇区字节数
0x0d: 每簇扇区数3 k9 I7 E0 p" x2 E+ A" V
0x0e~0x0f: 保留扇区数
0x10: fat表数$ u5 |& A- X9 G# \
0x11~0x12: 根目录项数(只有FAT12/FAT16使用此字段,FAT32此字段为0) L+ v: W( R o; d3 g5 R9 u8 F
0x13~0x14: 小扇区数(只有FAT12/FAT16使用此字段,FAT32此字段为0)
0x15: 媒体描述符(0xf8表示硬盘,0xf0表示高密度3.5寸软盘)
0x16~0x17: 每fat扇区数(只有FAT12/FAT16使用此字段,FAT32此字段为0)
0x18~0x19: 每磁道扇区数
0x1a~0x1b: 磁头数
0x1c~0x1f: 隐藏扇区数) n. U' v+ x( l8 y8 m* s
0x20~0x23: 总扇区数
0x24~0x27: 每fat扇区数(只被fat32使用)) }" o) I# B0 k% ^# @( _. S
0x28~0x29: fat表镜像标志,值为0表示系统保存2份互为备份的fat表,值为1表示系统仅保存1份fat表
0x2a~0x2b: 文件系统版本(只供fat32使用)8 C6 A1 j% [4 C* e3 w: K
0x2c~0x2f: 根目录簇号(只供fat32使用)
0x30~0x31: 文件系统信息扇区号(只供fat32使用)$ p0 {7 [1 d# J! W7 G6 L8 Z* ~9 g
0x32~0x33: 备份引导扇区(只供fat32使用)
0x34~0x3f: 保留+ U; h: s) w6 \, n8 ^$ E' p
7 T# K, n3 K4 _' C' J
EBPB结构:
0x40: 物理驱动器号
0x41: 保留
0x42: 拓展引导标签
0x43~0x46: 分区序号
0x47~0x51: 卷标% g" e+ S% v" g# _! l) k
0x52~0x59: 系统ID% Q, z6 e! D4 x
接下来,我们开始调试PBR了。在上一个帖子里,我们调玩MBR之后,程序跳转到PBR去执行,而从上面PBR的介绍可知,在开始处是一个跳转指令
[Asm] 纯文本查看 复制代码MEMORY:7C00 jmp short near ptr unk_7C5A
我们直接看7C5A处的代码
[Asm] 纯文本查看 复制代码MEMORY:7C5A xor ax, axMEMORY:7C5C mov ds, axMEMORY:7C5E assume ds:MEMORYMEMORY:7C5E mov es, axMEMORY:7C60 assume es:MEMORYMEMORY:7C60 mov ss, axMEMORY:7C62 mov bp, 7C00hMEMORY:7C65 mov sp, 7C00hMEMORY:7C68 cmp byte ptr [bp+40h], 0FFh ; 检测磁盘物理驱动号是否正确MEMORY:7C6D jnz short loc_7C73 ; 判断分区文件系统是fat12/fat16还是fat32MEMORY:7C6F mov [bp+40h], dlMEMORY:7C73MEMORY:7C73 loc_7C73: ; CODE XREF: MEMORY:7C6DjMEMORY:7C73 cmp word ptr [bp+16h], 0 ; 判断分区文件系统是fat12/fat16还是fat32MEMORY:7C78 jnz short loc_7C89 ; 如果是fat12/fat16则跳转MEMORY:7C7A cmp dword ptr [bp+11h], 0 ; 判断分区文件系统是fat12/fat16还是fat32MEMORY:7C80 jnz short loc_7C89 ; 如果是fat12/fat16则跳转MEMORY:7C82 cmp word ptr [bp+2Ah], 0MEMORY:7C87 jbe short loc_7C8C ; 如果是fat32则跳转MEMORY:7C89MEMORY:7C89 loc_7C89: ; CODE XREF: MEMORY:7C78jMEMORY:7C89 ; MEMORY:7C80jMEMORY:7C89 jmp loc_7D93
这里先检测分区文件系统是fat12/fat16还是fat32,如果是fat12/fat16,则输出一些信息,我们就不看了,只看fat32的情况。
[Asm] 纯文本查看 复制代码MEMORY:7C8C loc_7C8C: ; CODE XREF: MEMORY:7C87jMEMORY:7C8C mov ax, 800hMEMORY:7C8F mov dl, [bp+40h]MEMORY:7C93 int 13h ; DISK - DISK - GET CURRENT DRIVE PARAMETERS (XT,AT,XT286,CONV,PS)MEMORY:7C93 ; DL = drive numberMEMORY:7C93 ; Return: CF set on error, AH = status code, BL = drive typeMEMORY:7C93 ; DL = number of consecutive drivesMEMORY:7C93 ; DH = maximum value for head number, ES:DI -> drive parameterMEMORY:7C95 jnb short loc_7C9CMEMORY:7C97 mov cx, 0FFFFhMEMORY:7C9A mov dh, cl
在这里,用int 13h,ah=08h来读取磁盘参数,我们看看这种读取方式的介绍
从上面的介绍可知,如果读取失败,则cf标志位置1,那么jnb short loc_7C9C就不会执行。反之,cf标志位为0,jnb short loc_7C9C会被执行。我们接下来看0x7c9c处的代码
[Asm] 纯文本查看 复制代码MEMORY:7C9C loc_7C9C: ; CODE XREF: MEMORY:7C95jMEMORY:7C9C mov bl, ch ; ch存放的是柱面数的低8位(柱面数总共有10位,高2位来自于cl的高2位)MEMORY:7C9E mov bh, cl ; cl的低6位存放的是没磁道扇区数(高2位和ch组合来表示柱面数)MEMORY:7CA0 shr bh, 6 ; 这里把bh的值右移6位,那么bh中只剩下cl的高2位,然后和bl的8位一起表示柱面数,所以bx的值就是柱面数MEMORY:7CA3 and cl, 3Fh ; 这里取的是cl的低6位的值MEMORY:7CA6 movzx eax, dh ; dh存放的是磁头数MEMORY:7CAA movzx ebx, bxMEMORY:7CAE movzx ecx, clMEMORY:7CB2 inc eax ; 因为磁头数是从0开始计数,所以这里要+1MEMORY:7CB4 inc ebx ; 柱面数也是从0开始计数,这里也要+1MEMORY:7CB6 mul ecx ; 这里是磁头数*每磁道扇区数MEMORY:7CB9 mul ebx ; 上面得到的结果*柱面数,得到的值其实就是这个分区的大小MEMORY:7CBC mov dword_7DB4, eaxMEMORY:7CC0 mov eax, 0EhMEMORY:7CC6 add eax, [bp+1Ch] ; 计算第二个引导扇区的绝对偏移MEMORY:7CCB mov cx, 1MEMORY:7CCE xor bx, bxMEMORY:7CD0 mov es, bxMEMORY:7CD2 mov bx, 7E00hMEMORY:7CD5 call loc_7CDBMEMORY:7CD8 jmp loc_7E00
这里先读取磁盘参数,然后通过计算得出磁盘大小,然后计算第二引导扇区的偏移,接下来有个call,我们看这个call里面的代码
[Asm] 纯文本查看 复制代码MEMORY:7CDB loc_7CDB: ; CODE XREF: MEMORY:7CD5pMEMORY:7CDB push esMEMORY:7CDC cmp eax, dword_7DB4MEMORY:7CE1 jnb short loc_7CFFMEMORY:7CE3 pushadMEMORY:7CE5 mov ah, 41h ; 'A'MEMORY:7CE7 mov bx, 55AAhMEMORY:7CEA mov dl, [bp+40h]MEMORY:7CEE int 13h ; DISK - Check for INT 13h ExtensionsMEMORY:7CEE ; BX = 55AAh, DL = drive numberMEMORY:7CEE ; Return: CF set if not supportedMEMORY:7CEE ; AH = extensions versionMEMORY:7CEE ; BX = AA55hMEMORY:7CEE ; CX = Interface support bit map
这段代码在上一个帖子有讲过,就是检测int 13h的拓展功能是否可用,如果可用,接下来就会用int 13h的拓展功能来读取扇区。代码如下
[Asm] 纯文本查看 复制代码MEMORY:7CF0 jb short loc_7D49 ; 拓展功能不可用则跳转MEMORY:7CF2 cmp bx, 0AA55hMEMORY:7CF6 jnz short loc_7D49MEMORY:7CF8 test cl, 1MEMORY:7CFB jz short loc_7D49MEMORY:7CFD popadMEMORY:7CFFMEMORY:7CFF loc_7CFF: ; CODE XREF: MEMORY:7CE1jMEMORY:7CFF ; MEMORY:7D41jMEMORY:7CFF pushadMEMORY:7D01 cmp cx, 40h ; '@'MEMORY:7D04 jbe short loc_7D09MEMORY:7D06 mov cx, 40h ; '@'MEMORY:7D09MEMORY:7D09 loc_7D09: ; CODE XREF: MEMORY:7D04jMEMORY:7D09 mov word ptr dword_7D45, cxMEMORY:7D0D push 0MEMORY:7D0F push 0MEMORY:7D11 push eaxMEMORY:7D13 push esMEMORY:7D14 push bxMEMORY:7D15 push cxMEMORY:7D16 push 10hMEMORY:7D18 mov si, spMEMORY:7D1A mov dl, [bp+40h]MEMORY:7D1E mov ah, 42h ; 'B'MEMORY:7D20 int 13h ; DISK - IBM/MS Extension - EXTENDED READ (DL - drive, DS:SI - disk address packet)MEMORY:7D22 jb short loc_7D8BMEMORY:7D24 add sp, 10hMEMORY:7D27 popadMEMORY:7D29 push bxMEMORY:7D2A mov ebx, dword_7D45MEMORY:7D2F add eax, ebxMEMORY:7D32 shl ebx, 5MEMORY:7D36 mov dx, esMEMORY:7D38 add dx, bxMEMORY:7D3A mov es, dxMEMORY:7D3C pop bxMEMORY:7D3D sub cx, word ptr dword_7D45MEMORY:7D41 jnz short loc_7CFFMEMORY:7D43 pop esMEMORY:7D44 retn
这里读取的是第二启动扇区代码,存放的位置为0x7e00,然后进行一些读取结果的检测,如果读取正确,则函数返回。返回之后,程序跳转到0x7e00处去执行第二启动扇区的代码。
在第二启动引导代码中,先获取根目录簇号,然后检测这个值是否正确,代码如下
[Asm] 纯文本查看 复制代码MEMORY:7E00 ; MEMORY:7E5Bj ...MEMORY:7E00 mov eax, [bp+2Ch] ; 获取根目录簇号MEMORY:7E05 cmp eax, 0FFFFFF8hMEMORY:7E0B jb short loc_7E10
如果正确则跳转到0x7E10处,下面我们来看0x7E10处的代码
[Asm] 纯文本查看 复制代码MEMORY:7E10 loc_7E10: ; CODE XREF: MEMORY:7E0BjMEMORY:7E10 mov bx, 2000hMEMORY:7E13 mov es, bxMEMORY:7E15 assume es:nothingMEMORY:7E15 call loc_7F3E
这里设置了es的值,然后就调用一个call,我们进去看看这个call 的代码
[Asm] 纯文本查看 复制代码MEMORY:7F3E dec eaxMEMORY:7F40 dec eaxMEMORY:7F42 xor edx, edxMEMORY:7F45 movzx ebx, byte ptr [bp+0Dh] ; 获取每簇扇区数MEMORY:7F4B mul ebxMEMORY:7F4E push eaxMEMORY:7F50 xor edx, edxMEMORY:7F53 movzx eax, byte ptr [bp+10h] ; 获取fat表数MEMORY:7F59 mul dword ptr [bp+24h] ; 用fat表数*每fat扇区数MEMORY:7F5E movzx ebx, word ptr [bp+0Eh] ; 获取保留扇区数MEMORY:7F64 add eax, ebxMEMORY:7F67 add eax, [bp+1Ch] ; 加上隐藏扇区数MEMORY:7F6C pop ebxMEMORY:7F6E add eax, ebxMEMORY:7F71 xor bx, bxMEMORY:7F73 movzx cx, byte ptr [bp+0Dh]MEMORY:7F78 call sub_7CDBMEMORY:7F7B retn
在这里面进行了一些计算,其实是计算fat32文件系统中数据区的偏移,然后就调用了call sub_7CDB,这个call在前面有看到过,是一个读取扇区的call,这里其实是读取fat32文件系统的数据区数据,读取大小为一个簇,读取后存放位置为2000h:0000h。读取完之后,函数返回。接下来我们来看call loc_7F3E之后的代码
[Asm] 纯文本查看 复制代码MEMORY:7E18 xor bx, bxMEMORY:7E1A mov bl, [bp+0Dh] ; 获取每簇扇区数MEMORY:7E1E shl bx, 4 ;这里计算有多少个短目录项(每个短目录项占32个字节)MEMORY:7E21 mov ax, 2000hMEMORY:7E24 mov es, axMEMORY:7E26 xor di, diMEMORY:7E28 mov si, 7FA3hMEMORY:7E2B mov cx, 0BhMEMORY:7E2E repe cmpsbMEMORY:7E30 jz short loc_7E5DMEMORY:7E32MEMORY:7E32 loc_7E32: ; CODE XREF: MEMORY:7DBEjMEMORY:7E32 dec bxMEMORY:7E33MEMORY:7E33 loc_7E33: ; CODE XREF: MEMORY:7DCDjMEMORY:7E33 jnz short loc_7E38 通过上面的代码可以看出是在循环查找那个文件名,接下来我们看找到的情况
[Asm] 纯文本查看 复制代码MEMORY:7E5D loc_7E5D: ; CODE XREF: MEMORY:7E30jMEMORY:7E5D ; MEMORY:7E49jMEMORY:7E5D mov si, 7FAEhMEMORY:7E60MEMORY:7E60 loc_7E60: ; CODE XREF: MEMORY:7DECjMEMORY:7E60 call sub_7DA5
查找到了,si指向0x7FAE,这里有一个call,我们进去看看
[Asm] 纯文本查看 复制代码MEMORY:7DA5 sub_7DA5 proc near ; CODE XREF: sub_7CDB+B3pMEMORY:7DA5 ; sub_7CDB+BBp ...MEMORY:7DA5 lodsb ;从si中读取字符,存放到al中MEMORY:7DA6 or al, alMEMORY:7DA8 jz short locret_7DB3MEMORY:7DAA mov ah, 0EhMEMORY:7DAC mov bx, 7MEMORY:7DAF int 10h ; - VIDEO - WRITE CHARACTER AND ADVANCE CURSOR (TTY WRITE)MEMORY:7DAF ; AL = character, BH = display page (alpha modes)MEMORY:7DAF ; BL = foreground color (graphics modes)MEMORY:7DB1 jmp short sub_7DA5MEMORY:7DB3 ; ---------------------------------------------------------------------------MEMORY:7DB3MEMORY:7DB3 locret_7DB3: ; CODE XREF: sub_7DA5+3jMEMORY:7DB3 retn
这个函数的功能其实就是把si指向的地址中存放的字符串显示到屏幕上。就是这个效果
接下来就是对数据区数据的解析,先来看目录项的介绍(短文件名目录项)
然后我们再来看接下来的代码
[Asm] 纯文本查看 复制代码MEMORY:7E65 loc_7E65: ; CODE XREF: MEMORY:7DEFjMEMORY:7E65 xor dx, dxMEMORY:7E67 mov ax, es:[di+14h] ;取出文件起始簇号的高两个字节MEMORY:7E6B shl eax, 10h MEMORY:7E6F mov ax, es:[di+1Ah] ;取出文件起始簇号的低两个字节MEMORY:7E73 cmp eax, 2MEMORY:7E77 jnb short loc_7E7CMEMORY:7E79 jmp loc_7D93
从上面的代码可以看出,这里是取出文件起始簇号的高两个字节和低两个字节,并将他们组成文件的起始簇号,然后用文件起始簇号来和2做比较,为啥要和2做比较,是因为所有的簇都是从2开始编号的。在这里,eax的值大于2,所以执行 jnb short loc_7E7C指令,我们接下来看loc_7E7C处的代码
[Asm] 纯文本查看 复制代码MEMORY:7E7C loc_7E7C: ; CODE XREF: MEMORY:7E77jMEMORY:7E7C cmp eax, 0FFFFFF8hMEMORY:7E82 jb short loc_7E87MEMORY:7E84 jmp loc_7D93
这里只判断文件所谓的簇号是否大于最大值,这里是小于,所以 执行jb short loc_7E87,我们接下来看loc_7E87处的代码
[Asm] 纯文本查看 复制代码MEMORY:7E87 loc_7E87: ; CODE XREF: MEMORY:7E82jMEMORY:7E87 mov bx, 0F80hMEMORY:7E8A mov es, bxMEMORY:7E8C assume es:nothingMEMORY:7E8CMEMORY:7E8C loc_7E8C: ; CODE XREF: MEMORY:7EB3jMEMORY:7E8C cmp eax, 0FFFFFF8hMEMORY:7E92 jnb short loc_7EB5MEMORY:7E94 push eaxMEMORY:7E96 xor bx, bxMEMORY:7E98 push esMEMORY:7E99 call near ptr unk_7F3E
这里修改了下es的值,然后调用一个call,我们进去看这个call干了些啥
[Asm] 纯文本查看 复制代码MEMORY:7F3E dec eaxMEMORY:7F40 dec eax ; 这里是该簇的簇号-2MEMORY:7F42 xor edx, edxMEMORY:7F45 movzx ebx, byte ptr [bp+0Dh] ; 获取每簇扇区数MEMORY:7F4B mul ebxMEMORY:7F4E push eaxMEMORY:7F50 xor edx, edxMEMORY:7F53 movzx eax, byte ptr [bp+10h] ; 获取fat表个数MEMORY:7F59 mul dword ptr [bp+24h] ; fat表个数*每fat扇区数MEMORY:7F5E movzx ebx, word ptr [bp+0Eh] ; 获取保留扇区数MEMORY:7F64 add eax, ebxMEMORY:7F67 add eax, [bp+1Ch] ; 加上隐藏扇区数MEMORY:7F6C pop ebxMEMORY:7F6E add eax, ebxMEMORY:7F71 xor bx, bxMEMORY:7F73 movzx cx, byte ptr [bp+0Dh]MEMORY:7F78 call sub_7CDBMEMORY:7F7B retn
这段代码看着挺熟悉,上面的一大堆计算,其实可以总结成以下公式(这里计算的是绝对扇区号)
某簇起始扇区号 = 隐藏扇区数 + 保留扇区数 + 每个FAT表大小扇区数 × FAT表个数 + (该簇簇号 - 2) × 每簇扇区数
然后读取该簇的数据到0F80h:0000h处(call sub_7CDB)。我们接着看call near ptr unk_7F3E之后的代码
[Asm] 纯文本查看 复制代码MEMORY:7E9C pop esMEMORY:7E9D assume es:MEMORYMEMORY:7E9D xor bx, bxMEMORY:7E9F mov bl, [bp+0Dh]MEMORY:7EA3 shl bx, 5MEMORY:7EA6 mov ax, esMEMORY:7EA8 add ax, bx ; 这里其实是把es的值设置到刚才读取的那一簇数据之后MEMORY:7EAA mov es, axMEMORY:7EAC pop eaxMEMORY:7EAE push esMEMORY:7EAF call loc_7EC2MEMORY:7EB2 pop esMEMORY:7EB3 jmp short loc_7E8C
上面重新设置了下es的值,其实是让它指向刚才读取的那一簇数据之后,然后又是一个call,我们看call loc_7EC2内部的代码
[Asm] 纯文本查看 复制代码MEMORY:7EC2 loc_7EC2: ; CODE XREF: MEMORY:7E53pMEMORY:7EC2 ; MEMORY:7EAFpMEMORY:7EC2 shl eax, 2 ; 在fat中,每个fat表项占4个字节,且其数值与数据区的簇号相同MEMORY:7EC6 mov ecx, eaxMEMORY:7EC9 xor edx, edxMEMORY:7ECC movzx ebx, word ptr [bp+0Bh] ; 获取每扇区字节数MEMORY:7ED2 push ebxMEMORY:7ED4 div ebxMEMORY:7ED7 movzx ebx, word ptr [bp+0Eh] ; 获取保留扇区数MEMORY:7EDD add eax, ebxMEMORY:7EE0 mov ebx, [bp+1Ch] ; 获取隐藏扇区数MEMORY:7EE5 add eax, ebxMEMORY:7EE8 pop ebxMEMORY:7EEA dec ebxMEMORY:7EEC and ecx, ebxMEMORY:7EEF movzx ebx, word ptr [bp+28h] ; 获取FAT表镜像标志MEMORY:7EF5 and bx, 0Fh ; 值为0表示系统保存2份互为备份的FAT表,值为1表示系统仅保存1份FAT表MEMORY:7EF8 jz short loc_7F12MEMORY:7EFA cmp bl, [bp+10h]MEMORY:7EFE jb short loc_7F03MEMORY:7F00 jmp loc_7D93
上面这段代码其实就是计算出含有该簇号的fat表所在的扇区偏移。因为每个簇号在fat中占4个字节,所以簇号*4就得出该簇号在fat表中的偏移,然后除以扇区大小就可以得知该簇号相对于fat表的扇区偏移,再加上保留扇区和隐藏扇区,就可以计算出该簇号所在扇区的绝对偏移值。然后检测这个文件系统中有几个fat表,如果有两个,则跳到 short loc_7F12处。接下来我们看 short loc_7F12处的代码
[Asm] 纯文本查看 复制代码MEMORY:7F03 loc_7F03: ; CODE XREF: MEMORY:7EFEjMEMORY:7F03 push eaxMEMORY:7F05 mov eax, [bp+24h]MEMORY:7F0A mul ebxMEMORY:7F0D pop edxMEMORY:7F0F add eax, edxMEMORY:7F12MEMORY:7F12 loc_7F12: ; CODE XREF: MEMORY:7EF8jMEMORY:7F12 push ecxMEMORY:7F14 mov bx, 9000hMEMORY:7F17 mov es, bxMEMORY:7F19 assume es:nothingMEMORY:7F19 cmp eax, dword_7F3AMEMORY:7F1E jz short loc_7F2CMEMORY:7F20 mov dword_7F3A, eaxMEMORY:7F24 xor bx, bxMEMORY:7F26 mov cx, 1MEMORY:7F29 call sub_7CDBMEMORY:7F2CMEMORY:7F2C loc_7F2C: ; CODE XREF: MEMORY:7F1EjMEMORY:7F2C pop ecxMEMORY:7F2E mov eax, es:[ecx]MEMORY:7F33 and eax, 0FFFFFFFhMEMORY:7F39 retn
这里则是读取该簇号所在的扇区数据,然后获取该簇号在fat中的值,如果一个文件占用几个簇,那么该簇号在fat中的值就是下一个簇的簇号,如果文件结束,则该簇号在fat表中的值为0x0fffffff。这个函数结束了之后程序又会跳回上面的loc_7E8C,判断文件是否结束,如果没有结束则继续读取数据,如果结束,则程序跳转去执行loc_7EB5的代码。
[Asm] 纯文本查看 复制代码MEMORY:7EB5 loc_7EB5: ; CODE XREF: MEMORY:7E92jMEMORY:7EB5 mov dl, [bp+40h]MEMORY:7EB9 mov dh, byte ptr loc_7DFDMEMORY:7EBD jmp far ptr loc_F800
上面这段代码获取磁盘的驱动器号,然后程序跳转到loc_F800去执行,其实loc_F800就是刚才读取的FREELDR.SYS文件的数据,这里程序去执行FREELDR.SYS文件的代码。到了这里,DBR的调试也结束了,接下来就是实行FREELDR.SYS文件中的代码了。(这里的文件名和Windows的有些出入,Windows的应该是NTLDR)
三、FREELDR.SYS
我们接着看下面的代码
[Asm] 纯文本查看 复制代码MEMORY:F800 loc_F800: ; CODE XREF: MEMORY:7EBDJMEMORY:F800 ; MEMORY:F853jMEMORY:F800 jmp loc_FA00
在loc_F800中,只有一个跳转,我们接着去看loc_FA00处的代码
[Asm] 纯文本查看 复制代码MEMORY:FA00 loc_FA00: ; CODE XREF: MEMORY:loc_F800jMEMORY:FA00 cliMEMORY:FA01 xor ax, axMEMORY:FA03 mov ds, axMEMORY:FA05 mov es, axMEMORY:FA07 assume es:MEMORYMEMORY:FA07 mov fs, axMEMORY:FA09 mov gs, axMEMORY:FA0B mov ss, axMEMORY:FA0D mov sp, word_FAAEMEMORY:FA11 call sub_FC16
这里设置一些段寄存器之后,调用了一个call,我们进去看这个call 的代码
[Asm] 纯文本查看 复制代码MEMORY:FC16 sub_FC16 proc near ; CODE XREF: MEMORY:FA11pMEMORY:FC16 pushaMEMORY:FC17 call sub_FC07MEMORY:FC1A mov al, 0D1h ; ' ;告诉键盘控制器我们要写命令MEMORY:FC1C out 64h, al ; 8042 keyboard controller command register.MEMORY:FC1C ; Write output port (next byte to port 60h):MEMORY:FC1C ; 7: 1=keyboard data line pulled low (inhibited)MEMORY:FC1C ; 6: 1=keyboard clock line pulled low (inhibited)MEMORY:FC1C ; 5: enables IRQ 12 interrupt on mouse IBFMEMORY:FC1C ; 4: enables IRQ 1 interrupt on keyboard IBFMEMORY:FC1C ; 3: 1=mouse clock line pulled low (inhibited)MEMORY:FC1C ; 2: 1=mouse data line pulled low (inhibited)MEMORY:FC1C ; 1: A20 gate on/offMEMORY:FC1C ; 0: reset the PC (THIS BIT SHOULD ALWAYS BE SET TO 1)MEMORY:FC1E call sub_FC07MEMORY:FC21 mov al, 0DFh ; ' ;告诉键盘控制器我们要开启A20MEMORY:FC23 out 60h, al ; 8042 keyboard controller data register.MEMORY:FC25 call sub_FC07MEMORY:FC28 popaMEMORY:FC29 retn
这段代码的功能其实就是开启A20地址线,call sub_FC07这个函数其实是等待8042的Inputbuffer为空,我们看看它的代码
[Asm] 纯文本查看 复制代码MEMORY:FC07 sub_FC07 proc near ; CODE XREF: sub_FC07+CjMEMORY:FC07 ; sub_FC16+1p ...MEMORY:FC07 jmp short $+2MEMORY:FC09 ; ---------------------------------------------------------------------------MEMORY:FC09MEMORY:FC09 loc_FC09: ; CODE XREF: sub_FC07jMEMORY:FC09 jmp short $+2MEMORY:FC0B ; ---------------------------------------------------------------------------MEMORY:FC0BMEMORY:FC0B loc_FC0B: ; CODE XREF: sub_FC07:loc_FC09jMEMORY:FC0B in al, 64h ; 8042 keyboard controller status registerMEMORY:FC0B ; 7: PERR 1=parity error in data received from keyboardMEMORY:FC0B ; +----------- AT Mode ----------+------------ PS/2 Mode ------------+MEMORY:FC0B ; 6: |RxTO receive (Rx) timeout | TO general timeout (Rx or Tx)|MEMORY:FC0B ; 5: |TxTO transmit (Tx) timeout | MOBF mouse output buffer full |MEMORY:FC0B ; +------------------------------+-----------------------------------+MEMORY:FC0B ; 4: INH 0=keyboard communications inhibitedMEMORY:FC0B ; 3: A2 0=60h was the port last written to, 1=64h was lastMEMORY:FC0B ; 2: SYS distinguishes reset types: 0=cold reboot, 1=warm rebootMEMORY:FC0B ; 1: IBF 1=input buffer full (keyboard can't accept data)MEMORY:FC0B ; 0: OBF 1=output buffer full (data from keyboard is available)MEMORY:FC0D cmp al, 0FFhMEMORY:FC0F jz short locret_FC15MEMORY:FC11 test al, 2MEMORY:FC13 jnz short sub_FC07MEMORY:FC15 locret_FC15: ; CODE XREF: sub_FC07+8jMEMORY:FC15 retn
具体的就不在这里赘述,大家可以自己去百度下关于A20地址线的相关资料。开启了A20之后,我们接着看程序又干了些啥
[Asm] 纯文本查看 复制代码MEMORY:FA14 mov dword_6F00, 0FA41hMEMORY:FA1D mov ax, 1000hMEMORY:FA20 mov es, axMEMORY:FA22 assume es:nothingMEMORY:FA22 mov eax, es:3ChMEMORY:FA27 add eax, 18hMEMORY:FA2B mov eax, es:[eax+10h]MEMORY:FA31 add eax, 10000hMEMORY:FA37 mov dword_FA9B, eaxMEMORY:FA3B xor ax, axMEMORY:FA3D mov es, axMEMORY:FA3F assume es:MEMORYMEMORY:FA3F jmp short loc_FA7C
这里则是从内存中读取一个数值,然后保存在dword_FA9B里,至于这个数值干啥用,我们接着看
[Asm] 纯文本查看 复制代码MEMORY:FA7C loc_FA7C: ; CODE XREF: MEMORY:FA3FjMEMORY:FA7C cliMEMORY:FA7D mov word_FAAE, spMEMORY:FA81 lgdt fword ptr byte_FAD8MEMORY:FA86 mov eax, cr0MEMORY:FA89 or eax, 1MEMORY:FA8D mov cr0, eax
上面这段代码先是将fword ptr byte_FAD8加载到GDT表的寄存器中,然后开启保护模式。保护模式是否开启是由cr0的第0位控制的,为1表示开启保护模式,为0表示不开启。
开启了A20和保护模式之后,下面的代码就变成32位的了,至于后面都干了写啥,后期分析之后会分享出来,以上只是我个人的一些分析过程,有分析错误或者描述不对的地方,还希望大家能提出来,我们共同进步
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |