assembly - ¿Cómo desmontar el código del sector de arranque x86 de 16 bits en GDB con "x/i $ pc"? Se trata como 32 bits.
qemu 16-bit (3)
Como Jester señaló correctamente en un comentario, solo necesita usar la
set architecture i8086
cuando usa
gdb
para que sepa asumir el formato de instrucción 8086 de 16 bits.
Puedes aprender sobre los objetivos de gdb
here
.
Estoy agregando esto como respuesta porque era demasiado difícil de explicar en un comentario.
Si ensambla y vincula las cosas por separado, puede generar información de depuración que luego puede ser utilizada por
gdb
para proporcionar una depuración a nivel de origen, incluso cuando se realiza de forma remota con código de 16 bits.
Para hacer esto, modificamos un poco 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 de modo que contenga resultados de depuración en formato enano.
Lo vinculamos a una imagen final de elfo.
Esta imagen de elfo se puede utilizar para la depuración simbólica de
gdb
.
Luego podemos convertir el formato elfo a 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.
Yo uso el archivo
main.elf
(con información simbólica) cuando inicio
gdb
.
También agrego algunos
layouts
más útiles para el código de ensamblaje 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
layout asm
lugar de
layout src
si prefiere ver el ensamblaje 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, deberán modificarse para el entorno específico.
Mi muestra apunta a sistemas que entienden los binarios de 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 segmento: desplazamiento y utilizará el valor en
EIP
para puntos de interrupción.
Debe especificar puntos de interrupción como direcciones de 32 bits (
EIP
).
Cuando se trata de recorrer el código en modo real, puede ser engorroso porque
gdb
no maneja la segmentación en modo real.
Si ingresa a un controlador de interrupciones, descubrirá que
gdb
mostrará el código de ensamblaje relativo a
EIP
.
Efectivamente,
gdb
le mostrará el desensamblaje de la ubicación de memoria incorrecta ya que no contaba con
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 establecer la arquitectura en i8086 y luego se conecta a
gdb
.
Proporciona una serie de
nuevas macros
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 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 utiliza para filtrar llamadas de servicio de interrupciones. Por ejemplo, a veces solo desea interrumpir cuando se llama a la función AH = 0h de la interrupción 10h (cambiar el modo de pantalla).
stepo: esta es una macro kabalística utilizada para ''pasar'' la función e interrumpir llamadas. Como funciona ? Se extrae el código de operación de la instrucción actual y, si es una función o una llamada de interrupción, se calcula la dirección de la instrucción "siguiente", se agrega un punto de interrupción temporal en esa dirección y se llama a la función ''continuar''.
step_until_ret: se usa para dar un solo paso hasta que encontremos una instrucción ''RET''.
step_until_iret: se usa para dar un solo paso hasta encontrar una instrucción ''IRET''.
step_until_int: se usa para dar un solo paso hasta encontrar una instrucción ''INT''.
Este script también imprime direcciones y registros con la segmentación calculada. La salida después de cada ejecución de instrucción se ve así:
---------------------------[ 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
Por ejemplo, con un sector de arranque que BIOS imprime 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
Por lo tanto, se parece al
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 como se explica en ¿Cómo desarmo el código x86 sin procesar? Tal vez se implementó 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 desmontar win16 con GDB?
Las respuestas ya proporcionadas aquí son correctas porque parecen comportarse mal con versiones recientes de
gdb
y / o
qemu
.
El es un problema abierto en el software fuente con los detalles actuales.
TL; DR
Cuando en modo real,
qemu
negociará la arquitectura incorrecta (i386), debe anularla:
- Descargue el archivo de descripción - target.xml ( target.xml )
-
Inicie gdb y conéctese a su objetivo (
target remote ...
) -
Establezca la arquitectura de destino utilizando el archivo de descripción:
set tdesc filename target.xml
Configuración de arquitectura en gdb
Normalmente, cuando depura un
ELF
,
PE
o cualquier otro archivo de objeto, gdb puede inferir la arquitectura de los encabezados de los archivos.
Cuando depura un cargador de arranque, no hay ningún archivo de objeto para leer, por lo que puede decirle a
gdb
la arquitectura usted mismo (en el caso de un arco de cargador de arranque será
i8086
):
set architecture <arch>
Nota: cuando se conecta a una máquina virtual qemu, en realidad no es necesario decirle a gdb la arquitectura deseada, qemu negociará esta información por usted a través del protocolo
qXfer
.
Anular la arquitectura de destino
Como se mencionó anteriormente al depurar
qemu
virtuales de
qemu
,
qemu
realmente negociará su arquitectura a
gdb
, cuando apunta a 32 bits x86, la arquitectura es probablemente
i386
, que no es la arquitectura que queremos para el modo real.
Actualmente parece haber un problema en gdb que hace que elija la "arquitectura compatible más característica" entre la arquitectura del objetivo (i386) y la arquitectura proporcionada por el usuario (i8086).
Debido a que
gdb
ve a
i386
como un superconjunto adecuado de
i8086
lo usa en su lugar.
Elegir
i386
hace que todos los operandos tengan 32 bits por defecto (en lugar de 16), esto es lo que causa los errores de desensamblador.
Puede anular la arquitectura de destino especificando un
archivo de descripción
target.xml
:
set tdesc filename <file>
Hice
target.xml
de las fuentes
qemu
y cambié la arquitectura a i8086.