在不禁用 ASLR 的情况下为剥离的 PIE 二进制文件在 GDB 入口点设置断点

逆向工程 调试 数据库 小精灵
2021-07-06 02:11:41

给定一个与位置无关、静态链接、剥离的二进制文件,GDB 中似乎没有办法在不禁用 ASLR 的情况下在入口点设置断点。

  • break start 和类似的功能不起作用,因为没有符号信息
  • set stop-on-solib-events 1 不起作用,因为二进制文件不是动态链接的
  • break *0xdeadbeef 因为入口点不起作用,因为在二进制文件启动之前入口点是未解析的
  • catch load 不起作用,因为它不加载任何库
  • start不起作用,因为main未定义且未加载任何库

如果不修补二进制文件,我可以使用什么机制在第一条指令执行时中断?

可能的?

由于现在已删除的对该问题的回答说 PIE 静态链接的二进制文件是不可能的,一个简单的例子是链接器本身。

它是静态链接的。

$ ldd /lib/x86_64-linux-gnu/ld-2.19.so
    statically linked

它是可执行的。

$ strace /lib/x86_64-linux-gnu/ld-2.19.so
execve("/lib/x86_64-linux-gnu/ld-2.19.so", ["/lib/x86_64-linux-gnu/ld-2.19.so"], [/* 96 vars */]) = 0
brk(0)                                  = 0x7ff787b3d000
writev(2, [{"Usage: ld.so [OPTION]... EXECUTA"..., 1373}], 1Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]

它与位置无关。

$ readelf -h /lib/x86_64-linux-gnu/ld-2.19.so | grep DYN
  Type:                              DYN (Shared object file)

解决方案

看起来这可以通过使用一些可用的事件用 Python 来完成:http : //asciinema.org/a/19078

但是,我想要一个本机 GDB 解决方案。

_start在不禁用 ASLR 的情况下直接执行时,成功的解决方案将在 ld.so 中中断它应该是这样的:

sh $ strip -s /lib/x86_64-linux-gnu/ld-2.19.so -o ld.so
sh $ gdb ./ld.so
(gdb) $ set disable-randomization off
(gdb) $ <your magic commands>
(gdb) $ x/i $pc
=> 0x7f9ba515d2d0:     mov    rdi,rsp
(gdb) $ info proc map
process 10432
Mapped address spaces:

        Start Addr           End Addr       Size     Offset objfile
    0x7f9ba515c000     0x7f9ba517f000    0x23000        0x0 /lib/x86_64-linux-gnu/ld-2.19.so 
    0x7f9ba537e000     0x7f9ba5380000     0x2000    0x22000 /lib/x86_64- linux-gnu/ld-2.19.so
    0x7f9ba5380000     0x7f9ba5381000     0x1000        0x0 
    0x7fffc34c7000     0x7fffc38ca000   0x403000        0x0 [stack]
    0x7fffc398b000     0x7fffc398d000     0x2000        0x0 [vdso]
0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]
4个回答

更新:GDB 8.1 有一个starti命令,如下所述 /u/ruslan

在启动目标进程之前在未映射的地址上设置断点可以有效地做到这一点。这不是正确的功能,而是设置断点失败的副作用。

(gdb) break *0
Breakpoint 1 at 0x0
(gdb) r
Starting program: /home/user/ld.so 
Error in re-setting breakpoint 1: Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x0

Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x0

(gdb) x/i $pc
=> 0x7faae3a25cd0:      mov    rdi,rsp

从 GDB 8.1 开始,有一个特殊的命令:starti. GDB 会话示例:

$ gdb /bin/true
Reading symbols from /bin/true...(no debugging symbols found)...done.
(gdb) starti
Starting program: /bin/true 

Program stopped.
0xf7fdd800 in _start () from /lib/ld-linux.so.2
(gdb) x/5i $pc
=> 0xf7fdd800 <_start>: mov    eax,esp
   0xf7fdd802 <_start+2>:       call   0xf7fe2160 <_dl_start>
   0xf7fdd807 <_dl_start_user>: mov    edi,eax
   0xf7fdd809 <_dl_start_user+2>:       call   0xf7fdd7f0
   0xf7fdd80e <_dl_start_user+7>:       add    ebx,0x1f7e6

您可以定义 gdb 函数来中断 libc_star_main 的第一个参数。第一个 si/ni 是加载 libc 本身。把它放在你的 .gdbinit 文件中。

define bmain
    si
    ni
    b __libc_start_main
    c
    b *($rdi)
    c
end

只是偶然发现了这一点,并认为我会补充一点。

如果你想在一个剥离的、动态链接的程序的入口点停止,我推荐以下过程。

  1. 使用该文件启动 GDB。
  2. 启动程序,starti以便加载程序将其映射到内存中。(默认情况下,GDB 会为您的程序关闭 ASLR,但此方法无论哪种方式都有效。)
  3. 使用info file得到的入口点的地址。这将在启动后显示虚拟地址(这就是我们starti首先使用的原因)。
  4. 找到入口点并在该地址设置断点。
  5. 运行cont以到达断点。
$ gdb /usr/bin/ls
(gdb) starti
(gdb) info file
...
Local exec file:
    `/usr/bin/ls', file type elf64-x86-64.
    Entry point: 0x55555555a7d0
...
(gdb) b *0x55555555a7d0
(gdb) cont
...
Breakpoint 1, 0x000055555555a7d0 in ?? ()
(gdb) x/i $rip
=> 0x55555555a7d0:  endbr64 

现在,如果您想main在 Linux 上使用,请继续进行反汇编,直到您了解如何rdi设置(假设通常crt0.o已链接以启动 C 运行时)。

(gdb) 
   0x55555555a7f1:  lea    rdi,[rip+0xffffffffffffe5f8]        # 0x555555558df0

啊哈!我们可以main在 0x555555558df0 处找到(即使没有任何符号)。

(gdb) b *0x555555558df0
(gdb) cont

现在您在main. 留在这里以防其他人提出同样的问题。