gdb使用小技巧

0x00 背景

以defcon 2016的kiss为例,介绍一点gdb使用技巧。

0x01 找到main函数

因为该binary被strip了,b main的时候无法找到main函数,无法设置断点。所以我们要用其他方法找到main函数。

  1. 使用b write,在进入write函数时设置断点,然后run

  2. 可以看到在write函数的第一行程序停下了。

    => 0x7ffff7b00860 <write>:  
        cmp    DWORD PTR [rip+0x2d880d],0x0        # 0x7ffff7dd9074 <__libc_multiple_threads>
       0x7ffff7b00867 <write+7>:    jne    0x7ffff7b00879 <write+25>
       0x7ffff7b00869 <__write_nocancel>:   mov    eax,0x1
       0x7ffff7b0086e <__write_nocancel+5>: syscall 
       0x7ffff7b00870 <__write_nocancel+7>: cmp    rax,0xfffffffffffff001
    

    这个时候栈上的第一个数据应该是返回地址,所以使用

    x/4x $rsp
    

    查看栈上的第一个地址,结果为:

    0x7fffffffde58: 0x00005555555548dc  0x0000555555554e00
    0x7fffffffde68: 0x0000555555554b14  0x00007fffffffdf60
    
  3. 我们可以看一下0x00005555555548dc处的指令是什么:
    x/8i 0x00005555555548dc
    

    结果为:

    0x5555555548dc: lea    rdi,[rip+0x5bf]        # 0x555555554ea2
    0x5555555548e3: xor    esi,esi
    0x5555555548e5: xor    eax,eax
    0x5555555548e7: call   0x555555554880 <open@plt>
    0x5555555548ec: lea    rsi,[rsp+0x8]
    0x5555555548f1: mov    edx,0x8
    0x5555555548f6: mov    ebx,eax
    0x5555555548f8: mov    edi,eax
    
  4. 打开ida看一下,发现
    .text:00000000000008DC                 lea     rdi, file       ; "/dev/urandom"
    .text:00000000000008E3                 xor     esi, esi        ; oflag
    .text:00000000000008E5                 xor     eax, eax
    .text:00000000000008E7                 call    _open
    .text:00000000000008EC                 lea     rsi, [rsp+28h+buf] ; buf
    .text:00000000000008F1                 mov     edx, 8          ; nbytes
    .text:00000000000008F6                 mov     ebx, eax
    .text:00000000000008F8                 mov     edi, eax        ; fd
    

    这段代码正好与gdb中匹配。于是可以用这段代码的offset和gdb中的地址算出基址为:

    0x5555555548dc - 0x00000000000008DC = 0x555555554000
    
  5. 根据这个基址和ida中的main函数的offset可以算出main函数的地址:
    0x555555554000 + 0x00000000000008C0 = 0x5555555548c0
    
  6. 在gdb中用b *0x5555555548c0下断点,run起来,可以看到停在了main函数中。

0x02 注意

  1. disassemble addr命令可以把addr所在的函数全部显示(dump)出来,但是仅对有symbol的函数如库函数write起作用,对于symbol已经被strip了的main函数无法dump,只能用x/8i这样的命令查看指令。

  2. 以上binary的地址特征是offset非常小,如0x8c0等,使用gdb运行时会被加上0x555555554000这个基址,这样的特征是指定了-fPIC -pie才有的。

  3. 即使你的OS开启了ASLR,在gdb中默认ASLR也是关闭的,所以debug时每次的基址都一样,但是真正运行时地址还是会被随机化的。

  4. 即使不指定-fPIC -pie选项,只要开启了ASLR地址就会被随机化。唯一不同的是地址的offset特征不一样,没有-fPIC -pie选项的binary的offset一般为4006a0这样。

  5. 关闭ASLR时,-fPIC -pie选项编译的二进制文件运行时会在objdump出的offset上加上一个基址0x555555554000,而无-fPIC -pie选项编译的二进制文件运行时则直接以它objdump出的静态offset运行,如4006a0