Qemu 和原始二进制文件
Qemu and Raw Binary File
我正在编译和 运行ning 二进制文件(引导扇区、阶段 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,短两个字节。
在上面的代码中,我希望跳转或调用到达操作数地址,但它总是错过两个字节。
这是一个大胆的猜测,实际上可能是错误的。它基于以下事实:在 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
并且 assemble 它具有:
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位保护模式进入虚模式,return进入16位实模式。虚幻模式仍然是 16 位实模式,只是隐藏描述符缓存中的限制设置为 0xffffffff(4GiB 限制)。一旦 return 进入 16 位实模式,您将能够使用 32 位寻址直接寻址超过 64KiB 的段中的内存,但代码在 16 位实模式下仍然是 运行。
如果您正在为 16 位虚幻模式编写代码,您的编译器和 assembler 仍然需要生成 16 位代码。如果您打算 write/generate 32 位代码,那么虚幻模式不是一个选项,您需要进入 32 位保护模式才能执行 32 位代码。
我正在编译和 运行ning 二进制文件(引导扇区、阶段 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,短两个字节。
在上面的代码中,我希望跳转或调用到达操作数地址,但它总是错过两个字节。
这是一个大胆的猜测,实际上可能是错误的。它基于以下事实:在 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
并且 assemble 它具有:
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位保护模式进入虚模式,return进入16位实模式。虚幻模式仍然是 16 位实模式,只是隐藏描述符缓存中的限制设置为 0xffffffff(4GiB 限制)。一旦 return 进入 16 位实模式,您将能够使用 32 位寻址直接寻址超过 64KiB 的段中的内存,但代码在 16 位实模式下仍然是 运行。
如果您正在为 16 位虚幻模式编写代码,您的编译器和 assembler 仍然需要生成 16 位代码。如果您打算 write/generate 32 位代码,那么虚幻模式不是一个选项,您需要进入 32 位保护模式才能执行 32 位代码。