目录

MBR引导程序源码理解

本文的作用,不在于研究MBR引导程序,MBR引导程序有很多,而且去找GRUB2的源码,比反编译来的高效。

但是本文的作用是记录了一个学习过程,这个学习过程本身值得记录下来

参考链接

  1. Linux嵌入式开发第四阶段
  2. 如何反汇编原始的16位x86机器代码?
  3. 鸟哥的Linux私房菜
  4. 汇编语言debug命令与指令机器码
  5. 为什么主引导记录的内存地址是0x7C00?
  6. 为什么 BIOS 将 MBR 加载到 x86 中的 0x7C00 中?
  7. 一步步编写操作系统 08 bios跳转到神奇的内存地址0x7c00
  8. CPU指令集——AVX2
  9. 【编程语言/汇编】BIOS中断
  10. 大容量硬盘的读写操作:BIOS扩展13号中断
  11. 汇编语言常见的标志位:CF、PF、AF、ZF、SF、TF、IF、DF、OF
  12. grub2的源码
  13. ARM中的—汇编指令
  14. 扩展int13h调用详解(修正)
  15. INT 13
  16. 使用 BIOS 访问磁盘 (INT 13h)
  17. MBR (x86)
  18. Intel® 64 and IA-32 架构软件开发人员手册下载地址
  19. GRUB引导程序之第一阶段stage1.S分析
  20. movsb movsw movsd 指令详解

开机流程简述与MBR引导程序的关系

引入鸟哥的Linux私房菜一书中的原文:

  1. 载入 BIOS 的硬件信息与进行自我检测(自检),并根据设置取得第一个可启动的装置;
  2. 读取并执行第一个启动设备内的 MBR 的启动引导程序(亦即是grub2、spfdisk等程序);
  3. 根据启动引导程序的设置加载 Kernel , Kernel 会开始检测硬件与加载驱动程序;
  4. 在硬件驱动成功后,Kernel 会主动调用 systemd 程序,并以 default.target 流程启动;
    systemd 执行 sysinit.target 初始化系统及 basic.target 准备操作系统;
    systemd 启动 multi-user.target 下的本机与服务器服务;
    systemd 执行 multi-user.target 下的 /etc/rc.d/rc.local 文件;
    systemd 执行 multi-user.target 下的 getty.target 及登录服务;
    systemd 执行 graphical 需要的服务。

由于我的系统软件大多放置到硬盘中,所以 BIOS 会指定启动的设备好让我们可以读取磁盘中的操作系统内核文件。但由于不同的操作系统它的文件系统格式不相同,因此我们必须要以一个启动引导程序来处理内核文件加载(load)的问题,因此这个启动引导程序就被称为 boot loader。那这个 boot loader 程序安装在哪里?就在启动设备的第一个扇区(sector)中,也就是我们一直谈到的 MBR(Master Boot Record,主引导记录)。

而最终 boot loader 的功能就是【加载内核文件】。

在 BIOS 读完信息后,接下来就是会到第一个启动设备的 MBR 去读取 boot loader 了。

Linux 将 boot loader 的程序代码执行与设置值加载分成两个阶段(stage)

  • Stage 1:执行 boot loader 主程序
    第一阶段为执行 boot loader 的主程序,这个主程序必须要被安装在启动区,亦即是 MBR 或启动扇区(boot sector)。但如前所述,因为 MBR 实在太小了,所以 MBR 或启动扇区通常仅安装 boot loader 的最小主程序,并没有安装 loader 的相关配置文件。
  • Stage 2:主程序加载配置文件
    第二阶段为通过 boot loader 加载所有配置文件与相关的环境参数文件(包括文件系统定义与主要配置文件 grub.cfg),一般来说,配置文件都在/boot下面。

这些与 gurb2 有关的文件都放置到 /boot/grub2 中。

进入 BIOS 确认开机启动磁盘

在这里插入图片描述

上图Removable Devices分类下没有设备,

所以排第一顺位的是Bootable Add-in Cards这个设备。

其次是VMware Virtual SCSI Hard Drive (0:0),这就是我们在虚拟机配置的唯一一块硬盘。

经过试验,将VMware Virtual SCSI Hard Drive (0:0)调整为第一顺位,然后开机,开机过程和结果完全没有变化,都是正常开机,仿佛Bootable Add-in Cards没有产生任何效果一般。

那么上面这个Bootable Add-in Cards是什么呢?直接中文翻译就是“可引导开机的附加卡”,我猜测它可能的作用是在不需要进 BIOS 调整引导顺序的情况下,快速从U盘或者其他存储卡进行引导。

就是说,你插入了引导U盘,那么优先从U盘引导开机,如果没有U盘,那么还是从第二顺位的硬盘引导启动。

总结:

在正常引导开机进入CentOS系统的情况下,其实引导磁盘是VMware Virtual SCSI Hard Drive (0:0),我们只要在CentOS中找到对应的硬盘设备可以进行下一步了。

获取引导磁盘第一扇区MBR数据

确认磁盘挂载

[root@localhost ~]# lsblk
NAME            MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda               8:0    0   60G  0 disk 
├─sda1            8:1    0  200M  0 part /boot
└─sda2            8:2    0 59.8G  0 part 
  ├─centos-root 253:0    0 37.8G  0 lvm  /
  ├─centos-swap 253:1    0   10G  0 lvm  [SWAP]
  └─centos-home 253:2    0   12G  0 lvm  /home
sr0              11:0    1 1024M  0 rom

我暂时不知道具体的手段,来证明/dev/sda这个设备就是VMware Virtual SCSI Hard Drive (0:0)。我只是因为我只给虚拟机配置了唯一一块虚拟硬盘,才能够确定是/dev/sda

复制数据作为文件

[root@localhost niao]# dd if=/dev/sda of=MBR.bin bs=512 count=1
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.00241201 s, 212 kB/s

说明:

1+0 records in里面的数字1的单位是扇区,指的是一共读了磁盘上1个扇区的数据。

最终会得到一个文件:MBR.bin

内容是:

[root@localhost niao]# hexdump -C MBR.bin
00000000  eb 63 90 10 8e d0 bc 00  b0 b8 00 00 8e d8 8e c0  |.c..............|
00000010  fb be 00 7c bf 00 06 b9  00 02 f3 a4 ea 21 06 00  |...|.........!..|
00000020  00 be be 07 38 04 75 0b  83 c6 10 81 fe fe 07 75  |....8.u........u|
00000030  f3 eb 16 b4 02 b0 01 bb  00 7c b2 80 8a 74 01 8b  |.........|...t..|
00000040  4c 02 cd 13 ea 00 7c 00  00 eb fe 00 00 00 00 00  |L.....|.........|
00000050  00 00 00 00 00 00 00 00  00 00 00 80 01 00 00 00  |................|
00000060  00 00 00 00 ff fa 90 90  f6 c2 80 74 05 f6 c2 70  |...........t...p|
00000070  74 02 b2 80 ea 79 7c 00  00 31 c0 8e d8 8e d0 bc  |t....y|..1......|
00000080  00 20 fb a0 64 7c 3c ff  74 02 88 c2 52 be 05 7c  |. ..d|<.t...R..||
00000090  b4 41 bb aa 55 cd 13 5a  52 72 3d 81 fb 55 aa 75  |.A..U..ZRr=..U.u|
000000a0  37 83 e1 01 74 32 31 c0  89 44 04 40 88 44 ff 89  |7...t21..D.@.D..|
000000b0  44 02 c7 04 10 00 66 8b  1e 5c 7c 66 89 5c 08 66  |D.....f..\|f.\.f|
000000c0  8b 1e 60 7c 66 89 5c 0c  c7 44 06 00 70 b4 42 cd  |..`|f.\..D..p.B.|
000000d0  13 72 05 bb 00 70 eb 76  b4 08 cd 13 73 0d 5a 84  |.r...p.v....s.Z.|
000000e0  d2 0f 83 de 00 be 85 7d  e9 82 00 66 0f b6 c6 88  |.......}...f....|
000000f0  64 ff 40 66 89 44 04 0f  b6 d1 c1 e2 02 88 e8 88  |d.@f.D..........|
00000100  f4 40 89 44 08 0f b6 c2  c0 e8 02 66 89 04 66 a1  |.@.D.......f..f.|
00000110  60 7c 66 09 c0 75 4e 66  a1 5c 7c 66 31 d2 66 f7  |`|f..uNf.\|f1.f.|
00000120  34 88 d1 31 d2 66 f7 74  04 3b 44 08 7d 37 fe c1  |4..1.f.t.;D.}7..|
00000130  88 c5 30 c0 c1 e8 02 08  c1 88 d0 5a 88 c6 bb 00  |..0........Z....|
00000140  70 8e c3 31 db b8 01 02  cd 13 72 1e 8c c3 60 1e  |p..1......r...`.|
00000150  b9 00 01 8e db 31 f6 bf  00 80 8e c6 fc f3 a5 1f  |.....1..........|
00000160  61 ff 26 5a 7c be 80 7d  eb 03 be 8f 7d e8 34 00  |a.&Z|..}....}.4.|
00000170  be 94 7d e8 2e 00 cd 18  eb fe 47 52 55 42 20 00  |..}.......GRUB .|
00000180  47 65 6f 6d 00 48 61 72  64 20 44 69 73 6b 00 52  |Geom.Hard Disk.R|
00000190  65 61 64 00 20 45 72 72  6f 72 0d 0a 00 bb 01 00  |ead. Error......|
000001a0  b4 0e cd 10 ac 3c 00 75  f4 c3 00 00 00 00 00 00  |.....<.u........|
000001b0  00 00 00 00 00 00 00 00  fe 8b 0b 00 00 00 80 20  |............... |
000001c0  21 00 83 9f 06 19 00 08  00 00 00 40 06 00 00 9f  |!..........@....|
000001d0  07 19 8e fe ff ff 00 48  06 00 00 b8 79 07 00 00  |.......H....y...|
000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200

反汇编MBR.bin

利用nasm命令反汇编

# 安装nasm
yum install nasm
[root@localhost niao]# ndisasm MBR.bin 
00000000  EB63              jmp short 0x65
00000002  90                nop
00000003  108ED0BC          adc [bp-0x4330],cl
00000007  00B0B800          add [bx+si+0xb8],dh
0000000B  008ED88E          add [bp-0x7128],cl
0000000F  C0FBBE            sar bl,byte 0xbe
00000012  007CBF            add [si-0x41],bh
00000015  0006B900          add [0xb9],al
00000019  02F3              add dh,bl
0000001B  A4                movsb
0000001C  EA21060000        jmp word 0x0:0x621
00000021  BEBE07            mov si,0x7be
00000024  3804              cmp [si],al
00000026  750B              jnz 0x33
00000028  83C610            add si,byte +0x10
0000002B  81FEFE07          cmp si,0x7fe
0000002F  75F3              jnz 0x24
00000031  EB16              jmp short 0x49
00000033  B402              mov ah,0x2
00000035  B001              mov al,0x1
00000037  BB007C            mov bx,0x7c00
0000003A  B280              mov dl,0x80
0000003C  8A7401            mov dh,[si+0x1]
0000003F  8B4C02            mov cx,[si+0x2]
00000042  CD13              int 0x13
00000044  EA007C0000        jmp word 0x0:0x7c00
00000049  EBFE              jmp short 0x49
0000004B  0000              add [bx+si],al
0000004D  0000              add [bx+si],al
0000004F  0000              add [bx+si],al
00000051  0000              add [bx+si],al
00000053  0000              add [bx+si],al
00000055  0000              add [bx+si],al
00000057  0000              add [bx+si],al
00000059  0000              add [bx+si],al
0000005B  800100            add byte [bx+di],0x0
0000005E  0000              add [bx+si],al
00000060  0000              add [bx+si],al
00000062  0000              add [bx+si],al
00000064  FF                db 0xff
00000065  FA                cli
00000066  90                nop
00000067  90                nop
00000068  F6C280            test dl,0x80
0000006B  7405              jz 0x72
0000006D  F6C270            test dl,0x70
00000070  7402              jz 0x74
00000072  B280              mov dl,0x80
00000074  EA797C0000        jmp word 0x0:0x7c79
00000079  31C0              xor ax,ax
0000007B  8ED8              mov ds,ax
0000007D  8ED0              mov ss,ax
0000007F  BC0020            mov sp,0x2000
00000082  FB                sti
00000083  A0647C            mov al,[0x7c64]
00000086  3CFF              cmp al,0xff
00000088  7402              jz 0x8c
0000008A  88C2              mov dl,al
0000008C  52                push dx
0000008D  BE057C            mov si,0x7c05
00000090  B441              mov ah,0x41
00000092  BBAA55            mov bx,0x55aa
00000095  CD13              int 0x13
00000097  5A                pop dx
00000098  52                push dx
00000099  723D              jc 0xd8
0000009B  81FB55AA          cmp bx,0xaa55
0000009F  7537              jnz 0xd8
000000A1  83E101            and cx,byte +0x1
000000A4  7432              jz 0xd8
000000A6  31C0              xor ax,ax
000000A8  894404            mov [si+0x4],ax
000000AB  40                inc ax
000000AC  8844FF            mov [si-0x1],al
000000AF  894402            mov [si+0x2],ax
000000B2  C7041000          mov word [si],0x10
000000B6  668B1E5C7C        mov ebx,[0x7c5c]
000000BB  66895C08          mov [si+0x8],ebx
000000BF  668B1E607C        mov ebx,[0x7c60]
000000C4  66895C0C          mov [si+0xc],ebx
000000C8  C744060070        mov word [si+0x6],0x7000
000000CD  B442              mov ah,0x42
000000CF  CD13              int 0x13
000000D1  7205              jc 0xd8
000000D3  BB0070            mov bx,0x7000
000000D6  EB76              jmp short 0x14e
000000D8  B408              mov ah,0x8
000000DA  CD13              int 0x13
000000DC  730D              jnc 0xeb
000000DE  5A                pop dx
000000DF  84D2              test dl,dl
000000E1  0F83DE00          jnc word 0x1c3
000000E5  BE857D            mov si,0x7d85
000000E8  E98200            jmp word 0x16d
000000EB  660FB6C6          movzx eax,dh
000000EF  8864FF            mov [si-0x1],ah
000000F2  40                inc ax
000000F3  66894404          mov [si+0x4],eax
000000F7  0FB6D1            movzx dx,cl
000000FA  C1E202            shl dx,byte 0x2
000000FD  88E8              mov al,ch
000000FF  88F4              mov ah,dh
00000101  40                inc ax
00000102  894408            mov [si+0x8],ax
00000105  0FB6C2            movzx ax,dl
00000108  C0E802            shr al,byte 0x2
0000010B  668904            mov [si],eax
0000010E  66A1607C          mov eax,[0x7c60]
00000112  6609C0            or eax,eax
00000115  754E              jnz 0x165
00000117  66A15C7C          mov eax,[0x7c5c]
0000011B  6631D2            xor edx,edx
0000011E  66F734            div dword [si]
00000121  88D1              mov cl,dl
00000123  31D2              xor dx,dx
00000125  66F77404          div dword [si+0x4]
00000129  3B4408            cmp ax,[si+0x8]
0000012C  7D37              jnl 0x165
0000012E  FEC1              inc cl
00000130  88C5              mov ch,al
00000132  30C0              xor al,al
00000134  C1E802            shr ax,byte 0x2
00000137  08C1              or cl,al
00000139  88D0              mov al,dl
0000013B  5A                pop dx
0000013C  88C6              mov dh,al
0000013E  BB0070            mov bx,0x7000
00000141  8EC3              mov es,bx
00000143  31DB              xor bx,bx
00000145  B80102            mov ax,0x201
00000148  CD13              int 0x13
0000014A  721E              jc 0x16a
0000014C  8CC3              mov bx,es
0000014E  60                pushaw
0000014F  1E                push ds
00000150  B90001            mov cx,0x100
00000153  8EDB              mov ds,bx
00000155  31F6              xor si,si
00000157  BF0080            mov di,0x8000
0000015A  8EC6              mov es,si
0000015C  FC                cld
0000015D  F3A5              rep movsw
0000015F  1F                pop ds
00000160  61                popaw
00000161  FF265A7C          jmp word [0x7c5a]
00000165  BE807D            mov si,0x7d80
00000168  EB03              jmp short 0x16d
0000016A  BE8F7D            mov si,0x7d8f
0000016D  E83400            call word 0x1a4
00000170  BE947D            mov si,0x7d94
00000173  E82E00            call word 0x1a4
00000176  CD18              int 0x18
00000178  EBFE              jmp short 0x178
0000017A  47                inc di
0000017B  52                push dx
0000017C  55                push bp
0000017D  42                inc dx
0000017E  2000              and [bx+si],al
00000180  47                inc di
00000181  656F              gs outsw
00000183  6D                insw
00000184  004861            add [bx+si+0x61],cl
00000187  7264              jc 0x1ed
00000189  204469            and [si+0x69],al
0000018C  736B              jnc 0x1f9
0000018E  005265            add [bp+si+0x65],dl
00000191  61                popaw
00000192  640020            add [fs:bx+si],ah
00000195  45                inc bp
00000196  7272              jc 0x20a
00000198  6F                outsw
00000199  720D              jc 0x1a8
0000019B  0A00              or al,[bx+si]
0000019D  BB0100            mov bx,0x1
000001A0  B40E              mov ah,0xe
000001A2  CD10              int 0x10
000001A4  AC                lodsb
000001A5  3C00              cmp al,0x0
000001A7  75F4              jnz 0x19d
000001A9  C3                ret
000001AA  0000              add [bx+si],al
000001AC  0000              add [bx+si],al
000001AE  0000              add [bx+si],al
000001B0  0000              add [bx+si],al
000001B2  0000              add [bx+si],al
000001B4  0000              add [bx+si],al
000001B6  0000              add [bx+si],al
000001B8  FE8B0B00          dec byte [bp+di+0xb]
000001BC  0000              add [bx+si],al
000001BE  802021            and byte [bx+si],0x21
000001C1  00839F06          add [bp+di+0x69f],al
000001C5  1900              sbb [bx+si],ax
000001C7  0800              or [bx+si],al
000001C9  0000              add [bx+si],al
000001CB  40                inc ax
000001CC  06                push es
000001CD  0000              add [bx+si],al
000001CF  9F                lahf
000001D0  07                pop es
000001D1  198EFEFF          sbb [bp-0x2],cx
000001D5  FF00              inc word [bx+si]
000001D7  48                dec ax
000001D8  06                push es
000001D9  0000              add [bx+si],al
000001DB  B87907            mov ax,0x779
000001DE  0000              add [bx+si],al
000001E0  0000              add [bx+si],al
000001E2  0000              add [bx+si],al
000001E4  0000              add [bx+si],al
000001E6  0000              add [bx+si],al
000001E8  0000              add [bx+si],al
000001EA  0000              add [bx+si],al
000001EC  0000              add [bx+si],al
000001EE  0000              add [bx+si],al
000001F0  0000              add [bx+si],al
000001F2  0000              add [bx+si],al
000001F4  0000              add [bx+si],al
000001F6  0000              add [bx+si],al
000001F8  0000              add [bx+si],al
000001FA  0000              add [bx+si],al
000001FC  0000              add [bx+si],al
000001FE  55                push bp
000001FF  AA                stosb

源码解读

整个MBR(512 Byte)会被BIOS加载到内存0x7C00位置

格式说明

以第一条指令为例,对格式进行说明:0: eb 63 jmp 0x65

内存偏移地址(IP寄存器)机器操作码汇编助记码及其操作数
00000000EB63jmp short 0x65

00000000 EB63 jmp short 0x65

CPU执行jmp short 0x65指令的过程:

  1. CPU根据 CS:IP (假设是7C00:00)指向的内存单元读取jmp short 0x65指令,读取的指令进入指令缓冲器;
  2. IP=IP+所读指令的长度(2),从而指向下一个指令,此时CS:IP=7C00:02
  3. CPU执行指令jmp short 0x65,IP寄存器的值更新为65

这里会引出一个疑问:第一行就是转移指令,跨过了很大一块区域(02H~65H),这里难道不是指令?

事后对比GRUB2的源码就知道了:

此空间用于 BIOS 参数块!!!! 不要更改第一个跳转,也不要在该区域之后的任何地方开始代码(即下表蓝色区域)。

在这里插入图片描述

00000065 FA cli

  1. CPU读取 CS:IP=7C00:65 处的指令(cli)到指令缓冲器。
  2. 更新CS:IP=7C00:66
  3. CPU执行cli指令,这是禁止CPU中断发生,确保当前运行的代码不会被打断。

00000066 90 nop

  1. CPU读取 CS:IP=7C00:66 处的指令(nop)到指令缓冲器。
  2. 更新CS:IP=7C00:67
  3. CPU执行nop指令,这是个空操作指令,只是用于保证上下指令之间增加一个稳定时间。

00000067 90 nop

  1. CPU读取 CS:IP=7C00:67 处的指令(nop)到指令缓冲器。
  2. 更新CS:IP=7C00:68
  3. CPU执行nop指令,这是个空操作指令,只是用于保证上下指令之间增加一个稳定时间。

00000068 F6C280 test dl,0x80

test指令说明:

Test对两个参数(目标,源)执行AND逻辑操作,并根据结果设置标志寄存器,结果本身不会保存。

TEST AX,BX 与 AND AX,BX 命令有相同效果,只是Test指令不改变AX和BX的内容,而AND指令会把结果保存到AX中。

同时也会更新flag寄存器中的这几个标志位:C,O,P,Z,S(其中C与O两个标志会被设为0)

flag寄存器的运算结构标志位说明:

  • CF:进位标志位。一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。
  • OF:溢出标志位。一般情况下,在进行有符号数运算的时候,运算结果超过了机器所能表示的范围称为溢出,1表示有溢出,0表示无溢出
  • PF:奇偶标志位。它记录相关指令执行后,其结果的所有bit位中1的个数是否为偶数。1表示偶数,0表示奇数。实现原理,从第0位开始,逐位与相邻位取同或,直到第7位,同或结果就是PF值。
  • ZF:零标志位。它记录相关指令执行后,其结果是否为0。1表示结果是0,0表示结果非0。
  • SF:符号标志位。它记录相关指令执行后,其结果是否为负。1表示结果为负,0表示结果非负。
  1. CPU读取 CS:IP=7C00:68 处的指令(test dl,0x80)到指令缓冲器。
  2. 更新CS:IP=7C00:6B
  3. 执行指令test dl,0x80

dl寄存器代表驱动器号(80H到FFH);软驱从0开始,硬盘从80开始。那么这里测试dl寄存器是不是80开头的,很明显就是测试是否是硬盘驱动了。

dl寄存器没有赋过值,它是0x0,将0x800x0进行与运算,得到结果是0x0,而标志寄存器记录的状态为:

1514131211109876543210
OF溢出DFIFTFSF符号ZF零AFPF奇偶CF进位
00110

0000006B 7405 jz 0x72

  1. CPU读取 CS:IP=7C00:6B 处的指令(jz 0x72)到指令缓冲器。
  2. 更新CS:IP=7C00:6D
  3. 执行指令jz 0x72

jz指令,它的作用是,判断flag寄存器的ZF标志位是否为1,如果为1则进行转移。所以je指令是根据上一条指令的执行结果作为条件来进行执行的。

可知上一条指令test dl,0x80的结果是ZF=1,那么指令实际含义是:如果dl寄存器的值不是80开头,代表不是硬盘驱动,那么就更新IP寄存器的值为72

此时CS:IP=7C00:72

00000072 B280 mov dl,0x80

  1. CPU读取 CS:IP=7C00:72 处的指令(mov dl,0x80)到指令缓冲器。
  2. 更新CS:IP=7C00:74
  3. 执行指令mov dl,0x80,将80送入dl寄存器

该指令和上一条指令配合,共同实现:判断dl寄存器是否80开头(代表硬盘驱动器),如果不是,则直接将0x80送入dl覆盖。

00000074 EA797C0000 jmp word 0x0:0x7c79

  1. CPU读取 CS:IP=7C00:74 处的指令(jmp word 0x0:0x7c79)到指令缓冲器。
  2. 更新CS:IP=7C00:79
  3. 执行指令jmp word 0x0:0x7c79。 将CS:IP寄存器改为0x7c79

ljmp 到下一条指令,因为一些虚假的 BIOS 跳转到 07C0:0000 而不是 0000:7C00。为了以防万一,用此命令进行纠正。

00000079 31C0 xor ax,ax

  1. CPU读取 CS:IP=00:7C79 处的指令(xor ax,ax)到指令缓冲器。
  2. 更新CS:IP=00:7C7B
  3. 执行指令xor ax,ax。 将AX寄存器的值,与自身做逻辑异或计算,始终得到0x0,并将结果送入AX寄存器。
  4. flag寄存器PF奇偶标记位更新为1

0000007B 8ED8 mov ds,ax

  1. CPU读取 CS:IP=00:7C7B 处的指令(mov ds,ax)到指令缓冲器。
  2. 更新CS:IP=00:7C7D
  3. 执行指令mov ds,ax。 此时AX=0x0,将AX的值设置到DS寄存器中,最终DS=0x0

CPU 要读写一个内存单元的时候,必须先给出这个内存单元的地址,在 8086CPU 中,内存地址由段地址和偏移地址组成。8086CPU 中有一个 DS 寄存器,通常用来存放要访问数据的段地址。比如我们要读取 10000H 单元的内容,可以用如下的程序段进行。

mov bx,1000H
mov ds,bx
mov al,[0]

上面[...]形式表示一个内存单元,而[0]中的0只是一个内存单元的偏移地址,而整个mov al,[0]指令都没有明确指定一个段地址,像这种指令执行的时候,CPU 会自动从 DS 寄存器中取值作为[...]这个内存单元的段地址。
例如mov al,[0]就是指把1000:0这个内存单元的值传送给 al 寄存器。

0000007D 8ED0 mov ss,ax

  1. CPU读取 CS:IP=00:7C7D 处的指令(mov ss,ax)到指令缓冲器。
  2. 更新CS:IP=00:7C7F
  3. 执行指令mov ss,ax。 此时AX=0x0,将AX的值设置到SS寄存器中,最终SS=0x0

8086CPU 中,有两个寄存器,段寄存器 SS 和寄存器 SP,栈顶的段地址存放在 SS 中,偏移地址存放在 SP 中。任意时刻,SS:SP 指向栈顶元素。push 指令和 pop 指令执行时,CPU 从 SS 和 SP 中得到栈顶的地址。

0000007F BC0020 mov sp,0x2000

  1. CPU读取 CS:IP=00:7C7F 处的指令(mov sp,0x2000)到指令缓冲器。
  2. 更新CS:IP=00:7C82
  3. 执行指令mov sp,0x2000。 将SP寄存器的值设为SP=0x2000

此时 SS 和 SP 两个寄存器都有了明确的值,代表此段代码执行到这里,正式构建了一个栈空间。

SS:SP=00:2000

00000082 FB sti

  1. CPU读取 CS:IP=00:7C82 处的指令(sti)到指令缓冲器。
  2. 更新CS:IP=00:7C83
  3. 执行指令sti。 允许CPU中断发生。

这条指令和上面00000065 FA cli指令形成配合,在clisti包围中的代码不会被外部中断,以确保它们能够一次正确运行。

00000083 A0647C mov al,[0x7c64]

从 DS 寄存器取出值0x0作为[0x7c64]内存单元的段地址,合起来就是:0:7C64

内存地址0:7C64所指位置代表的是启动盘:从中加载内核的磁盘,0xff表示使用启动盘。

指令mov al,[0x7c64]的意思就是将AX低字节AL寄存器的值设为0xff

00000086 3CFF cmp al,0xff

cmp指令说明:
CMP(比较)指令执行从目的操作数中减去源操作数的隐含减法操作,并且不修改任何操作数

  • 比较两个无符号数,则标志位结果表示如下表所示:

    CMP结果ZFCF
    目的操作数 < 源操作数01
    目的操作数 > 源操作数00
    目的操作数 = 源操作数10
  • 比较两个有符号数,则标志位结果表示如下表所示:

    CMP结果标志位
    目的操作数 < 源操作数SF ≠ OF
    目的操作数 > 源操作数SF=OF
    目的操作数 = 源操作数ZF=1

cmp al,0xff指令的意思是,比较AL寄存器的值和0xff的大小。

此时AL=0xff,cmp结果相等,即是flag寄存器标记位ZF=1;

1514131211109876543210
OF溢出DFIFTFSF符号ZF零AFPF奇偶CF进位
00110

00000088 7402 jz 0x8c

jz指令根据flag寄存器标记位ZF是否等于1来进行转移。

此时ZF=1,jz指令进行转移,将IP的值设置为0x8c

这里检查我们是否有强制磁盘引用

0000008C 52 push dx

  1. CPU读取 CS:IP=00:7C8C 处的指令(push dx)到指令缓冲器。
  2. 更新CS:IP=00:7C8D
  3. 执行指令push dx

栈是一种具有特殊的访问方式的存储空间,它的特性在于,最后进入这个空间的数据,最先出去。

入栈就是将一个新的元素放到栈顶,出栈就是从栈顶取出一个元素。
栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出。栈的这种操作规则被称为:LIFO(Last In First Out,后进先出)

8086CPU 中,有两个寄存器,段寄存器 SS 和寄存器 SP,栈顶的段地址存放在 SS 中,偏移地址存放在 SP 中。任意时刻,SS:SP 指向栈顶元素。push 指令和 pop 指令执行时,CPU 从 SS 和 SP 中得到栈顶的地址。

push dx 的执行,由以下步骤完成。

  1. 取出寄存器记录的栈顶地址SS:SP=00:2000
  2. 判断数据宽度:DX 寄存器数据是16位,所以SP=SP-2=0X1FFE
  3. 取出 DX 寄存器的值,由上面00000072 B280 mov dl,0x80指令可知 DX 的低位寄存器 dl 被设置过值0x80, 而整个 DX = 0x0080
  4. 将 DX 中的内容送入 SS:SP 指向的内存单元处,SS:SP 此时指向新栈顶

此时栈内状态如下表(H代表16进制数):

内存单元地址数据SS:SP所在位置来源
1FFEH80SS:SPDX
1FFFH00
2000H

0000008D BE057C mov si,0x7c05

寄存器赋值:SI=0x7C05

SI 寄存器是4个可以用在[...]中来进行内存单元寻址的寄存器之一。

其他三个分别是:BX、DI、BP。

只要在[...]中使用寄存器 BP,而指令中没有显性地给出段地址,段地址就默认在 SS 中,除此之外,段地址默认在 DS 中。

这一步是将 si 寄存器设置为磁盘地址包的地址

00000090 B441 mov ah,0x41

寄存器赋值:AH=0x41

ah寄存器代表的是即将执行的中断例程的功能号。

int 13 ah=41代表的是,检查是否支持LBA寻址模式。

00000092 BBAA55 mov bx,0x55aa

寄存器赋值:BX=0X55AA

00000095 CD13 int 0x13

  1. CPU读取 CS:IP=00:7C95 处的指令(int 0x13)到指令缓冲器。
  2. 更新CS:IP=00:7C97
  3. 执行指令int 0x13

int 指令的格式为:int n,n 为中断类型码,它的功能是引发中断过程。

CPU 执行 int n 指令,相当于引发一个 n 号中断的中断过程,执行过程如下:

  1. 根据中断类型码 n,从中断向量表中取得对应的中断程序入口地址(段地址和偏移地址)
  2. 标志寄存器入栈,IF=0,TF=0;
    此时SS:SP=00:1FFE,标志寄存器16位,SP=SP-2=1FFC
    标志寄存器此时的值为:FLAG=0x0044=0000 0000 0100 0100
    入栈后,栈顶表现为:0000:1FFD=0x000000:1FFC=0x44
  3. CS 入栈;16位,SP=SP-2=1FFC0000:1FFB=0x000000:1FFA=0x00
  4. IP 入栈;16位,SP=SP-2=1FF80000:1FF9=0x7C0000:1FF8=0x97
  5. (IP)=(n*4),计算结果IP=0000:004C内存单元的16位的值
  6. (CS)=(n*4+2),计算结果CS=0000:004E内存单元的16位的值

在最后一步完成后,CPU 开始执行 n 号(0x13号)中断的中断处理程序。

flag寄存器的状态控制标志位说明:
状态控制标志位是用来控制CPU操作的,它们要通过专门的指令才能使之发生改变。

  1. TF(追踪标志位):当TF被置为1 时,CPU进入单步执行方式,即每执行一条指令,产生一个单步中断请求。这种方式主要用于程序的调试。
    注意:指令系统中没有专门的指令来改变标志位TF的值,但程序员可用 pushf 入栈后修改栈内值再 popf 出栈来修改TF标志位的值。
  2. IF(中断允许标志位):用来决定CPU是否响应CPU外部的可屏蔽中断发出的中断请求,当IF=1时,CPU响应CPU外部的可屏蔽中断发出的中断请求,当IF=0时,CPU不响应CPU外部的可屏蔽中断发出的中断请求。
    注意:不管该标志为何值,CPU都必须响应CPU外部的不可屏蔽中断所发出的中断请求,以及CPU内部产生的中断请求。
  3. DF(方向标志位):在串处理指令中,每次操作后,如果DF=0,则si、di递增,如果DF=1,则si、di递减。
    注意:DF的值是由程序员进行设定的。(cld命令是将DF置为0,std命令是将DF值为1)。

中断向量表:
CPU 用8位的中断类型码通过中断向量表找到相应的中断处理程序的入口地址。中断向量表,就是中断处理程序入口地址的列表。
中断向量表在内存中保存,其中存放着 256 个中断源所对应的中断处理程序的入口。
对于 8086CPU 机,中断向量表指定放在内存地址0处,从内存 0000:0000 到 0000:03FF 的 1024 个单元中存放着中断向量表。

中断例程:
中断例程即中断向量表指向的中断处理程序,名称使用惯例而已。
CPU 执行 int 指令进入终端例程之前,标志寄存器、当前的 CS 和 IP 被压入栈中,在执行完中断例程后,应该用 iret 指令恢复 int 指令执行前的标志寄存器和 CS、IP 的值,从而接着执行应用程序。
int 指令和 iret 指令的配合使用与 call 指令和 ret 指令的配合使用具有相似的思路。只是 iret 比 ret 多了一个标志寄存器。

所有中断例程的最后一个指令都是 iret。它用汇编指令来理解就相当于:

pop IP
pop CS
popf

int 13H ah=41H API说明

功能:检验扩展功能是否存在

入口:

  • ah = 41H
  • BX = 55AAH
  • DL = 驱动器号

返回:

  • CF = 0
    • ah = 扩展功能的主版本号
    • al = 内部使用
    • BX = AA55H
    • CX = 接口支持的位掩码
      • 1 使用数据包结构的设备访问
      • 2 驱动器锁定和弹出
      • 4 增强的磁盘驱动支持(EDD)
  • CF = 1
    • ah = 错误码01H,无效命令

这个调用检验对特定的驱动器是否存在扩展功能. 如果进位标志置 1 则此驱动器不支持扩展功能. 如果进位标志为 0, 同时 BX = AA55h, 则存在扩展功能. 此时 CX 的 0 位表示是否支持第一个子集, 1位表示是否支持第二个子集.

对于 1.x 版的扩展 Int13H 来说, 主版本号 AH = 1. AL 是副版本号,但这仅限于 BIOS 内部使用, 任何软件不得检查 AL 的值.

int 13H ah=41H中断例程执行前

int 0x13指令执行前的标志寄存器状态如下表:

1514131211109876543210
OF溢出DFIFTFSF符号ZF零AFPF奇偶CF进位
00110

int 0x13指令执行期间,中断例程执行前的栈空间状态如下表:

内存单元地址数据SS:SP所在位置来源
1FF8H97SS:SPIP
1FF9H7C
1FFAH00CS
1FFBH00
1FFCH44FLAG
1FFDH00
1FFEH80DX
1FFFH00
2000H

int 0x13 AH=41H指令执行期间,中断例程执行前的各入口参数寄存器状态如下表:

寄存器数据参数意义
ah0x41检测扩展中断功能是否安装
BX0x55AA为了主动避免BX原值跟中断例程的结果混淆,在中断前先赋值为0x55AA
dl0x80驱动器号(80H到FFH);软驱从0开始,硬盘从80开始

int 13H ah=41H中断例程执行过程

寄存器赋值:BX=0XAA55

int 13H ah=41H中断例程执行后

中断例程的最后一条指令 iret 执行,恢复CS:IP和标志寄存器,返回继续执行主程序指令。

  1. 出栈,恢复主程序IP=0x7C97
  2. 出栈,恢复主程序CS=0x0000
  3. 出栈,恢复主程序FLAG=0x0044

int 0x13 AH=41H指令执行期间,中断例程执行后的各出口参数寄存器状态如下表:

寄存器数据参数意义
BX0xAA55成功时,它被例程更新为BX=0xAA55,否则它保持原值不变
ah0x00成功时,ah=版本号,否则ah=1
CS0x0000出栈恢复中断前
IP0x7C97出栈恢复中断前
FLAG0x0044出栈恢复中断前,但是CF标志位成功是为0,否则为1

iret 指令执行后的栈空间状态:

内存单元地址数据SS:SP所在位置来源
1FFEH80SS:SPDX
1FFFH00
2000H

00000097 5A pop dx

出栈:恢复DX=0x0080

内存单元地址数据SS:SP所在位置来源
2000HSS:SP

00000098 52 push dx

入栈:保存DX=0x0080

内存单元地址数据SS:SP所在位置来源
1FFEH80SS:SPDX
1FFFH00
2000H

%dl 可能已被 INT 13 破坏,AH=41H。 例如,在 AST BIOS 1.04 中会发生这种情况。所以通过重复出入栈来纠正。

00000099 723D jc 0xd8

  1. CPU读取 CS:IP=00:7C99 处的指令(jc 0xd8)到指令缓冲器。
  2. 更新CS:IP=00:7C9B
  3. 执行指令jc 0xd8

jc 和 jnc 指令说明:

jc:标志位CF=1则跳转否则不跳转
jnc:标志位CF=0则跳转否则不跳转

当前标志寄存器:

1514131211109876543210
OF溢出DFIFTFSF符号ZF零AFPF奇偶CF进位
00110

所以jc 0xd8不进行跳转。

jc指令与上面的int 13H ah=41H中断例程形成配合。后者的作用是判断 BIOS 是否支持扩展int13中断,如果支持,则CF=0,否则CF=1,那么jc指令就可以根据 BIOS 是否支持扩展int13中断来执行不同位置的“子程序指令”。

一般现在新的支持LBA模式的主板和Win98自带的DOS7操作系统是支持扩展INT 13的。

0xd8 处的指令是 CHS 寻址模式的,即是说如果不支持 LBA 寻址模式,则使用 CHS。

0000009B 81FB55AA cmp bx,0xaa55

  1. CPU读取 CS:IP=00:7C9B 处的指令(cmp bx,0xaa55)到指令缓冲器。
  2. 更新CS:IP=00:7C9F
  3. 执行指令cmp bx,0xaa55

cmp上文出现过,它是比较两个数值的大小。

此时 BX=0xAA55,与第二个立即数0xAA55相等,所以标志寄存器更新为:

1514131211109876543210
OF溢出DFIFTFSF符号ZF零AFPF奇偶CF进位
00110

0000009F 7537 jnz 0xd8

  1. CPU读取 CS:IP=00:7C9F 处的指令(jnz 0xd8)到指令缓冲器。
  2. 更新CS:IP=00:7CA1
  3. 执行指令jnz 0xd8

此时标志位ZF不等于0,所以jnz 0xd8不进行跳转。

谨慎起见,这条指令和上一条指令配合使用,继jc 0xd8之后,再次对int 13H ah=41H中断指令的结果进行确认。确认BIOS支持扩展int13。

0xd8 处的指令是 CHS 寻址模式的,即是说如果不支持 LBA 寻址模式,则使用 CHS。

000000A1 83E101 and cx,byte +0x1

  1. CPU读取 CS:IP=00:7CA1 处的指令(and cx,byte +0x1)到指令缓冲器。
  2. 更新CS:IP=00:7CA4
  3. 执行指令and cx,byte +0x1

1 byte=8 bit

byte +0x1 = 0000 0001

and 指令说明:按位与运算
二进制下,每个对应位两者都为1则为1,否则为0;
AND 指令总是清除溢出和进位标志位,并根据目标操作数的值来修改符号标志位、零标志位和奇偶标志位。

结果:

由于 int13 ah=41 检查是否支持 LBA了,不知道怎么了CX的最低位被设置了1,就代表支持LBA。

并且and指令执行完毕后,cx=0x01

1514131211109876543210
OF溢出DFIFTFSF符号ZF零AFPF奇偶CF进位
00010

000000A4 7432 jz 0xd8

  1. CPU读取 CS:IP=00:7CA4 处的指令(jz 0xd8)到指令缓冲器。
  2. 更新CS:IP=00:7CA6
  3. 执行指令jz 0xd8

此时标志位ZF=0

所以不进行跳转

000000A6 31C0 xor ax,ax

  1. CPU读取 CS:IP=00:7C79 处的指令(xor ax,ax)到指令缓冲器。
  2. 更新CS:IP=00:7C7B
  3. 执行指令xor ax,ax。 将AX寄存器的值,与自身做逻辑异或计算,始终得到0x0,并将结果送入AX寄存器。
1514131211109876543210
OF溢出DFIFTFSF符号ZF零AFPF奇偶CF进位
00110

000000A8 894404 mov [si+0x4],ax

上面有指令将 si 寄存器设置为磁盘地址包的地址0x7c05

[si+0x4]作为内存偏移地址,而默认段地址是ds寄存器的值。ds=0x0,所以内存地址就是00:7c09

已知AX=0x00

把AX寄存器的值(16位),写入到内存00:7c09处的连续两个字节。

那么这一步就是要把扩展功能的主版本号存入内存。

00:7c0aH00:7c09H
0x000x00

000000AB 40 inc ax

inc指令只有一个操作数,作用就是将操作数+1,结果送回到操作数。

已知AX=0x00

把ax寄存器的值(16位),自增1,结果为0x01,结果送回AX

此时AX=0x01

000000AC 8844FF mov [si-0x1],al

[si-0x1]和ds寄存器,共同表示内存地址=00:7c04

将AX=0x0001(16位),那么其低字节寄存器al=0x01

写入内存00:7c04处1个字节。

00:7c0aH00:7c09H00:7c04H
0x000x000x01

000000AF 894402 mov [si+0x2],ax

已知此时AX=0x0001,ds=0x0,si=0x7c05

[si-0x1]和ds寄存器,共同表示内存地址=00:7c07

将ax寄存器的值写入内存:

00:7c0aH00:7c09H00:7c08H00:7c07H00:7c04H
0x000x000x000x010x01

000000B2 C7041000 mov word [si],0x10

已知ds=0x0,si=0x7c05

[si]和ds寄存器,共同表示内存地址=00:7c05

将立即数0x10写入内存:

00:7c0aH00:7c09H00:7c08H00:7c07H00:7c05H00:7c04H
0x000x000x000x010x100x01

000000B6 668B1E5C7C mov ebx,[0x7c5c]

ebx是bx的延伸,bx是16位寄存器,ebx是32位寄存器,也可以理解为bx是ebx的低字寄存器。

将内存00:7c5c处连续4个字节(2个字)的数,写入到ebx寄存器中。

从磁盘头部数据表中可以发现,[0x7c5c]对应的是下表的4个红色字节块:

在这里插入图片描述

磁盘数据写入内存之后如下表表示:

00:7c5fH00:7c5eH00:7c5dH00:7c5cH
0x000x000x000x01

将这段内存数据写入ebx=0x00000001

000000BB 66895C08 mov [si+0x8],ebx

已知:ebx=0x00000001,ds=0x00,si=0x7c05

[si+0x8]和ds共同表示一个内存地址:00:7c0d

将ebx的值写入内存:

00:7c10H00:7c0fH00:7c0eH00:7c0dH00:7c0aH00:7c09H00:7c08H00:7c07H00:7c05H00:7c04H
0x000x000x000x010x000x000x000x010x100x01

000000BF 668B1E607C mov ebx,[0x7c60]

已知:ebx=0x00000001,ds=0x00

[0x7c60]和ds共同表示一个内存地址:00:7c60

从磁盘头部数据表中可以发现,[0x7c60]对应的是下表的4个红色字节块:

在这里插入图片描述

磁盘数据写入内存之后如下表表示:

00:7c63H00:7c62H00:7c61H00:7c60H
0x000x000x000x00

将这段内存数据写入ebx=0x00000000

000000C4 66895C0C mov [si+0xc],ebx

已知:ebx=0x00000000,ds=0x00,si=0x7c05

[si+0xc]与ds共同表示内存地址:00:7c11

将ebx的值写入内存:

00:7c14H00:7c13H00:7c12H00:7c11H00:7c10H00:7c0fH00:7c0eH00:7c0dH00:7c0aH00:7c09H00:7c08H00:7c07H00:7c05H00:7c04H
0x000x000x000x000x000x000x000x010x000x000x000x010x100x01

000000C8 C744060070 mov word [si+0x6],0x7000

已知:ds=0x00,si=0x7c05

[si+0x6]和ds共同表示内存地址:00:7c0b

将立即数0x7000写入内存:

00:7c14H00:7c13H00:7c12H00:7c11H00:7c10H00:7c0fH00:7c0eH00:7c0dH00:7c0cH00:7c0bH00:7c0aH00:7c09H00:7c08H00:7c07H00:7c05H00:7c04H
0x000x000x000x000x000x000x000x010x700x000x000x000x000x010x100x01

000000CD B442 mov ah,0x42

将立即数0x42送入ah

ah作为下一条中断指令的功能编号。

000000CF CD13 int 0x13

再次执行了一条int13中断指令,跟上次执行的不同的是,这次ah=42,代表的功能不同了。

ah=42的功能api介绍:

读取磁盘:

  • 在磁盘地址包结构中设置适当的值
  • 设置 DS:SI -> 内存中的磁盘地址包
  • 设置 AH = 0x42
  • 设置 DL = “驱动器号”——“C”驱动器通常为 0x80
  • 发出一个 INT 0x13。

如果在传输过程中出现任何错误,将设置进位标志。AH 成功时应设置为 0。

磁盘地址包格式:

OffsetSizeDescription
01数据包大小(24 字节)
11总是 0
22要传输的扇区数(某些 BIOS 上最多 127 个)
44传输缓冲区(16 位段:16 位偏移)(见注释 #1)
8448位起始 LBA 的低 32 位
12448位起始 LBA 的高 16 位

已知:

  1. DS:SI = 00:7c05,代表“磁盘地址包”的地址
  2. 磁盘地址内容(结合DS:SI和磁盘地址包格式,对已知数据进行分段展示):
    1. 数据包大小(块的大小)

      00:7c05H
      0x10
    2. 要传输的扇区数(某些 BIOS 上最多 127 个)

      00:7c08H00:7c07H
      0x000x01
    3. 传输缓冲区(16 位段:16 位偏移)

      段h段l偏移地址h偏移地址l
      00:7c0cH00:7c0bH00:7c0aH00:7c09H
      0x700x000x000x00
    4. 48位起始 LBA 的低 32 位

      00:7c10H00:7c0fH00:7c0eH00:7c0dH
      0x000x000x000x01
    5. 48位起始 LBA 的高 16 位

      00:7c14H00:7c13H00:7c12H00:7c11H
      0x000x000x000x00
  3. AH = 0x42
  4. dl = 0x80

将编号80的驱动器(c盘)的第一个扇区(512 Byte)读到内存地址0x7000

注意:文档本身所描述的汇编代码是存在内存0x7c00处,地址不同,不要混淆,也不会覆盖。

000000D1 7205 jc 0xd8

  1. CPU读取 CS:IP=00:7CD1 处的指令(jc 0xd8)到指令缓冲器。
  2. 更新CS:IP=00:7CD3
  3. 执行指令jc 0xd8

jc 和 jnc 指令说明:

jc:标志位CF=1则跳转否则不跳转
jnc:标志位CF=0则跳转否则不跳转

假设int 13H ah=42H读取磁盘数据到内存成功执行了,那么CF标志位复位0,所以当前标志寄存器:

1514131211109876543210
OF溢出DFIFTFSF符号ZF零AFPF奇偶CF进位
00110

所以jc 0xd8不进行跳转。

0xd8是指当磁盘读取失败,则改用CHS寻址模式再尝试。

000000D3 BB0070 mov bx,0x7000

  1. CPU读取 CS:IP=00:7CD3 处的指令(mov bx,0x7000)到指令缓冲器。
  2. 更新CS:IP=00:7CD6
  3. 执行指令mov bx,0x7000

寄存器赋值:BX=0x7000

000000D6 EB76 jmp short 0x14e

已知CS:IP=00:7CD6

无条件跳转到0x14e地址的指令处。即CS:IP=00:7D4E

0000014E 60 pushaw

pusha: push all

通用寄存器压(按规定顺序)入栈

0000014F 1E push ds

将寄存器DS压入栈,将此时的ds保存,因为后续有指令对其赋值更新,压入栈以便再次恢复。

00000150 B90001 mov cx,0x100

寄存器赋值:CX=0x100,作为后续rep指令的重复次数

00000153 8EDB mov ds,bx

已知BX=0x7000

寄存器赋值DS=0x7000

00000155 31F6 xor si,si

  1. CPU读取 CS:IP=00:7D55 处的指令(xor si,si)到指令缓冲器。
  2. 更新CS:IP=00:7D57
  3. 执行指令xor si,si。 将AX寄存器的值,与自身做逻辑异或计算,始终得到0x0,并将结果送入SI寄存器。
  4. flag寄存器PF奇偶标记位更新为1

此时DS:SI = 7000:00

00000157 BF0080 mov di,0x8000

注意:0x8000是GRUB引导机内核地址

寄存器赋值DI=0x8000

0000015A 8EC6 mov es,si

已知:SI=0x00

寄存器赋值ES=0x00

0000015C FC cld

已知:

  • SI=0x00
  • DI=0x8000

标志寄存器更新:DF=0

flag寄存器的状态控制标志位说明:
状态控制标志位是用来控制CPU操作的,它们要通过专门的指令才能使之发生改变。

  1. TF(追踪标志位):当TF被置为1 时,CPU进入单步执行方式,即每执行一条指令,产生一个单步中断请求。这种方式主要用于程序的调试。
    注意:指令系统中没有专门的指令来改变标志位TF的值,但程序员可用 pushf 入栈后修改栈内值再 popf 出栈来修改TF标志位的值。
  2. IF(中断允许标志位):用来决定CPU是否响应CPU外部的可屏蔽中断发出的中断请求,当IF=1时,CPU响应CPU外部的可屏蔽中断发出的中断请求,当IF=0时,CPU不响应CPU外部的可屏蔽中断发出的中断请求。
    注意:不管该标志为何值,CPU都必须响应CPU外部的不可屏蔽中断所发出的中断请求,以及CPU内部产生的中断请求。
  3. DF(方向标志位):在串处理指令中,每次操作后,如果DF=0,则si、di递增,如果DF=1,则si、di递减。
    注意:DF的值是由程序员进行设定的。(cld命令是将DF置为0,std命令是将DF值为1)。

0000015D F3A5 rep movsw

已知:

  • DS:SI=7000:00
  • ES:DI=00:8000
  • CX=0x100
  • DF=0

指令说明:

  • rep

    (repeat)重复
    rep指令作用是重复 后面的指令,是个前缀,例如这里 rep movsw,就是重复执行movsw指令
    rep指令每次执行的时候都从ecx寄存器里面读取值,当cx/ecx大于0,就执行后面语句,执行完以后,会让cx/ecx减1,然后再执行rep后面的指令。

  • movsw

    数据传送指令,都是从源地址向目的地址传送数据
    16位模式下:
    源地址是DS:SI,目的地址是ES:DI
    32位模式下:
    源地址是DS:ESI,目的地址是ES:EDI
    注意:在传送完成之后,SI和DI(或者ESI和EDI)会增加或者减小。
    当DF=0 时,表示正向传送,传送之后SI和DI(或者ESI和EDI)的值会增加;
    当DF=1 时,表示反向传送,传送之后SI和DI(或者ESI和EDI)的值会减小;

    movsb:传送一个字节,之后SI和DI(或者ESI和EDI)加/减1
    movsw:传送一个字,之后SI和DI(或者ESI和EDI)加/减2
    movsd:传送一个双字,之后SI和DI(或者ESI和EDI)加/减4

    单纯的movsb/ movsw/ movsd只能执行一次,如果希望处理器自动地反复执行,可以加上指令前缀rep;在寄存器CX(16位模式)或者ECX(32位模式)中设置传送的次数。当CX/ECX不等于0时,则执行movsb/ movsw/ movsd,执行后,CX/ECX的值减一,直到减为0为止。

指令作用:

7000:007000:100之间的内存数据,完整传送到00:800000:8100之间。

0000015F 1F pop ds

恢复DS寄存器的值,消除本小段代码对寄存器值的破坏

00000160 61 popaw

恢复各通用寄存器的值,消除本小段代码对寄存器值的破坏

00000161 FF265A7C jmp word [0x7c5a]

根据磁盘位置找到这个位置,[0x7c5a]的值是0x8000

那么就相当于,开始执行在00:8000处的内核指令了。

至此,MBR引导程序跑完了。

以上MBR引导程序执行过的指令,通过绿色块标识

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐