Ex1t's space

GDB在CTF解题中的一些使用技巧

字数统计: 990阅读时长: 4 min
2018/11/26 Share

当我拿到一道PWN题时,应该如何用GDB处理这个二进制程序。
我搭建了一个最轻量版的64位Ubuntu16的虚拟机(无图形界面)用来调试PWN的二进制程序
安装了GDB以及GDB的Peda插件,并且安装了32位的兼容

找到main函数

CTF的PWN题一般是经过了strip的,没有符号表,也不知道函数名
(但是不知道为什么IDA Pro就能识别出main函数,很奇怪,这个坑留到后面补)
所以想要调试题目,最先要做的事情就是找到程序的入口函数: main

最快的方式是直接使用 r(run)指令,跑一下这个程序,程序会在输入点循环等待输入

1
2
3
4
5
6
ubuntu@ubuntuMin:~$ gdb sendflag
gdb-peda$ r
Starting program: /home/ubuntu/sendflag
WELCOME TO PWN
I WILL SEND YOU FLAG
BUT YOU SHOULD SEND ME SOME 'a'

这个时候按下Ctrl+C手动中断这个程序,程序会中断在输入函数里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
gdb-peda$ r
Starting program: /home/ubuntu/sendflag
WELCOME TO PWN
I WILL SEND YOU FLAG
BUT YOU SHOULD SEND ME SOME 'a'
^C
Program received signal SIGINT, Interrupt.
[----------------------------------registers-----------------------------------]
EAX: 0xfffffe00
EBX: 0x0
ECX: 0x804b410 --> 0x0
EDX: 0x400
ESI: 0xf7fc85a0 --> 0xfbad2288
EDI: 0xf7fc8d60 --> 0xfbad2a84
EBP: 0xffffcf68 --> 0xffffd5b8 --> 0xffffd5f8 --> 0xffffd628 --> 0x0
ESP: 0xffffcf18 --> 0xffffcf68 --> 0xffffd5b8 --> 0xffffd5f8 --> 0xffffd628 --> 0x0
EIP: 0xf7fd9be9 (<__kernel_vsyscall+9>: pop ebp)
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0xf7fd9be3 <__kernel_vsyscall+3>: mov ebp,esp
0xf7fd9be5 <__kernel_vsyscall+5>: sysenter
0xf7fd9be7 <__kernel_vsyscall+7>: int 0x80
=> 0xf7fd9be9 <__kernel_vsyscall+9>: pop ebp
0xf7fd9bea <__kernel_vsyscall+10>: pop edx
0xf7fd9beb <__kernel_vsyscall+11>: pop ecx
0xf7fd9bec <__kernel_vsyscall+12>: ret
0xf7fd9bed: adc bh,bl
[------------------------------------stack-------------------------------------]
0000| 0xffffcf18 --> 0xffffcf68 --> 0xffffd5b8 --> 0xffffd5f8 --> 0xffffd628 --> 0x0
0004| 0xffffcf1c --> 0x400
0008| 0xffffcf20 --> 0x804b410 --> 0x0
0012| 0xffffcf24 --> 0xf7eec373 (<read+35>: pop ebx)
0016| 0xffffcf28 --> 0xf7fc8000 --> 0x1afdb0
0020| 0xffffcf2c --> 0xf7e81627 (<_IO_file_underflow+295>: add esp,0x10)
0024| 0xffffcf30 --> 0x0
0028| 0xffffcf34 --> 0x804b410 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGINT
0xf7fd9be9 in __kernel_vsyscall ()
gdb-peda$

程序中断之后,使用 bt (backtrack) 来回溯函数调用,打印每一级的返回地址,
从而可以确定call调用指令的地址(一般返回地址是call指令地址+1的位置)
我们可以找到一个叫做__libc_start_main ()的lib函数,这个函数的第一个参数就是main函数的地址,是用来调用main函数的。

1
int __cdecl __libc_start_main(int (__cdecl *main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void *stack_end);

可以看到__libc_start_main ()的返回地址在第九级: 0x080483b1,因为没有符号表所以显示in ?? ()

1
2
3
4
5
6
7
8
9
10
11
gdb-peda$ bt
#0 0xf7fd9be9 in __kernel_vsyscall ()
#1 0xf7eec373 in read () from /lib32/libc.so.6
#2 0xf7e81627 in _IO_file_underflow () from /lib32/libc.so.6
#3 0xf7e825e7 in _IO_default_uflow () from /lib32/libc.so.6
#4 0xf7e823dc in __uflow () from /lib32/libc.so.6
#5 0xf7e67429 in _IO_vfscanf () from /lib32/libc.so.6
#6 0xf7e7362e in __isoc99_scanf () from /lib32/libc.so.6
#7 0x080484dd in ?? ()
#8 0xf7e30637 in __libc_start_main () from /lib32/libc.so.6
#9 0x080483b1 in ?? ()

我们去看一下这个地址0x080483b1之前的汇编代码,GDB中disas(disasemble)命令用来将二进制反汇编出汇编代码
这里我们用disas start,+length的格式反汇编一段地址空间的数据
可以看到在call之前push了一个地址,根据32位linux的传参规则,最后一个push的是第一个参数,那么
0x804848b就可以确定是main的地址了

1
2
3
4
5
6
7
8
9
gdb-peda$ disas 0x080483b1-20,+20
Dump of assembler code from 0x804839d to 0x80483b1:
0x0804839d: test DWORD PTR [eax+ecx*1],eax
0x080483a0: push 0x8048540
0x080483a5: push ecx
0x080483a6: push esi
0x080483a7: push 0x804848b
0x080483ac: call 0x8048360 <__libc_start_main@plt>
End of assembler dump.

现在去看一下main,发现了几个PUT函数和scanf函数,找到了程序的核心代码,这个题目是个非常简单的溢出题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
disas 0x804848b,+100
Dump of assembler code from 0x804848b to 0x80484ef:
0x0804848b: lea ecx,[esp+0x4]
0x0804848f: and esp,0xfffffff0
0x08048492: push DWORD PTR [ecx-0x4]
0x08048495: push ebp
0x08048496: mov ebp,esp
0x08048498: push ecx
0x08048499: sub esp,0x14
0x0804849c: sub esp,0xc
0x0804849f: push 0x80485c0
0x080484a4: call 0x8048340 <puts@plt>
0x080484a9: add esp,0x10
0x080484ac: sub esp,0xc
0x080484af: push 0x80485cf
0x080484b4: call 0x8048340 <puts@plt>
0x080484b9: add esp,0x10
0x080484bc: sub esp,0xc
0x080484bf: push 0x80485e4
0x080484c4: call 0x8048340 <puts@plt>
0x080484c9: add esp,0x10
0x080484cc: sub esp,0x8
0x080484cf: lea eax,[ebp-0x10]
0x080484d2: push eax
0x080484d3: push 0x8048604
0x080484d8: call 0x8048370 <__isoc99_scanf@plt>
0x080484dd: add esp,0x10
0x080484e0: cmp DWORD PTR [ebp-0xc],0x61616161
0x080484e7: jne 0x804850b
0x080484e9: sub esp,0xc
0x080484ec: push 0x8048608

CATALOG
  1. 1. 找到main函数