汇编语言写引导扇区

来源:互联网 发布: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  00
00007C24  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 




原创粉丝点击