0x00 背景
以defcon 2016的kiss为例,介绍一点gdb使用技巧。
0x01 找到main函数
因为该binary被strip了,b main的时候无法找到main函数,无法设置断点。所以我们要用其他方法找到main函数。
- 使用
b write
,在进入write函数时设置断点,然后run
。 -
可以看到在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
- 我们可以看一下
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
- 打开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
- 根据这个基址和ida中的main函数的offset可以算出main函数的地址:
0x555555554000 + 0x00000000000008C0 = 0x5555555548c0
-
在gdb中用
b *0x5555555548c0
下断点,run
起来,可以看到停在了main函数中。
0x02 注意
-
disassemble addr
命令可以把addr
所在的函数全部显示(dump)出来,但是仅对有symbol的函数如库函数write起作用,对于symbol已经被strip了的main函数无法dump,只能用x/8i
这样的命令查看指令。 -
以上binary的地址特征是offset非常小,如
0x8c0
等,使用gdb运行时会被加上0x555555554000
这个基址,这样的特征是指定了-fPIC -pie
才有的。 -
即使你的OS开启了
ASLR
,在gdb中默认ASLR
也是关闭的,所以debug时每次的基址都一样,但是真正运行时地址还是会被随机化的。 -
即使不指定
-fPIC -pie
选项,只要开启了ASLR
地址就会被随机化。唯一不同的是地址的offset特征不一样,没有-fPIC -pie
选项的binary的offset一般为4006a0
这样。 -
关闭ASLR时,
-fPIC -pie
选项编译的二进制文件运行时会在objdump出的offset上加上一个基址0x555555554000
,而无-fPIC -pie
选项编译的二进制文件运行时则直接以它objdump出的静态offset运行,如4006a0
。