GDB调试基础
ssooking Lv5

GDB调试

GDB即GUN Debugger,是linux上的标准调试器,常配合gef、peda等插件来使用。

为方便调试,可以给gdb安装两个常用插件:gef和peda

另外,推荐radware2和cutter调试工具。

编译程序

入门调试,本次调试一个简单的c程序,对程序运行过程中寄存器和栈的变化进行分析。c源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//test4.c
#include <stdio.h>
int add(int a, int b){
int result = a + b;
return result;
}
int main(){
int a = 10;
int b = 20;
char c = 'L';
float d = 2.33;
add(a, b);
return 0;
}

编译程序

1
gcc hellogdb.c -o hellogdb 

汇编文件编译

将汇编保存在一个名为max.s的文件中,并使用以下命令进行编译:

1
2
$ as max.s -o max.o
$ ld max.o -o max

调试程序

1
gdb ./filename

使用gdb ./filename命令打开待调试文件,也可以进入gdb后使用file filename加载待调试文件。如果安装了GEF,将会显示gef>提示符。

根据二进制文件、核心文件或进程ID启动GDB:

附加到一个进程:$ gdb -pid $(pidof <process>)

调试二进制代码:$ gdb ./file

1
2
$ gdb -q /path/to/my/bin
gef➤ gef help

远程调试:

1
2
remote:~ $ gdbserver 0.0.0.0:1234/path/to/file
Running as PID: 666

还有:

1
2
local:~ $ gdb -q
gef➤ gef-remote -t your.ip.address:1234 -p 666

查看代码

1
list 1,30		//查看前30行代码

注意,只有使用gcc -g参数编译的程序,才能在调试时看源代码。

下断点

让程序在指定函数如main、fread等处停下,以便调试。

1
2
gef> b main
gef> b *<address> #在指定内存地址处停下

其中指令 b 即 breakpoint 的缩写,与使用 breakpoint 效果一样;main 是指程序的main函数。即在main函数处下断点。下断点的方式有多种,使用起来也非常灵活。

如果使用gcc -g参数编译的程序,在使用list xxx命令查看源码时,直接在某行代码处下断点。

其他断点操作命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
info break
del <number> (删除编号为x的断点)
delete (删除所有断点)
delete <range>(删除指定编号范围内的断点)

break (or just b) <function-name>
break <line-number>
break filename:function
break filename:line-number
break *<address>
break +<offset>
break –<offset>
tbreak (设置一个临时断点)
disable/enable <breakpoint-number-or-range> (不删除断点,只是启用/禁用它们)
continue (or just c) – (继续执行,直到下一个断点)
continue <number> (继续,但忽略当前断点指定次数。对循环内的断点非常有用)
finish 继续,直至函数末尾

查看程序信息

起始地址

1
2
3
4
5
6
7
8
$ nm hellogdb | grep _start
0804a018 B __bss_start
0804a010 D __data_start
0804a010 W data_start
w __gmon_start__
08049f08 t __init_array_start
U __libc_start_main@@GLIBC_2.0
080482e0 T _start
1
2
3
(gdb) backtrace 	 #查看函数调用的顺序

(gdb) info args #查看当前函数参数的值*

寄存器信息

info registersi r命令检查寄存器的值 。

内存信息

examine 简写 x,以各种格式显示变量内容。

使用gdb> help x命令来查看使用方式

语法 : x/<number><format><unit>

参数解释:

  • number:要显示的内存单元个数

  • format:选择显示内容的格式,支持如下格式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    t(binary)						二进制
    o(octal) 八进制
    d(decimal) 十进制
    x(hex) 十六进制
    a(address) 内存地址
    c(char) 字符形
    f(float) 浮点数
    s(string) 字符串
    i(instruction) 指令形式
    u(unsigned decimal) 无符号十进制
  • unit:每个内存单元的大小,以字节计算

    1
    2
    3
    4
    b (byte)				1 bytes
    h (half word) 2 bytes
    w (word) 4 bytes
    g (giant) 8 bytes

常用举例

1
2
3
4
5
gef> x/10i $sp			当前栈
gef> x/16xw $pc
gef> x/16xw $esp

print $buf

查看反汇编代码

disassemble命令可以查看函数的反汇编输出。 如查看main函数:disassemble main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
gef➤  disassemble main
Dump of assembler code for function main:
0x080483f1 <+0>: push ebp
0x080483f2 <+1>: mov ebp,esp
0x080483f4 <+3>: sub esp,0x10
0x080483f7 <+6>: mov DWORD PTR [ebp-0xc],0xa
0x080483fe <+13>: mov DWORD PTR [ebp-0x8],0x14
0x08048405 <+20>: mov BYTE PTR [ebp-0xd],0x4c
0x08048409 <+24>: fld DWORD PTR ds:0x80484b0
0x0804840f <+30>: fstp DWORD PTR [ebp-0x4]
0x08048412 <+33>: push DWORD PTR [ebp-0x8]
0x08048415 <+36>: push DWORD PTR [ebp-0xc]
0x08048418 <+39>: call 0x80483db <add>
0x0804841d <+44>: add esp,0x8
0x08048420 <+47>: mov eax,0x0
0x08048425 <+52>: leave
0x08048426 <+53>: ret
End of assembler dump.

x/i 反汇编 – 通常,我们会使用 x/10i $eip-20 来查看当前的汇编($eip是指令寄存器)

以16进制的形式打印当前程序中的变量tf里面的内容

(gdb) p /x *tf

执行程序

正常执行

1
2
run
run 参数

Crash执行

1
2
3
run $(python -c "A"*27+)

run $(python -c 'print " A" *17 + " \x1b\x84\x04\x08" ')

单步调试

用于单步调试的命令:单步执行下一条命令。可以进入函数内部

1
2
3
stepi
s
step <number-of-steps-to-perform>

执行下一行代码。不会进入函数内部

1
2
3
nexti
n
next <number>

继续处理,直到达到指定的行号、函数名称、地址、文件名函数或文件名:行号

1
2
until
until <line-number>

显示当前行号以及所在的函数:where

gef> nexti 5

溢出测试

1
$ ./bufferoverflow $(python -c 'print " A" *17 + " \x1b\x84\x04\x08" ')

文件型:

1
python -c 'print " A" *17 + "\x1b\x84\x04\x08"' > input.txt

命令速查

二进制入门–动态跟踪源代码和反汇编代码

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
#!bash
--------terminal-----------
gdb
help
help data
help stack
help status
-----------end-------------
gdb stack0 #===>gdb + file stack0
l(list) #show source
b 7(break 7) #add break on line7
b 8(break 8) #add break on line8
b 11
b 13
b 15
info b(info break)
info registers #show registers
r(run)
disas /m
/r #show hex(显示汇编指令对应十六进制值)
/m #show source if available(如果有源码,显示对应行源码)
set disassembly-flavor att/intel #设置att或intel格式
set disassemble-next-line #设置下一步是否显示汇编码
si(setpi) #==> step into,like f7 in od
ni(nexti) #==> like f8 in od
leave #立即中断当前函数运行并返回,当前函数的剩余语句将不被执行
finish #执行到当前函数返回之后停止,当前函数的剩余语句将正常运行
bt(backtrace) #查看所有栈帧信息
f num(frame num) #选择并打印第num个栈帧信息
info f(info frame) #查看当前栈帧信息

Refenence

https://azeria-labs.com/debugging-with-gdb-introduction/

https://www.cnblogs.com/HacTF/p/7773750.html

https://bbs.pediy.com/thread-250772.htm

http://www.cppblog.com/baby-fly/archive/2010/07/27/121395.html

  • Post title:GDB调试基础
  • Post author:ssooking
  • Create time:2019-12-16 00:00:00
  • Post link:https://ssooking.github.io/2019/12/gdb调试基础/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.