使用 Far Ret 调试二进制文件切换代码段

逆向工程 数据库 小精灵 二进制 外壳代码 amd64
2021-06-30 07:20:57

GDB 似乎无法处理切换代码段的二进制文件。

使用pwntools*,生成retf用于切换到64 位代码段的32 位intel 二进制文件是微不足道的Linux 支持这一点,并且一切“工作正常”。

>>> print shellcraft.to_64bit()
    push 0x33
    jmp 2f
1:
    retf
2:
    call 1b

>>> print shellcraft.amd64.mov('rax', 0xdeadbeefcafebabe)
mov rax, 0xdeadbeefcafebabe

我们可以将这些组合成二进制:

>>> asm(shellcraft.to_64bit())
'j3\xeb\x01\xcb\xe8\xfa\xff\xff\xff'

>>> asm(shellcraft.amd64.mov('rax', 0xdeadbeefcafebabe), arch='amd64')
'H\xb8\xbe\xba\xfe\xca\xef\xbe\xad\xde'

并将其全部粘贴到 ELF 中:

>>> ELF.from_bytes('j3\xeb\x01\xcb\xe8\xfa\xff\xff\xff' + 'H\xb8\xbe\xba\xfe\xca\xef\xbe\xad\xde')
[*] '/var/folders/4h/rrwj8fpj1cqcfb83syr_f930008rd2/T/pwn-asm-NbnRYW/step3-elf'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8049000)
    RWX:      Has RWX segments

对于希望在不安装pwntools情况下重现此内容的 StackExchange 用户,完整的二进制数据是:

7f454c4601010100000000000000000002000300010000007490040834000000a000000000000000340020000200280003000200010000000000000000900408009004088900000089000000070000000010000051e57464000000000000000000000000000000000000000007000000100000006a33eb01cbe8faffffff48b8bebafecaefbeadde00002e7368737472746162002e7368656c6c636f64650000000000000000000000000000000000000000000000000000000000000000000000000000000000000b00000001000000070000007490040874000000150000000000000000000000010000000000000001000000030000000000000000000000890000001600000000000000000000000100000000000000

现在,当我们在 GDB 下运行它时,我们最终得到了我们期望的寄存器状态,但是没有办法看到扩展的寄存器,或者说服它切换架构——或者显示正确的反汇编!

$ gdb -q --nx --nh "/home/user/step3-elf"
Reading symbols from /home/user/step3-elf...(no debugging symbols found)...done.
(gdb) x/5i 0x8049074
   0x8049074:   push   $0x33
   0x8049076:   jmp    0x8049079
   0x8049078:   lret
   0x8049079:   call   0x8049078
   0x804907e:   dec    %eax
(gdb) b *0x8049074
Breakpoint 1 at 0x8049074
(gdb) r
Starting program: /home/user/step3-elf

Breakpoint 1, 0x08049074 in ?? ()
(gdb) si
0x08049076 in ?? ()
(gdb)
0x08049079 in ?? ()
(gdb)
0x08049078 in ?? ()
(gdb)
0x0804907e in ?? ()
(gdb) x/i $pc
=> 0x804907e:   dec    %eax
(gdb) i r eax
eax            0x0  0
(gdb) ni
0x08049088 in ?? ()
(gdb) i r eax
eax            0xcafebabe   -889275714
(gdb) set arch i386:x86-64
warning: Selected architecture i386:x86-64 is not compatible with reported target architecture i386
Architecture `i386:x86-64' not recognized.
The target architecture is set automatically (currently i386)

当 GDB 拒绝切换架构时,如何说服 GDB 显示正确的 64 位反汇编?即使info reg all没有正确显示所有寄存器(例如 RAX)。

* to_64bit() 不在 pwntools 的发布版本中

1个回答

由于 AMD64 中如何实现 32 位模式向后兼容,CPU 和操作系统可以轻松支持在 32 位和 64 位执行模式之间切换。这是一种要求,以避免在 64 位机器上复制大多数系统二进制文件以执行 32 位代码(这对于单独分发仍然很常见)。

对于调试器和某些进程操作工具而言,情况并非如此,在某些情况下,它们需要捆绑 64 位辅助进程或同时提供 32 位和 64 位二进制文​​件。在 32 位和 64 位执行模式之间切换确实只是一步之遥,但是通过调试器来支持要复杂得多。必须为调试器的内部状态(不是被调试进程的状态,某些 API 根本无法跨越进程位数边界等)提供转换/转换函数。

这并不是说这是不可能的,只是为了解释为什么这可能还不可用。