¿Cómo desensamblar el código de sector de arranque x86 de 16 bits en GDB con “x / i $ pc”? Se trata como de 32 bits

Por ejemplo, con un sector de arranque, el BIOS imprime a en la pantalla main.asm :

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

Entonces:

 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' 

Yo obtengo:

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

Así que parece que el mov ax, 0x0E61 se interpretó como un mov %eax 32 bits y consumió la siguiente instrucción int 0x10 como datos.

¿Cómo puedo decirle a GDB que este es un código de 16 bits?

Ver también:

  • en 2007, un desarrollador de GDB respondió “use objdump ” https://www.sourceware.org/ml/gdb/2007-03/msg00308.html tal como se explica en ¿Cómo desmonte el código x86 sin formato? Tal vez fue implementado mientras tanto?
  • superconjunto: uso de GDB en modo de 16 bits
  • similar, pero el OP tiene un error allí, entonces tal vez es algo más? ¿Cómo puedo desensamblar win16 con GDB?

Como Jester señaló correctamente en un comentario, solo necesita usar la set architecture i8086 cuando usa gdb para que sepa que debe asumir el formato de instrucción 8086 de 16 bits. Puede obtener más información sobre los objectives de gdb aquí .

Estoy agregando esto como respuesta porque era muy difícil de explicar en un comentario. Si ensambla y vincula cosas por separado, puede generar información de depuración que luego puede usar gdb para proporcionar depuración a nivel de fuente, incluso cuando se realiza de forma remota contra código de 16 bits. Para hacer esto, modificamos ligeramente su archivo de ensamblaje:

 ;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 

He agregado algunos comentarios para identificar los cambios triviales realizados. Ahora podemos usar comandos como estos para ensamblar nuestro archivo para que contenga resultados de depuración en el formato enano. Lo vinculamos a una imagen elf final. Esta imagen de duende se puede utilizar para la depuración simbólica por gdb . Podemos convertir el formato elf en un binario plano con 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' 

He hecho algunos cambios menores. Utilizo el archivo main.elf (con información simbólica) al iniciar gdb .

También agrego algunos diseños más útiles para el código de ensamblado y los registros que pueden facilitar la depuración en la línea de comandos. También rompo en main (no en la dirección). El código fuente de nuestro archivo de ensamblaje también debería aparecer debido a la información de depuración. Puede usar el layout asm lugar del layout src si prefiere ver el ensamblado sin formato.

Este concepto general puede funcionar en otros formatos compatibles con NASM y LD en otras plataformas. elf32 y elf_i386 , así como el tipo de depuración tendrán que ser modificados para el entorno específico. Mi muestra se dirige a sistemas que entienden los binarios Linux Elf32.


Depuración del gestor de arranque en modo real de 16 bits con GDB / QEMU

Desafortunadamente, por defecto, gdb no hace cálculos de desplazamiento de segmento: utilizará el valor en EIP para puntos de corte. Debe especificar puntos de interrupción como direcciones de 32 bits ( EIP ).

Cuando se trata de recorrer el código de modo real, puede ser engorroso porque gdb no maneja la segmentación en modo real. Si ingresa en un manejador de interrupciones, descubrirá que gdb mostrará el código de ensamblaje relacionado con EIP . Efectivamente, gdb le mostrará el desassembly de la ubicación incorrecta de la memoria, ya que no tuvo en cuenta la CS . Afortunadamente, alguien ha creado un script GDB para ayudar. Descargue el script en su directorio de desarrollo y luego ejecute QEMU con algo como:

 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' 

El script se encarga de configurar la architecture en i8086 y luego se engancha en gdb . Proporciona varias macros nuevas que pueden facilitar el paso por el código de 16 bits.

break_int: agrega un punto de interrupción en un vector de interrupción de software (la forma en que el viejo MS DOS y el BIOS exponen sus API)

break_int_if_ah: agrega un punto de interrupción condicional en una interrupción de software. AH tiene que ser igual al parámetro dado. Esto se usa para filtrar llamadas de servicio de interrupciones. Por ejemplo, a veces solo quiere interrumpir cuando se llama a la función AH = 0h de la interrupción 10h (cambiar el modo de pantalla).

stepo: se trata de una macro kabalística utilizada para la función ‘step-over’ e interrupción de llamadas. Como funciona ? El código de operación de la instrucción actual se extrae y si se trata de una llamada de función o de interrupción, se computa la dirección de instrucción “siguiente”, se agrega un punto de interrupción temporal en esa dirección y se invoca la función ‘continuar’.

step_until_ret: esto se usa para dar un solo paso hasta que encontremos una instrucción ‘RET’.

step_until_iret: esto se usa para dar un solo paso hasta que encontremos una instrucción ‘IRET’.

step_until_int: esto se usa para dar un solo paso hasta que encontramos una instrucción ‘INT’.

Esta secuencia de comandos también imprime direcciones y registros con segmentación calculada en. Salida después de la ejecución de cada instrucción:

 ---------------------------[ 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 
: 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

Funciona con:

 set architecture i8086 

como lo menciona Jester .

set architecture está documentada en: https://sourceware.org/gdb/onlinedocs/gdb/Targets.html y podemos obtener una lista de objectives con:

 set architecture 

(sin argumentos) o completar la pestaña en el prompt de GDB.