如何使用“x / i $ pc”反汇编GDB中的16位x86引导扇区代码? 它被视为32位

例如,用BIOS打印到屏幕main.asm的引导扇区:

 org 0x7c00 bits 16 cli mov ax, 0x0E61 int 0x10 hlt times 510 - ($-$$) db 0 dw 0xaa55 

然后:

 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' 

我得到:

 0x7c00: cli 0x7c01: mov $0x10cd0e61,%eax 0x7c06: hlt 

所以看起来像mov ax, 0x0E61被解释为32位mov %eax ,并将下一条指令int 0x10作为数据mov ax, 0x0E61

我怎么能告诉GDB这是16位的代码?

也可以看看:

  • 在2007年,一个GDB的开发者回答“use objdump ” https://www.sourceware.org/ml/gdb/2007-03/msg00308.html ,解释如何反汇编原始的x86代码? 也许它是同时执行的?
  • 超集: 以16位模式使用GDB
  • 相似,但OP在那里有错误,所以也许这是别的? 我怎样才能用GDB反汇编win16

正如Jester在评论中正确地指出的那样,在使用gdb时只需要使用set architecture i8086 ,以便它知道采用16位8086指令格式。 你可以在这里了解gdb的目标。

我把这个作为答案join,因为在评论中很难解释。 如果分别汇编并链接,则可以生成debugging信息,然后gdb可以使用debugging信息来提供源代码级别的debugging,即使对16位代码进行远程debugging也是如此。 为此,我们稍微修改一下你的程序集文件:

 ;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 

我已经添加了一些意见,以确定所做的微小的更改。 现在我们可以使用像这样的命令来组装我们的文件,以便它包含矮格式的debugging输出。 我们把它链接到一个最终的精灵图像。 这个精灵图像可以用于gdb符号debugging。 然后,我们可以将elf格式转换为objcopy的平面二进制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' 

我做了一些小的改变。 启动gdb时使用main.elf文件(带有符号信息)。

我还为汇编代码添加了一些更有用的布局 ,并且可以使在命令行上进行debugging的寄存器更加容易。 我也打破main (而不是地址)。 由于debugging信息,我们的程序集文件中的源代码也应该出现。 您可以使用layout asm而不是layout src如果您想要查看原始程序集。

这个通用的概念可以在NASMLD在其他平台上支持的其他格式上工作。 elf32elf_i386以及debuggingtypes将不得不针对特定环境进行修改。 我的示例目标是了解Linux Elf32二进制文件的系统。


使用GDB / QEMUdebugging16位实模式引导程序

不幸的是,默认情况下, gdb不会执行segment:offset计算,并将使用EIP中的值作为断点。 您必须指定断点为32位地址( EIP )。

当涉及到实模式代码时,它可能很麻烦,因为gdb不处理实模式分割。 如果你进入一个中断处理程序,你会发现gdb会显示与EIP相关的汇编代码。 有效的gdb会显示你反汇编错误的内存位置,因为它没有考虑CS 。 幸运的是有人创build了一个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' 

脚本负责将体系结构设置为i8086,然后将自身挂接到gdb 。 它提供了一些新的macros ,可以简化16位代码。

break_int:在软件中断向量上添加一个断点(好的旧MS DOS和BIOS公开它们的API的方式)

break_int_if_ah:在软件中断上添加一个条件断点。 AH必须等于给定的参数。 这用于过滤中断的服务调用。 例如,当中断10h的函数AH = 0h被调用(改变屏幕模式)时,有时只想要中断。

stepo:这是一个kabalisticmacros,用于“跨越”function和中断呼叫。 它是如何工作的 ? 提取当前指令的操作码,如果是函数或中断调用,则计算“下一个”指令地址,在该地址上添加临时断点,并调用“继续”function。

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 

它适用于:

 set architecture i8086 

正如Jester所说的那样 。

set architecturelogging在: https : //sourceware.org/gdb/onlinedocs/gdb/Targets.html ,我们可以得到一个目标列表:

 set architecture 

(无参数)或GDB提示符上的制表符完成。