Cir*_*四事件 7 x86 assembly gdb qemu disassembly
例如,使用BIOS打印a
到屏幕的引导扇区main.asm
:
org 0x7c00
bits 16
cli
mov ax, 0x0E61
int 0x10
hlt
times 510 - ($-$$) db 0
dw 0xaa55
Run Code Online (Sandbox Code Playgroud)
然后:
nasm -o main.img main.asm
qemu-system-i386 -hda main.img -S -s &
gdb -ex 'target remote localhost:1234' \
-ex 'break *0x7c00' \
-ex 'continue' \
-ex 'x/3i $pc'
Run Code Online (Sandbox Code Playgroud)
我明白了:
0x7c00: cli
0x7c01: mov $0x10cd0e61,%eax
0x7c06: hlt
Run Code Online (Sandbox Code Playgroud)
所以看起来它mov ax, 0x0E61
被解释为32位mov %eax
并且将下一条指令int 0x10
作为数据.
我怎么能告诉GDB这是16位代码?
也可以看看:
objdump
" https://www.sourceware.org/ml/gdb/2007-03/msg00308.html,如下所示:如何反汇编原始x86代码?也许这是在实施的同时实施的?Mic*_*tch 16
正如Jester在评论中正确指出的那样,你只需要在使用set architecture i8086
时使用gdb
它,以便它知道假定采用16位8086指令格式.您可以在此处了解gdb目标.
我将此作为答案添加,因为在评论中难以解释.如果单独组装和链接事物,则可以生成调试信息,然后可以使用它们gdb
来提供源级调试,即使远程执行16位代码也是如此.为此,我们稍微修改您的程序集文件:
;org 0x7c00 - remove as it may be rejected when assembling
; with elf format. We can specify it on command
; line or via a linker script.
bits 16
; Use a label for our main entry point so we can break on it
; by name in the debugger
main:
cli
mov ax, 0x0E61
int 0x10
hlt
times 510 - ($-$$) db 0
dw 0xaa55
Run Code Online (Sandbox Code Playgroud)
我添加了一些注释来确定所做的微不足道的更改.现在我们可以使用这些命令来汇编我们的文件,使其包含矮化格式的调试输出.我们将它链接到最终的精灵图像.此精灵图像可用于符号调试gdb
.然后我们可以将elf格式转换为平面二进制格式objcopy
nasm -f elf32 -g3 -F dwarf main.asm -o main.o
ld -Ttext=0x7c00 -melf_i386 main.o -o main.elf
objcopy -O binary main.elf main.img
qemu-system-i386 -hda main.img -S -s &
gdb main.elf \
-ex 'target remote localhost:1234' \
-ex 'set architecture i8086' \
-ex 'layout src' \
-ex 'layout regs' \
-ex 'break main' \
-ex 'continue'
Run Code Online (Sandbox Code Playgroud)
我做了一些小改动.我main.elf
在启动时使用该文件(带有符号信息)gdb
.
我还为汇编代码和寄存器添加了一些更有用的布局,这些寄存器可以使命令行上的调试更容易.我也打破了main
(不是地址).由于调试信息,我们的汇编文件中的源代码也应该出现.如果您更喜欢看原始装配layout asm
,layout src
则可以使用而不是.
这个一般概念可以在其他平台上使用NASM和LD支持的其他格式.elf32
并且elf_i386
必须针对特定环境修改调试类型.我的示例针对了解Linux Elf32二进制文件的系统.
不幸的是,默认情况下gdb
不执行segment:offset计算,并将使用EIP中的值作为断点.您必须将断点指定为32位地址(EIP).
当涉及到单步实模式代码时,它可能很麻烦,因为gdb
它不处理实模式分割.如果您进入中断处理程序,您将发现gdb
将显示相对于EIP的汇编代码.有效地gdb
将向您显示错误的内存位置的反汇编,因为它没有考虑到CS.值得庆幸的是,有人创建了一个GDB脚本来提供帮助.将脚本下载到您的开发目录,然后运行QEMU,例如:
qemu-system-i386 -hda main.img -S -s &
gdb -ix gdbinit_real_mode.txt main.elf \
-ex 'target remote localhost:1234' \
-ex 'break main' \
-ex 'continue'
Run Code Online (Sandbox Code Playgroud)
该脚本负责将架构设置为i8086,然后将其自身挂钩gdb
.它提供了许多新的宏,可以使16位代码更容易步进.
break_int:在软件中断向量上添加断点(好的旧MS DOS和BIOS公开其API的方式)
break_int_if_ah:在软件中断上添加条件断点.AH必须等于给定参数.这用于过滤中断的服务调用.例如,有时您只想在调用中断10h的函数AH = 0h时中断(更改屏幕模式).
stepo:这是一个用于'跳过'函数和中断调用的kabalistic宏.它是如何工作的 ?提取当前指令的操作码,如果是函数或中断调用,则计算"下一个"指令地址,在该地址上添加临时断点并调用"继续"函数.
step_until_ret:这用于单步执行,直到我们遇到'RET'指令.
step_until_iret:这用于单步执行,直到我们遇到'IRET'指令.
step_until_int:这用于单步执行,直到我们遇到'INT'指令.
此脚本还使用计算的分段打印出地址和寄存器.每次执行指令后的输出如下所示:
---------------------------[ STACK ]---
D2EA F000 0000 0000 6F62 0000 0000 0000
7784 0000 7C00 0000 0080 0000 0000 0000
---------------------------[ DS:SI ]---
00000000: 53 FF 00 F0 53 FF 00 F0 C3 E2 00 F0 53 FF 00 F0 S...S.......S...
00000010: 53 FF 00 F0 53 FF 00 F0 53 FF 00 F0 53 FF 00 F0 S...S...S...S...
00000020: A5 FE 00 F0 87 E9 00 F0 76 D6 00 F0 76 D6 00 F0 ........v...v...
00000030: 76 D6 00 F0 76 D6 00 F0 57 EF 00 F0 76 D6 00 F0 v...v...W...v...
---------------------------[ ES:DI ]---
00000000: 53 FF 00 F0 53 FF 00 F0 C3 E2 00 F0 53 FF 00 F0 S...S.......S...
00000010: 53 FF 00 F0 53 FF 00 F0 53 FF 00 F0 53 FF 00 F0 S...S...S...S...
00000020: A5 FE 00 F0 87 E9 00 F0 76 D6 00 F0 76 D6 00 F0 ........v...v...
00000030: 76 D6 00 F0 76 D6 00 F0 57 EF 00 F0 76 D6 00 F0 v...v...W...v...
----------------------------[ CPU ]----
AX: AA55 BX: 0000 CX: 0000 DX: 0080
SI: 0000 DI: 0000 SP: 6F2C BP: 0000
CS: 0000 DS: 0000 ES: 0000 SS: 0000
IP: 7C00 EIP:00007C00
CS:IP: 0000:7C00 (0x07C00)
SS:SP: 0000:6F2C (0x06F2C)
SS:BP: 0000:0000 (0x00000)
OF <0> DF <0> IF <1> TF <0> SF <0> ZF <0> AF <0> PF <0> CF <0>
ID <0> VIP <0> VIF <0> AC <0> VM <0> RF <0> NT <0> IOPL <0>
---------------------------[ CODE ]----
=> 0x7c00 <main>: cli
0x7c01: mov ax,0xe61
0x7c04: int 0x10
0x7c06: hlt
0x7c07: add BYTE PTR [bx+si],al
0x7c09: add BYTE PTR [bx+si],al
0x7c0b: add BYTE PTR [bx+si],al
0x7c0d: add BYTE PTR [bx+si],al
0x7c0f: add BYTE PTR [bx+si],al
0x7c11: add BYTE PTR [bx+si],al
Run Code Online (Sandbox Code Playgroud)
Mat*_*har 10
此处已经提供的答案是正确的,似乎与最新版本的gdb
和/或qemu
.
这是关于具有当前详细信息的源软件的未解决问题。
当在实模式下qemu
会协商错误的架构(i386)时,您需要覆盖它:
target remote ...
)set tdesc filename target.xml
通常,当你调试ELF
,PE
或任何其他目标文件GDB可以从文件标题推断架构。当您调试引导加载程序时,没有要读取的目标文件,因此您可以gdb
自己告诉架构(在引导加载程序的情况下,arch 将是i8086
):
set architecture <arch>
Run Code Online (Sandbox Code Playgroud)
注意:当附加到 qemu 虚拟机时,实际上不需要告诉 gdb 所需的架构,qemu 会通过协议为您协商这些信息
qXfer
。
如上所述,调试qemu
VMqemu
时实际上会将其架构协商为gdb
,当面向 32 位 x86 时,架构可能是i386
,这不是我们想要的实模式架构。
目前,gdb 中似乎存在一个问题,导致它在目标架构 (i386) 和用户提供的架构 (i8086) 之间选择最“功能强大的兼容架构”。由于gdb
认为i386
作为一个适当的超集i8086
,它使用它来代替。选择i386
会导致所有操作数默认为 32 位(而不是 16 位),这会导致反汇编器错误。
您可以通过指定target.xml
描述文件来覆盖目标架构:
set tdesc filename <file>
Run Code Online (Sandbox Code Playgroud)
我从源代码制作了这个描述文件,qemu
并将架构更改为 i8086。
它适用于:
set architecture i8086
Run Code Online (Sandbox Code Playgroud)
正如小丑所提到的。
set architecture
记录在: https: //sourceware.org/gdb/onlinedocs/gdb/Targets.html,我们可以通过以下方式获取目标列表:
set architecture
Run Code Online (Sandbox Code Playgroud)
(无参数)或 GDB 提示符上的 Tab 补全。
归档时间: |
|
查看次数: |
3732 次 |
最近记录: |