CTF-All-In-One/doc/2.2_gdb&peda.md

241 lines
12 KiB
Markdown
Raw Normal View History

2017-07-17 14:10:48 +07:00
# 2.1 gdb 和 peda 调试器
2017-08-06 21:46:54 +07:00
- [gdb 的组成架构](#gdb-的组成架构)
- [gdb 基本工作原理](#gdb-基本工作原理)
- [gdb 基本操作](#gdb-基本操作)
2017-08-16 09:14:24 +07:00
- [gdb-peda](#gdb-peda)
2017-08-23 19:36:52 +07:00
- [GEF/pwndbg](#gefpwndbg)
2017-08-06 21:46:54 +07:00
## gdb 的组成架构
![](../pic/2.2_gdb.png)
2017-08-08 23:09:42 +07:00
2017-08-06 21:46:54 +07:00
## gdb 基本工作原理
gdb 通过系统调用 `ptrace` 来接管一个进程的执行。ptrace 系统调用提供了一种方法使得父进程可以观察和控制其它进程的执行检查和改变其核心映像以及寄存器。它主要用来实现断点调试和系统调用跟踪。ptrace 系统调用的原型如下:
2017-08-07 22:01:04 +07:00
```
2017-08-06 21:46:54 +07:00
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
```
- **pid_t pid**:指示 ptrace 要跟踪的进程。
- **void *addr**:指示要监控的内存地址。
- **void *data**:存放读取出的或者要写入的数据。
- **enum __ptrace_request request**:决定了系统调用的功能,几个主要的选项:
2017-08-07 22:01:04 +07:00
- *PTRACE_TRACEME*:表示此进程将被父进程跟踪,任何信号(除了 `SIGKILL`)都会暂停子进程,接着阻塞于 `wait()` 等待的父进程被唤醒。子进程内部对 `exec()` 的调用将发出 `SIGTRAP` 信号,这可以让父进程在子进程新程序开始运行之前就完全控制它。
2017-08-06 21:46:54 +07:00
- *PTRACE_ATTACH*attach 到一个指定的进程,使其成为当前进程跟踪的子进程,而子进程的行为等同于它进行了一次 PTRACE_TRACEME 操作。但需要注意的是,虽然当前进程成为被跟踪进程的父进程,但是子进程使用 `getppid()` 的到的仍将是其原始父进程的 pid。
- *PTRACE_CONT*:继续运行之前停止的子进程。可同时向子进程交付指定的信号。
#### gdb 的三种调试方式
- 运行并调试一个新进程
- 运行 gdb通过命令行或 `file` 命令指定目标程序。
- 输入 `run` 命令, gdb 执行下面的操作:
- 通过 `fork()` 系统调用创建一个新进程
- 在新创建的子进程中执行操作:`ptrace(PTRACE_TRACEME, 0, 0, 0)`
- 在子进程中通过 `execv()` 系统调用加载用户指定的可执行文件
- attach 并调试一个已经运行的进程
- 用户确定需要进行调试的进程 PID
- 运行 gdb输入 `attach <pid>`gdb 将对指定进程执行操作:`ptrace(PTRACE_ATTACH, pid, 0, 0)`
- 远程调试目标机上新创建的进程
- gdb 运行在调试机上gdbserver 运行在目标机上,两者之间的通信数据格式由 gdb 远程串行协议Remote Serial Protocol定义
- RSP 协议数据的基本格式为: `$..........#xx`
- gdbserver 的启动方式相当于运行并调试一个新创建的进程
#### 断点的实现
断点的功能是通过内核信号实现的,在 x86 架构上,内核向某个地址打入断点,实际上就是往该地址写入断点指令 `INT 3`,即 `0xCC`。目标程序运行到这条指令之后会触发 `SIGTRAP` 信号gdb 捕获这个信号,并根据目标程序当前停止的位置查询 gdb 维护的断点链表,若发现在该地址确实存在断点,则可判定为断点命中。
2017-08-07 22:01:04 +07:00
2017-08-06 21:46:54 +07:00
## gdb 基本操作
2017-09-05 22:06:55 +07:00
使用 `-tui` 选项可以将代码显示在一个漂亮的交互式窗口中。
#### break -- b
- `break` 当不带参数时,在所选栈帧中执行的下一条指令处设置断点。
- `break <function>` 在函数体入口处打断点。
- `break <line>` 在当前源码文件指定行的开始处打断点。
- `break -N` `break +N` 在当前源码行前面或后面的 `N` 行开始处打断点,`N` 为正整数。
- `break <filename:line>` 在源码文件 `filename``line` 行处打断点。
- `break <filename:function>` 在源码文件 `filename``function` 函数入口处打断点。
- `break <address>` 在程序指令的地址处打断点。
- `break ... if <cond>` 设置条件断点,`...` 代表上述参数之一(或无参数),`cond` 为条件表达式,仅在 `cond` 值非零时停住程序。
#### info breakpoints -- i b
查看断点,观察点和捕获点的列表。用法:
- `info breakpoints [list…]`
- `info break [list…]`
`list…` 用来指定若干个断点的编号(可省略),可以是 `2` `1-3` `2 5` 等。
#### disable -- dis
禁用断点,参数使用空格分隔。不带参数时禁用所有断点。
- `disable [breakpoints] [list…]` `breakpoints``disable` 的子命令(可省略),`list…` 同 `info breakpoints` 中的描述。
#### enable
启用断点,参数使用空格分隔。不带参数时启用所有断点。
- `enable [breakpoints] [list…]` 启用指定的断点(或所有定义的断点)。
- `enable [breakpoints] once list…` 临时启用指定的断点。GDB 在停止您的程序后立即禁用这些断点。
- `enable [breakpoints] delete list…` 使指定的断点启用一次然后删除。一旦您的程序停止GDB 就会删除这些断点。等效于用 `tbreak` 设置的断点。
`breakpoints``disable` 中的描述。
#### clear
在指定行或函数处清除断点。参数可以是行号,函数名称或 `*` 跟一个地址。
- `clear` 当不带参数时,清除所选栈帧在执行的源码行中的所有断点。
- `clear <function>`, `clear <filename:function>` 删除在命名函数的入口处设置的任何断点。
- `clear <line>`, `clear <filename:line>` 删除在指定的文件指定的行号的代码中设置的任何断点。
- `clear <address>` 清除指定程序指令的地址处的断点。
#### delete -- d
删除断点。参数使用空格分隔。不带参数时删除所有断点。
- `delete [breakpoints] [list…]`
#### tbreak
设置临时断点。参数形式同 `break` 一样。当第一次命中时被删除。
#### watch
2017-09-06 11:32:33 +07:00
为表达式设置观察点。每当一个表达式的值改变时,观察点就会停止执行您的程序。
- `watch [-l|-location] <expr>` 如果给出了 `-l` 或者 `-location`,则它会对 `expr` 求值并观察它所指向的内存。
2017-09-05 22:06:55 +07:00
#### step -- s
2017-09-06 11:32:33 +07:00
单步执行程序,直到到达不同的源码行。
- `step [N]` 参数 `N` 表示执行 N 次(或由于另一个原因直到程序停止)。
2017-09-05 22:06:55 +07:00
#### reverse-step
2017-09-06 11:32:33 +07:00
反向步进程序,直到到达另一个源码行的开头。
- `reverse-step [N]` 参数 `N` 表示执行 N 次(或由于另一个原因直到程序停止)。
2017-09-05 22:06:55 +07:00
#### next -- n
2017-09-06 11:32:33 +07:00
单步执行程序,执行完子程序调用。
- `next [N]`
`step` 不同,如果当前的源代码行调用子程序,则此命令不会进入子程序,而是继续执行,将其视为单个源代码行。
2017-09-05 22:06:55 +07:00
#### reverse-next
2017-09-06 11:32:33 +07:00
反向步进程序,执行完子程序调用。
- `reverse-next [N]`
如果要执行的源代码行调用子程序,则此命令不会进入子程序,调用被视为一个指令。
2017-09-05 22:06:55 +07:00
#### return
2017-09-06 11:32:33 +07:00
您可以使用 `return` 命令取消函数调用的执行。如果你给出一个表达式参数,它的值被用作函数的返回值。
- `return <expression>``expression` 的值作为函数的返回值并使函数直接返回。
2017-09-05 22:06:55 +07:00
#### finish -- fin
2017-09-06 11:32:33 +07:00
执行直到选定的栈帧返回。
- `finish`
2017-09-05 22:06:55 +07:00
#### until -- u
2017-09-06 11:32:33 +07:00
执行程序直到大于当前栈帧或当前栈帧中的指定位置(与 `break` 命令相同的参数)的源码行。此命令常用于通过一个循环,以避免单步执行。
- `until <location>` 继续运行程序,直到达到指定的位置,或者当前栈帧返回。
2017-09-05 22:06:55 +07:00
#### continue -- c
2017-09-06 11:32:33 +07:00
在信号或断点之后,继续运行被调试的程序。
- `continue [N]`
如果从断点开始,可以使用数字 `N` 作为参数,这意味着将该断点的忽略计数设置为 `N - 1`(以便断点在第 N 次到达之前不会中断)。
2017-09-05 22:06:55 +07:00
#### print -- p
2017-09-06 11:32:33 +07:00
求表达式 expr 的值并打印。可访问的变量是所选栈帧的词法环境,以及范围为全局或整个文件的所有变量。
- `print [expr]`
- `print /f [expr]` 通过指定 `/f` 来选择不同的打印格式,其中 `f` 是一个指定格式的字母
2017-09-05 22:06:55 +07:00
#### x
2017-09-06 11:32:33 +07:00
检查内存。
- `x/nfu <addr>`
- `x <addr>`
`n`, `f`, 和 `u` 都是可选参数,用于指定要显示的内存以及如何格式化。
`addr` 是要开始显示内存的地址的表达式。
`n` 重复次数(默认值是 1指定要显示多少个单位`u` 指定)的内存值。
`f` 显示格式(初始默认值是 `x`),显示格式是 `print('x''d''u''o''t''a''c''f''s')` 使用的格式之一,再加 `i`(机器指令)。
`u` 单位大小,`b` 表示单字节,`h` 表示双字节,`w` 表示四字节,`g` 表示八字节。
2017-09-05 22:06:55 +07:00
#### display
2017-09-06 11:32:33 +07:00
每次程序停止时打印表达式 expr 的值。
- `display <expr>`
- `display/fmt <expr>`
- `display/fmt <addr>`
`fmt` 用于指定显示格式。对于格式 `i``s`,或者包括单位大小或单位数量,将表达式 `addr` 添加为每次程序停止时要检查的内存地址。
2017-09-05 22:06:55 +07:00
#### info display
2017-09-06 11:32:33 +07:00
打印自动显示的表达式列表,每个表达式都带有项目编号,但不显示其值。
#### undisplay
取消某些表达式在程序停止时自动显示。参数是表达式的编号(使用 `info display` 查询编号)。不带参数表示取消所有自动显示表达式。
2017-09-05 22:06:55 +07:00
#### disable display
2017-09-06 11:32:33 +07:00
禁用某些表达式在程序停止时自动显示。禁用的显示项目被再次启用。参数是表达式的编号(使用 `info display` 查询编号)。不带参数表示禁用所有自动显示表达式。
2017-09-05 22:06:55 +07:00
#### enable display
2017-09-06 11:32:33 +07:00
启用某些表达式在程序停止时自动显示。参数是重新显示的表达式的编号(使用 `info display` 查询编号)。不带参数表示启用所有自动显示表达式。
2017-09-05 22:06:55 +07:00
#### help -- h
2017-09-06 11:32:33 +07:00
打印命令列表。
- `help <class>` 您可以获取该类中各个命令的列表。
- `help <command>` 显示如何使用该命令的简述。
2017-09-05 22:06:55 +07:00
#### attach
2017-09-06 11:32:33 +07:00
挂接到 GDB 之外的进程或文件。将进程 ID 或设备文件作为参数。
- `attach <process-id>`
2017-09-05 22:06:55 +07:00
#### run -- r
2017-09-06 11:32:33 +07:00
启动被调试的程序。可以直接指定参数,也可以用 `set args` 设置(启动所需的)参数。还允许使用 `>`, `<`, 或 `>>` 进行输入和输出重定向。
2017-09-05 22:06:55 +07:00
#### backtrace -- bt
2017-09-06 11:32:33 +07:00
打印整个栈的回溯。
- `bt` 打印整个栈的回溯,每个栈帧一行。
- `bt n` 类似于上,但只打印最内层的 n 个栈帧。
- `bt -n` 类似于上,但只打印最外层的 n 个栈帧。
- `bt full n` 类似于 `bt n`,还打印局部变量的值。
> 注意:使用 gdb 调试时,会自动关闭 ASLR所以可能每次看到的栈地址都不变。
2017-09-05 22:06:55 +07:00
#### ptype
2017-09-06 11:32:33 +07:00
打印类型 TYPE 的定义。
- `ptype[/FLAGS] TYPE-NAME | EXPRESSION`
2017-09-05 22:06:55 +07:00
2017-09-06 11:32:33 +07:00
参数可以是由 `typedef` 定义的类型名, 或者 `struct STRUCT-TAG` 或者 `class CLASS-NAME` 或者 `union UNION-TAG` 或者 `enum ENUM-TAG`
2017-08-17 22:31:30 +07:00
2017-08-16 09:14:24 +07:00
## gdb-peda
2017-08-22 20:52:52 +07:00
#### 安装
2017-08-16 09:14:24 +07:00
```shell
2017-08-22 20:52:52 +07:00
$ git clone https://github.com/longld/peda.git ~/peda
$ echo "source ~/peda/peda.py" >> ~/.gdbinit
$ echo "DONE! debug your program with gdb and enjoy"
2017-08-16 09:14:24 +07:00
```
2017-08-18 19:51:19 +07:00
2017-09-05 22:06:55 +07:00
如果系统为 Arch Linux则可以直接安装
```shell
$ yaourt -S peda
```
2017-08-22 20:52:52 +07:00
#### peda命令
- `aslr` - 显示/设置 gdb 的 ASLR
- `checksec` - 检查二进制文件的安全选项
- `dumpargs` - 在调用指令停止时显示传递给函数的参数
- `dumprop` - 在特定的内存范围显示 ROP gadgets
- `elfheader` - 获取正在调试的 ELF 文件的头信息
- `elfsymbol` - 从 ELF 文件中获取没有调试信息的符号信息
- `lookup` - 搜索属于内存范围的地址的所有地址/引用
- `patch` - 使用字符串/十六进制字符串/整形数
- `pattern` - 生成,搜索或写入循环 pattern 到内存
- `procinfo` - 显示调试进程的 /proc/pid/
- `pshow` - 显示各种 PEDA 选项和其他设置
- `pset` - 设置各种PEDA选项和其他设置
- `readelf` - 获取 ELF 的文件头信息
- `ropgadget` - 获取二进制或库的常见 ROP gadgets
- `ropsearch` - 搜索内存中的 ROP gadgets
- `searchmem|find` - 搜索内存中的 pattern; 支持正则表达式搜索
- `shellcode` - 生成或下载常见的 shellcode
- `skeleton` - 生成 python exploit 代码模板
- `vmmap` - 在调试过程中获取段的虚拟映射地址范围
- `xormem` - 用一个 key 来对一个内存区域执行 XOR 操作
2017-08-23 19:36:52 +07:00
## GEF/pwndbg
2017-08-22 20:52:52 +07:00
除了 peda 外还有一些优秀的 gdb 增强工具,功能大致相同,可以看情况选用。
- [GEF](https://github.com/hugsy/gef) - Multi-Architecture GDB Enhanced Features for Exploiters & Reverse-Engineers
2017-08-22 20:53:40 +07:00
- [pwndbg](https://github.com/pwndbg/pwndbg) - Exploit Development and Reverse Engineering with GDB Made Easy