汇编语言写引导扇区
来源:互联网 发布:java sleep1000 编辑:程序博客网 时间:2024/04/18 11:34
《自己动手写操作系统》第三章的第一个程序中,作者没有把可作为引导扇区的程序拿出来,编译通过,但是在bochs 中执行时,bochs会提示没有找到启动盘,然后反汇编bin文件后,看到文件末尾并没有启动盘第510和511字节的0xaa55标志,因此要想法将0xaa55标志写入启动盘中。
试了挺多方法,为了节省篇幅,我就把最后使用的方法贴出来吧。这种方法模仿作者在第一章第一个程序中贴出来的程序,即首先计算出之前实打实的代码的长度,然后接下来(510-长度)的内容填充0,最后两个字节写上0xaa55。完整的程序最后附上,下面分析原理。
times 510-(4+(GDTLen1+3)/4*4+(SegCode16Len+3)/4*4+(SegCode32Len)) db 0dw 0xAA55
当然,第三章这个程序比第一章的复杂,一是第三章程序中涉及到了分节,因此第一章中程序的 510 -($-$$)就失去了效用,因为$$表示的当前节的节首,因此需要分别统计这三个节的长度。二是因为分节,产生了节首的对齐问题。下面分别阐述这两个问题的解决方案。
得到每个节的长度,这个问题很简单,只要在节首和节尾记录一下即可,如下所示,不再赘述。
[SECTION .gdt]LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址GdtLenequ$ - LABEL_GDT; GDT长度GdtPtrdwGdtLen - 1; GDT界限dd0; GDT基地址SelectorCode32equLABEL_DESC_CODE32- LABEL_GDTSelectorVideoequLABEL_DESC_VIDEO- LABEL_GDTGDTLen1 equ $-LABEL_GDT;END of [SECTION .gdt]
下面讨论节首的对齐问题。第一个问题解决之后,我曾想着直接用各个节的长度和来代表程序的长度,结果出错,还是找不到启动盘。通过反汇编我发现如下两个问题,一是补充的0的个数总是过多,说明程序的长度我统计少了,二是反汇编后程序每个节首的代码总是与源程序不同,说明在两个节之间产生了一些额外的代码,使反汇编器被骗。如下两段代码,其中左面代码是源程序中16位部分与32位部分交界处的一段,右边代码为相同位置的反汇编代码,其中红色区域的反汇编的代码,是可疑代码。
mov cr0, eaxjmp dword SelectorCode32:0 , [SECTION .s32][BITS 32]LABEL_SEG_CODE32: mov ax, SelectorVideo mov gs, ax mov edi, (80 * 11 + 79) * 2
00007C72 0F22C0 mov cr0,eax00007C75 66EA000000000800 jmp dword 0x8:0x000007C7D 0000 add [bx+si],al00007C7F 0066B8 add [bp-0x48],ah00007C82 1000 adc [bx+si],al00007C84 8EE8 mov gs,ax00007C86 BF7E07 mov di,0x77e
其中可疑的是,在反汇编后的红色代码处,出现了连续的几个0,因此我马上想到了是不是为了节首的对齐。为了验证这种思路,再看一下本程序的第一条指令的反汇编指令
00007C00 E92100 jmp word 0x7c24
这条指令要求程序跳转到0x7c24处,据源代码,这个地方是16位代码的首地址。下面贴出来0x7c24附近的反汇编代码,左图是原反汇编代码,中图是我调整后的反汇编代码,右图是原程序中相应部分的代码。0x7c24是程序的起点了,那么0x7c24以上部分是什么呢?还是一群0。分析右图,其中蓝色区域为伪代码,也就是说绿色区域是紧接着0x7c24的区域,蓝色区域是什么?是数据,经计算,这个6 byte的数据应该是23,转化成十六进制,正好是0x0000000017,如中图绿色标注部分。好了,中图中红和绿之间还夹了两个字节,我们更有理由相信这是为了对齐的设置了,因为这个区域没有代码,没有数据,唯一的特点是处于两个节的相交处,我们离真相只有一步之遥。做如下计算,计算出16代码区的字节总数,0x52,发现,0x52不是4的倍数,而0x52+2=0x54是4的倍数,加的这个2正好是中图中红色区域和绿色区域之间夹的2个字节。实验其他区域,也成立。
真相大白了,NASM在编译这个程序时是这样处理的:每个节都是4对齐的。因此如果仅简单的把所有节的长度加在一起得到程序的长度必然使得求得的长度偏小。我们应该考虑那些为了对齐作出牺牲的区域,所以有了公式:节实际占字节数=(有效字节数+3)/4*4,开头所提公式即为该式应用。
00007C1C 17 pop ss00007C1D 0000 add [bx+si],al00007C1F 0000 add [bx+si],al00007C21 0000 add [bx+si],al00007C23 008CC88E add [si-0x7138],cl00007C27 D88EC08E fmul dword [bp-0x7140]00007C2B D0BC0001 sar byte [si+0x100],100007C2F 6631C0 xor eax,eax00007C32 8CC8 mov ax,cs00007C34 66C1E004 shl eax,0x4
00007C1C 17 pop ss00007C1D 0000 add [bx+si],al00007C1F 0000 add [bx+si],al00007C21 0000 add [bx+si],al00007C23 0000007C24 8CC88E 00007C27 D88EC08E fmul dword [bp-0x7140]00007C2B D0BC0001 sar byte [si+0x100],100007C2F 6631C0 xor eax,eax00007C32 8CC8 mov ax,cs00007C34 66C1E004 shl eax,0x4
GdtPtr dw GdtLen - 1 ; GDT界限 dd 0 ; GDT基地址SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
GDTLen1 equ $-LABEL_GDT[SECTION .s16][BITS 16]LABEL_BEGIN: mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, 0100h xor eax, eax mov ax, cs shl eax, 4
至此,写引导扇区的一大难题解决了,下面贴出代码3.1的更新,希望能对同样困惑的朋友有所帮助。
%include"pm.inc"; 常量, 宏, 以及一些说明org07c00hHAHA_begin:jmpLABEL_BEGINQianlen equ $-HAHA_begin[SECTION .gdt]LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址GdtLenequ$ - LABEL_GDT; GDT长度GdtPtrdwGdtLen - 1; GDT界限dd0; GDT基地址SelectorCode32equLABEL_DESC_CODE32- LABEL_GDTSelectorVideoequLABEL_DESC_VIDEO- LABEL_GDTGDTLen1 equ $-LABEL_GDT[SECTION .s16][BITS16]LABEL_BEGIN:movax, csmovds, axmoves, axmovss, axmovsp, 0100hxoreax, eaxmovax, csshleax, 4addeax, LABEL_SEG_CODE32movword [LABEL_DESC_CODE32 + 2], axshreax, 16movbyte [LABEL_DESC_CODE32 + 4], almovbyte [LABEL_DESC_CODE32 + 7], ahxoreax, eaxmovax, dsshleax, 4addeax, LABEL_GDT; eax <- gdt 基地址movdword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址lgdt[GdtPtr]cliinal, 92horal, 00000010bout92h, almoveax, cr0oreax, 1movcr0, eaxjmpdword SelectorCode32:0; 执行这一句会把 SelectorCode32 装入 cs,; 并跳转到 Code32Selector:0 处SegCode16Len equ $-LABEL_BEGIN[SECTION .s32][BITS32]LABEL_SEG_CODE32:movax, SelectorVideomovgs, ax; 视频段选择子(目的)movedi, (80 * 11 + 79) * 2; 屏幕第 11 行, 第 79 列。movah, 0Ch; 0000: 黑底 1100: 红字moval, 'A'mov[gs:edi], axjmp$SegCode32Lenequ$ - LABEL_SEG_CODE32; END of [SECTION .s32]times 510-(4+(GDTLen1+3)/4*4+(SegCode16Len+3)/4*4+(SegCode32Len)) db 0dw 0xAA55
- 汇编语言写引导扇区
- 写引导扇区所得
- Linux汇编写引导扇区
- 自己动手写操作系统-编写引导扇区
- 引导扇区
- 引导扇区
- 自己写操作系统1——引导扇区
- 主引导扇区释疑
- 硬盘主引导扇区
- FAT32引导扇区代码
- FAT32引导扇区代码
- 操作系统引导扇区
- 硬盘主引导扇区
- 引导扇区学习
- 主引导扇区
- 主引导扇区
- 主引导扇区
- 主引导扇区
- 基础2012.8.3.00.00
- 基础2012.8.4.00.00
- struts2session与request的值问题
- Node(20) File Upload with formidable module
- JS表单验证Email地址
- 汇编语言写引导扇区
- 39个让你受益的HTML5教程
- extjs4.0——charts
- 杭电acm1032 The 3n + 1 problem
- 搞不懂的U-boot
- 以前的代码1
- Ubuntu 12.04不能在华硕F81se系列电脑上安装解决办法
- Writing endian-independent code in C(Don't let endianness "byte" you)
- 项目经理的职责