org 7c00h segment .textbits 16
start:
jmp short main
nop
首先两句定义整个代码的运行位置,org 7C00H指示整个代码的其实位置在7C00处,这样可以减少不必要的便宜运算。也就是整个MBR加载到系统之后不存在偏移。而后面的bits 16则是表明整个代码段都是16位对齐,start后面是MBR的开始,这一句通过汇编会生成两字节的调整指令和一跳空操作指令。紧跟三条跳转指令后面的是MBR当中的一些属性值。
OEMName db 'MSWIN4.0' ;厂商标志和OS版本号 BytesPerSector dw 512 ;每扇区的字节数通过后面的注释,还不能对整个属性有一个大概的了解。并且在在上面的属性当中实际有些数值是不正确的,比如BootDrive,实际上MBR有一些信息是在系统安装的时候进行填写,这里只是为了占位符,所以填入的是0,接下来是开头位置的jmp指令的main标号。(具体的FAT分析可以参考FAT手册)SectsPerCluster db 1 ;每簇的扇区数 ReservedSectors dw 1 ;第一个FAT开始之前的扇区数,包括引导扇区 NumberOfFats db 2 ;该分区上FAT的副本数 MaxRootEntries dw 0 ; 对FAT32分区而言,本字段必须设置为 0 TotalSectors dw 0 ; 对FAT32分区而言,本字段必须设置为 0 MediaDescriptor db 0f8h ; 媒体被类型信息,值0xF8表示硬盘 SectorsPerFat dw 0 ; 对FAT32分区而言,本字段必须设置为 0 SectorsPerTrack dw 18 ;包含使用INT13h的磁盘的“每道扇区数”几何结构值 NumberOfHeads dw 2 ;使用INT 13h的磁盘的“磁头数”几何结构值 HiddenSectors dd 0 ;分区上引导扇区之前的扇区数 TotalSectorsBig dd 0 ;全部扇区 SectorsPerFatBig dd 0 ;只被FAT32使用,该分区每个FAT所占的扇区数 ExtendedFlags dw 0 ;此域为FAT32特有。Bits0-3: 活动的FAT(active FAT)数目,只在镜像禁止时才效。Bits 4-6: 保留Bits 7: 0 表示FAT实时镜像到所的FAT表中
;1 表示只一个活动的FAT表。这个表就是Bits0-3所指定的那个
;Bits8-15: 保留 FSVersion dw 0 ;此域为FAT32特有 RootDirStartCluster dd 0 ;根目录所在第一个簇的簇号 FSInfoSector dw 0 ;只供FAT32使用, FAT32分区的保留区中的文件系统信息 BackupBootSector dw 6 ;如果不为0,表示在保留区中引导记录的备份数据所占的扇区数,通常为6 Reserved1 times 12 db 0 ;12个字节均为0x00 保留 BootDrive db 80 ;用于BIOS中断0x13得到磁盘驱动器参数,0x00为软盘,0x80为硬盘 Reserved db 0 ;保留 ExtendSig db 29h ;段必须要有能被Windows 2000所识别的值0x28或0x29 SerialNumber dd 00000000h ;在格式化磁盘时所产生的一个随机序号,它有助于区分磁盘 VolumeLabel db 'NO NAME ' ;卷标也作为一个特殊的文件(有文件名无文件内容)保存在根目录中 FileSystem db 'FAT32 ' ;通常设置为“FAT32”
main: 00007C5A 33C9 xor cx,cx 00007C5C 8ED1 mov ss,cx ; 00007C5E BCF47B mov sp,0x7bf4 ; 首先设置段寄存器和相应的堆栈,注意这里的堆栈式向下增长的,所以当sp设置为7c00的时候不会覆盖掉代码段 00007C61 8EC1 mov es,cx 00007C63 8ED9 mov ds,cx 00007C65 BD007C mov bp,0x7c00 ;这里SP不等于BP,其中BP和SP之间的数据用于局部变量 00007C68 884E02 mov [bp+0x2],cl ;这里bp+2实际上是最开头的第三个nop指令 ;********************************************************************** 00007C6B 8A5640 mov dl,[bp+BootDrive] 00007C6E B408 mov ah,0x8 00007C70 CD13 int 0x13 ;获取整个磁盘的参数 00007C72 7305 jnc drive_param_ok ; 这一条跳转应该不陌生了吧,INT13调用会设置进位标志 drive_param_error: 00007C74 B9FFFF mov cx,0xffff ; We couldn't determine the drive parameters 00007C77 8AF1 mov dh,cl ; So just set the CHS to 0xff drive_param_ok: 00007C79 660FB6C6 movzx eax,dh ;将获得的参数数据用于计算整个磁盘的大小,假设没有跳转的话,下面的inc指令将使得整个ax溢出,也就是ax为0,所以整个大小为0 00007C7D 40 inc ax ; BIOS的int 13进行读取磁盘的数据操作,功能08H 功能描述:读取驱动器参数 00007C7E 660FB6D1 movzx edx,cl ; 入口参数:AH=08H,DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘 00007C82 80E23F and dl,0x3f ; 出口参数:CF=1——操作失败,AH=状态代码,参见功能号01H中的说明,否则, BL=01H — 360K;=02H — 1.2M;=03H — 720K;=04H — 1.44M 00007C85 F7E2 mul dx ; CH=柱面数的低8位 ; CL的位7-6=柱面数的该2位 ;CL的位5-0=扇区数 ;DH=磁头数 ;上面的mul指令执行之后,数据dx:ax存放的是磁头数目*扇区数目00007C87 86CD xchg cl,ch ; 两个寄存器交换之后,CX向右移动六位就可以得到柱面数 00007C89 C0ED06 shr ch,0x6 ; 00007C8C 41 inc cx ; 00007C8D 660FB7C9 movzx ecx,cx 00007C91 66F7E1 mul ecx ; 柱面数*先前的结果得到最终磁盘驱动器的大小,并保存到局部变量当中 00007C94 668946F8 mov [bp-0x8],eax ; 00007C98 837E1600 cmp word [bp+TotalSectors],byte +0x0 ; 00007C9C 7538 jnz print_ntldr_error_message ; 00007C9E 837E2A00 cmp word [bp+FSVersion],byte +0x0 ; 上面进行MBR的属性分析,如果属性不满足则打印出错信息 00007CA2 7732 ja print_ntldr_error_message ; /***************************************************************** 整个FAT32文件系统支持多系统启动的,而FAT32真正的boot sector有三个扇区长,所以微软对FAT32的启动进行了扩展,其中扇区0是传统的MBR,扇区1保存的是文件系统信息 扇区2用于存放额外的启动代码。为了实现多系统启动,所以就约定将win2k的boot sector存放到第十三个sector,前面的第二个sector用于存放98的boot 00007CA4 668B461C mov eax,[bp+HiddenSectors] ; Get the count of hidden sectors 00007CA8 6683C00C add eax,byte +0xc ; Add 12 to that value so that we are loading the 13th sector of the volume 00007CAC BB0080 mov bx,0x8000 ; Read the sector to address 0x8000 00007CAF B90100 mov cx,0x1 ; Read just one sector 00007CB2 E82B00 call read_sectors ; Read it 00007CB5 E94803 jmp 0x8000 ; Jump to the next sector of boot code print_disk_error_message: 00007CB8 A0FA7D mov al,[DISK_ERR_offset_from_0x7d00] putchars: 00007CBB B47D mov ah,0x7d 00007CBD 8BF0 mov si,ax ;这一段实际上是组装整个四,其中si等于7dbf,最后面的代码到了7d6f,然后ret本身一个字节, get_another_char: ;后面的NTLDR包含11字节,filter包含49字节,同时filter的0可以作为NTLDR的结尾符号;NTLDR_ERR包含19字节,那么7DBF刚好指向DISK_ERR 00007CBF AC lodsb 00007CC0 84C0 test al,al 00007CC2 7417 jz reboot ;如果数据等于0,则直接重启 00007CC4 3CFF cmp al,0xff ;最后这里会设置零标志位 00007CC6 7409 jz print_reboot_message 00007CC8 B40E mov ah,0xe 00007CCA BB0700 mov bx,0x7 00007CCD CD10 int 0x10 00007CCF EBEE jmp short get_another_char;如果上面的跳转不成功就会循环打印数据 print_reboot_message: 00007CD1 A0FB7D mov al,[RESTART_ERR_offset_from_0x7d00] 00007CD4 EBE5 jmp short putchars print_ntldr_error_message: 00007CD6 A0F97D mov al,[NTLDR_ERR_offset_from_0x7d00] 00007CD9 EBE0 jmp short putchars reboot: 00007CDB 98 cbw 00007CDC CD16 int 0x16 ;等待 00007CDE CD19 int 0x19 ;发出重启中断 read_sectors: 00007CE0 6660 pushad 00007CE2 663B46F8 cmp eax,[bp-0x8] ;首先验证要读的地址是不是在范围内,如果在磁盘驱动范围内,就跳转到7D34开始执行 00007CE6 0F824A00 jc near 0x7d34 00007CEA 666A00 o32 push byte +0x0;这里利用66前缀强制压入堆栈两个字节也就是0是以word类型存在于堆栈当中 00007CED 6650 push eax 00007CEF 06 push es 00007CF0 53 push bx 00007CF1 666810000100 push dword 0x10010 00007CF7 807E0200 cmp byte [bp+0x2],0x0 ;查看是否有改动,最初是设置为0,如果不是等于0则表明已经执行过一次,直接跳过下面的执行 00007CFB 0F852000 jnz near 0x7d1f 00007CFF B441 mov ah,0x41 00007D01 BBAA55 mov bx,0x55aa 00007D04 8A5640 mov dl,[bp+BootDrive] 00007D07 CD13 int 0x13 ;利用int 13执行磁盘检验, 00007D09 0F821C00 jc near 0x7d29 00007D0D 81FB55AA cmp bx,0xaa55 00007D11 0F851400 jnz near 0x7d29 00007D15 F6C101 test cl,0x1 00007D18 0F840D00 jz near 0x7d29 00007D1C FE4602 inc byte [bp+0x2] ;设置执行标志 00007D1F B442 mov ah,0x42 00007D21 8A5640 mov dl,[bp+BootDrive] 00007D24 8BF4 mov si,sp ;执行int 13中断,AH=42,其中si依次为0x10010,由于bx没有改变,所以这里堆栈当中是8000,es为0,而eax则开始读取的地址,加上开始压入堆栈的word 0组成中断的参数 00007D26 CD13 int 0x13 ;具体分析可以参考上一篇 00007D28 B0F9 mov al,0xf9 ;仅仅作为占位符号 00007D2A 6658 pop eax 00007D2C 6658 pop eax 00007D2E 6658 pop eax 00007D30 6658 pop eax ;清空压入的堆栈参数 00007D32 EB2A jmp short 0x7d5e ;跳过正常的读取流程 00007D34 6633D2 xor edx,edx ;恢复原来的C、H、S值,然后利用这些值进行BIOS读取,因为BIOS有标准BIOS和LBA BIOS,所以在上面需要进行判断 00007D37 660FB74E18 movzx ecx,word [bp+SectorsPerTrack] 00007D3C 66F7F1 div ecx ;edx:eax除以ecx得到磁道号,磁道号存放在eax当中 00007D3F FEC2 inc dl ;递增dl,因为dl存放的是余数,所以这里dl实际上扇区号 00007D41 8ACA mov cl,dl 00007D43 668BD0 mov edx,eax ; 00007D46 66C1EA10 shr edx,0x10 ;edx向右移动16位,也就是清空edx 00007D4A F7761A div word [bp+NumberOfHeads];磁道号除以磁头数,得到柱面号,余数dl当中存放磁头号 00007D4D 86D6 xchg dl,dh ;交换之后存放到dh当中,因为dl需要存放函数参数 00007D4F 8A5640 mov dl,[bp+BootDrive] 00007D52 8AE8 mov ch,al 00007D54 C0E406 shl ah,0x6 00007D57 0ACC or cl,ah 00007D59 B80102 mov ax,0x201 00007D5C CD13 int 0x13 00007D5E 6661 popad ;验证上面的int 13是否正确执行,如果没能得到正确结果则显示错误,然后退出 00007D60 0F8254FF jc near print_disk_error_message 00007D64 81C30002 add bx,0x200 ;bx存放读入的当前扇区的尾部的位置 00007D68 6640 inc eax 00007D6A 49 dec cx 00007D6B 0F8571FF jnz near read_sectors 00007D6F C3 ret ;从这里结束整个read过程NTLDR db 'NTLDR '
filler times 49 db 0
NTLDR_ERR db 0dh,0ah,'NTLDR is missing',0ffh
DISK_ERR db 0dh,0ah,'Disk error',0ffh
RESTART_ERR db 0dh,0ah,'Press any key to restart',0dh,0ah
more_filler times 16 db 0
NTLDR_offset_from_0x7d00 db 0
NTLDR_ERR_offset_from_0x7d00 db 0ach
DISK_ERR_offset_from_0x7d00 db 0bfh
RESTART_ERR_offset_from_0x7d00 db 0cch
dw 0
dw 0aa55h
*******************************************************************************************************
根据上面的分析,整个流程转到8000,开始执行
00008000 660FB64610 movzx eax,byte [bp+NumberOfFats] ;获得分区上的FAT表项数目
00008005 668B4E24 mov ecx,[bp+SectorsPerFatBig] ;该分区每个FAT表项所占的扇区数,利用这个数目加上隐藏和保留的数据可以得出起始根目录
00008009 66F7E1 mul ecx ;
0000800C 6603461C add eax,[bp+HiddenSectors] ;
00008010 660FB7560E movzx edx,word [bp+ReservedSectors] ;
00008015 6603C2 add eax,edx ;
00008018 668946FC mov [bp-0x4],eax ; 将起始根目录的所在的扇区号存放起来
0000801C 66C746F4FFFFFFFF mov dword [bp-0xc],0xffffffff ; 保存0Xffffffff,加上之前使用的三个区间,实际上局部存储都已经被使用了
00008024 668B462C mov eax,[bp+RootDirStartCluster] ;读取根目录开始的簇
00008028 6683F802 cmp eax,byte +0x2 ;如果小于2,那么不存在用于启动的根目录
0000802C 0F82A6FC jc near print_ntldr_error_message ;
00008030 663DF8FFFF0F cmp eax,0xffffff8 ; 将根目录开始的簇和最大的簇号比较,如果没有借位说明超出界限
00008036 0F839CFC jnc near print_ntldr_error_message ;
search_root_directory_cluster:
0000803A 6650 push eax ; 保存根目录的起始簇号
0000803C 6683E802 sub eax,byte +0x2 ;对簇号进行调整,因为起始的两个簇没有使用,所以减去2
00008040 660FB65E0D movzx ebx,byte [bp+SectsPerCluster] ; 保存每个簇拥有的扇区数目
00008045 8BF3 mov si,bx ;
00008047 66F7E3 mul ebx ;
0000804A 660346FC add eax,[bp-0x4] ; 跳过前面保存的数据,直接得到根目录的扇区号,同时EDX保存磁头号和柱面号
read_directory_sector:
0000804E BB0082 mov bx,0x8200 ;
00008051 8BFB mov di,bx ;
00008053 B90100 mov cx,0x1 ;
00008056 E887FC call read_sectors ; 由于一个扇区是512字节,以十六进制表示就为0x200,所以这次的存放地址是0x8200
check_entry_for_ntldr:
00008059 382D cmp [di],ch ; 测试读到的第一个数据是否为0
0000805B 741E jz ntldr_not_found ; 如果为零,则表明NTLDR不存在
0000805D B10B mov cl,0xb ;
0000805F 56 push si ;
00008060 BE707D mov si,NTLDR ;0x7d70 ;
00008063 F3A6 repe cmpsb ;进行循环比较,比较次数为11
00008065 5E pop si ;
00008066 741B jz ntldr_found ;如果相等,就表明找到NTLDR
00008068 03F9 add di,cx ; 否则跳过前面的11个字符串
0000806A 83C715 add di,byte +0x15 ; 同时增加
0000806D 3BFB cmp di,bx ; 与bx进行比较,如果没有超出一个扇区的范围,就循环比较,直到找到NTLDR为止,
0000806F 72E8 jc check_entry_for_ntldr ;
00008071 4E dec si ; 否则循环进行搜索
00008072 75DA jnz read_directory_sector ;
00008074 6658 pop eax ; 如果还没能找到NTLDR,则搜索下一个FAT表项
00008076 E86500 call get_fat_entry ;
00008079 72BF jc search_root_directory_cluster ;
ntldr_not_found:
0000807B 83C404 add sp,byte +0x4
0000807E E955FC jmp print_ntldr_error_message
ntldr_load_segment_address dw 0x2000
ntldr_found:
00008083 83C404 add sp,byte +0x4 ; 调整sp,覆盖掉原来保存的根目录
00008086 8B7509 mov si,[di+0x9] ;
00008089 8B7D0F mov di,[di+0xf] ;
0000808C 8BC6 mov ax,si ;
0000808E 66C1E010 shl eax,0x10 ;组装NTLDR的起始簇号
00008092 8BC7 mov ax,di ;
00008094 6683F802 cmp eax,byte +0x2 ; 其中EAX肯定比2要大,否则就出错
00008098 0F823AFC jc near print_ntldr_error_message ;
0000809C 663DF8FFFF0F cmp eax,0xffffff8 ;
000080A2 0F8330FC jnc near print_ntldr_error_message ;
load_next_ntldr_cluster:
000080A6 6650 push eax ;
000080A8 6683E802 sub eax,byte +0x2 ;
000080AC 660FB64E0D movzx ecx,byte [bp+SectsPerCluster] ;
000080B1 66F7E1 mul ecx ;
000080B4 660346FC add eax,[bp-0x4] ;
000080B8 BB0000 mov bx,0x0 ;
000080BB 06 push es ;
000080BC 8E068180 mov es,[ntldr_load_segment_address] ;
000080C0 E81DFC call read_sectors ; 将数据读到指定的位置
000080C3 07 pop es ;
000080C4 6658 pop eax ;
000080C6 C1EB04 shr bx,0x4 ; bx当中包含传递的数目,用于找到下一个存放数据的内存地址
000080C9 011E8180 add [ntldr_load_segment_address],bx ;
000080CD E80E00 call get_fat_entry ;获取下一个FAT的簇号在EAX当中
000080D0 0F830200 jnc near jump_to_ntldr ;
000080D4 72D0 jc load_next_ntldr_cluster ;
jump_to_ntldr:
000080D6 8A5640 mov dl,[bp+BootDrive] ;
000080D9 EA00000020 jmp 0x2000:0x0 ;
get_fat_entry:
000080DE 66C1E002 shl eax,0x2 ; 根目录的扇区号乘以4
000080E2 E81100 call load_fat_sector ;
000080E5 26668B01 mov eax,[es:bx+di] ; BX等于0,这里实际上是检验整个
000080E9 6625FFFFFF0F and eax,0xfffffff ;
000080EF 663DF8FFFF0F cmp eax,0xffffff8 ; 比较一下是否越界
000080F5 C3 ret ;
load_fat_sector:
000080F6 BF007E mov di,0x7e00 ; 将FAT表项加载到7e00,刚好是7C00+200
000080F9 660FB74E0B movzx ecx,word [bp+SectsPerCluster] ; 得到每一个簇里面包含多少扇区
000080FE 6633D2 xor edx,edx ;
00008101 66F7F1 div ecx ;得到簇号存放在EAX当中
00008104 663B46F4 cmp eax,[bp-0xc] ; 如果EAX等于0xFFFFFFFF那么就执行函数返回,表明已经达到簇的极限了
00008108 743A jz load_fat_sector_end ;
0000810A 668946F4 mov [bp-0xc],eax ; 更新原来保存的起始簇号的信息
0000810E 6603461C add eax,[bp+HiddenSectors] ;
00008112 660FB74E0E movzx ecx,word [bp+ReservedSectors] ;
00008117 6603C1 add eax,ecx ; 跳过保留的信息
0000811A 660FB75E28 movzx ebx,word [bp+ExtendedFlags] ;
0000811F 83E30F and bx,byte +0xf ; 验证FAT是否有效
00008122 7416 jz load_fat_sector_into_memory ; 如果有效就将FAT当中的数据读取到内存当中去
00008124 3A5E10 cmp bl,[bp+NumberOfFats] ; 根据bl当中的标志来判断是否还有后续的FAT表项需要分析
00008127 0F83ABFB jnc near print_ntldr_error_message ;
0000812B 52 push dx ;
0000812C 668BC8 mov ecx,eax ;将当前的FAT偏移暂时保存到ECX当中
0000812F 668B4624 mov eax,[bp+SectorsPerFatBig] ; 得到每一个FAT表项占用的扇区数目
00008133 66F7E3 mul ebx ;由于整个EBX存放的是FAT表项的数目
00008136 6603C1 add eax,ecx ;跳到下一个FAT表项
00008139 5A pop dx ;恢复dx
load_fat_sector_into_memory:
0000813A 52 push dx ;
0000813B 8BDF mov bx,di ;
0000813D B90100 mov cx,0x1 ;
00008140 E89DFB call read_sectors ;
00008143 5A pop dx ; Restore dx
load_fat_sector_end:
00008144 8BDA mov bx,dx ;表明读取了多少数据
00008146 C3 ret ; 返回
00008147 0000 add [bx+si],al
00008149 0000 add [bx+si],al
0000814B 0000 add [bx+si],al
0000814D 0000 add [bx+si],al
0000814F 0000 add [bx+si],al
00008151 0000 add [bx+si],al
00008153 0000 add [bx+si],al
00008155 0000 add [bx+si],al
00008157 0000 add [bx+si],al
00008159 0000 add [bx+si],al
0000815B 0000 add [bx+si],al
0000815D 0000 add [bx+si],al
0000815F 0000 add [bx+si],al
00008161 0000 add [bx+si],al
00008163 0000 add [bx+si],al
00008165 0000 add [bx+si],al
00008167 0000 add [bx+si],al
00008169 0000 add [bx+si],al
0000816B 0000 add [bx+si],al
0000816D 0000 add [bx+si],al
0000816F 0000 add [bx+si],al
00008171 0000 add [bx+si],al
00008173 0000 add [bx+si],al
00008175 0000 add [bx+si],al
00008177 0000 add [bx+si],al
00008179 0000 add [bx+si],al
0000817B 0000 add [bx+si],al
0000817D 0000 add [bx+si],al
0000817F 0000 add [bx+si],al
00008181 0000 add [bx+si],al
00008183 0000 add [bx+si],al
00008185 0000 add [bx+si],al
00008187 0000 add [bx+si],al
00008189 0000 add [bx+si],al
0000818B 0000 add [bx+si],al
0000818D 0000 add [bx+si],al
0000818F 0000 add [bx+si],al
00008191 0000 add [bx+si],al
00008193 0000 add [bx+si],al
00008195 0000 add [bx+si],al
00008197 0000 add [bx+si],al
00008199 0000 add [bx+si],al
0000819B 0000 add [bx+si],al
0000819D 0000 add [bx+si],al
0000819F 0000 add [bx+si],al
000081A1 0000 add [bx+si],al
000081A3 0000 add [bx+si],al
000081A5 0000 add [bx+si],al
000081A7 0000 add [bx+si],al
000081A9 0000 add [bx+si],al
000081AB 0000 add [bx+si],al
000081AD 0000 add [bx+si],al
000081AF 0000 add [bx+si],al
000081B1 0000 add [bx+si],al
000081B3 0000 add [bx+si],al
000081B5 0000 add [bx+si],al
000081B7 0000 add [bx+si],al
000081B9 0000 add [bx+si],al
000081BB 0000 add [bx+si],al
000081BD 0000 add [bx+si],al
000081BF 0000 add [bx+si],al
000081C1 0000 add [bx+si],al
000081C3 0000 add [bx+si],al
000081C5 0000 add [bx+si],al
000081C7 0000 add [bx+si],al
000081C9 0000 add [bx+si],al
000081CB 0000 add [bx+si],al
000081CD 0000 add [bx+si],al
000081CF 0000 add [bx+si],al
000081D1 0000 add [bx+si],al
000081D3 0000 add [bx+si],al
000081D5 0000 add [bx+si],al
000081D7 0000 add [bx+si],al
000081D9 0000 add [bx+si],al
000081DB 0000 add [bx+si],al
000081DD 0000 add [bx+si],al
000081DF 0000 add [bx+si],al
000081E1 0000 add [bx+si],al
000081E3 0000 add [bx+si],al
000081E5 0000 add [bx+si],al
000081E7 0000 add [bx+si],al
000081E9 0000 add [bx+si],al
000081EB 0000 add [bx+si],al
000081ED 0000 add [bx+si],al
000081EF 0000 add [bx+si],al
000081F1 0000 add [bx+si],al
000081F3 0000 add [bx+si],al
000081F5 0000 add [bx+si],al
000081F7 0000 add [bx+si],al
000081F9 0000 add [bx+si],al
000081FB 0000 add [bx+si],al
000081FD 0055AA add [di-0x56],dl ; We can't forget the infamous boot signature