如何使用“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
如果您想要查看原始程序集。
这个通用的概念可以在NASM和LD在其他平台上支持的其他格式上工作。 elf32
和elf_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 architecture
logging在: https : //sourceware.org/gdb/onlinedocs/gdb/Targets.html ,我们可以得到一个目标列表:
set architecture
(无参数)或GDB提示符上的制表符完成。