我正在编译并运行二进制文件(引导扇区,阶段1,阶段2)进行练习。引导扇区是asm,第一阶段是运行良好的asm。第二阶段的加载为0x1000,我有一些asm跳转到我的C代码的开头。我的跳跃和呼唤似乎(短)偏离了两个字节。
我已经尝试过Bochs和Qemu中的代码(逐步进行)。所有代码看起来都不错。我什至在IDA中将其拆解,并且看起来不错。我认为这可能是我缺乏代码对齐知识。
第二阶段从0x1000开始:
0x1000: cli
0x1001: xor eax,eax
0x1003: mov eax,0x1f1a
0x1008: mov esp,eax
0x100a: sti
0x100b: jmp 0x1010
第一个跳转到达0x1010(这是反汇编的C代码):
0x1010: push 0x16b4
0x1015: call 0x14ca <---
0x101a: add esp,0x4
0x101d: jmp 0x101d
对0x14CA的调用实际上位于0x000014c9,短两个字节。
就像上面的代码一样,我希望跳转或调用能够到达操作数地址,但总是会错过两个字节。
答案 0 :(得分:3)
这是一个疯狂的猜测,实际上可能是错误的。基于以下事实:在32位代码中,您编码的相对JMP和CALL指令为5字节,在16位代码中为3字节。 5个字节-3个字节= 2个字节。鉴于相对的JMP和CALL目标是基于距下一条指令开始的距离,因此它可能会提示发生了什么问题。
如果我使用此代码:
bits 32
org 0x1000
cli
xor eax,eax
mov eax,0x1f1a
mov esp,eax
sti
jmp 0x1010
push 0x16b4
call 0x14ca
add esp,0x4
jmp 0x101d
并使用以下命令进行组装:
nasm -f bin stage2.asm -o stage2.bin
并查看以下内容的32位解码:
ndisasm -b32 -o 0x1000 stage2.bin
我得到:
00001000 FA cli 00001001 31C0 xor eax,eax 00001003 B81A1F0000 mov eax,0x1f1a 00001008 89C4 mov esp,eax 0000100A FB sti 0000100B E900000000 jmp dword 0x1010 00001010 68B4160000 push dword 0x16b4 00001015 E8B0040000 call dword 0x14ca 0000101A 83C404 add esp,byte +0x4 0000101D E9FBFFFFFF jmp dword 0x101d
这看起来是正确的。但是,如果我使用以下方法解码与16位相同的代码:
ndisasm -b16 -o 0x1000 stage2.bin
我得到:
00001000 FA cli 00001001 31C0 xor ax,ax 00001003 B81A1F mov ax,0x1f1a 00001006 0000 add [bx+si],al 00001008 89C4 mov sp,ax 0000100A FB sti 0000100B E90000 jmp word 0x100e 0000100E 0000 add [bx+si],al 00001010 68B416 push word 0x16b4 00001013 0000 add [bx+si],al 00001015 E8B004 call word 0x14c8 00001018 0000 add [bx+si],al 0000101A 83C404 add sp,byte +0x4 0000101D E9FBFF jmp word 0x101b 00001020 FF db 0xff 00001021 FF db 0xff
指令解码不正确,但是存在JMP和CALL并进入错误的存储位置。这看起来非常像您所看到的观察结果。
我希望在您看不到代码的情况下,从0x1000开始执行阶段2时,您已经进入了32位保护模式。如果您还没有,那么我怀疑这是您问题的根源。我相信32位编码指令正在16位实模式下执行。
OP从评论中建议他们在进入虚幻模式的过程中进入了32位保护模式。他们相信,虚幻模式仍会将指令解码为32位代码,从而产生问题。
通过进入32位保护模式进入非实模式,然后返回到16位实模式。虚幻模式仍然是16位实模式,但隐藏描述符缓存中的限制设置为0xffffffff(4GiB限制)。返回到16位实模式后,您可以使用32位寻址直接在64KiB以外的段中对内存进行寻址,但是代码仍在16位实模式下运行。
如果您正在为16位虚幻模式编写代码,则编译器和汇编器仍需要生成16位代码。如果打算编写/生成32位代码,则不能选择使用虚幻模式,并且需要输入32位保护模式才能执行32位代码。