# 2.1 gdb 和 peda 调试器 - [gdb 的组成架构](#gdb-的组成架构) - [gdb 基本工作原理](#gdb-基本工作原理) - [gdb 基本操作](#gdb-基本操作) - [gdb-peda](#gdb-peda) ## gdb 的组成架构 ![](../pic/2.2_gdb.png) ## gdb 基本工作原理 gdb 通过系统调用 `ptrace` 来接管一个进程的执行。ptrace 系统调用提供了一种方法使得父进程可以观察和控制其它进程的执行,检查和改变其核心映像以及寄存器。它主要用来实现断点调试和系统调用跟踪。ptrace 系统调用的原型如下: ``` #include 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**:决定了系统调用的功能,几个主要的选项: - *PTRACE_TRACEME*:表示此进程将被父进程跟踪,任何信号(除了 `SIGKILL`)都会暂停子进程,接着阻塞于 `wait()` 等待的父进程被唤醒。子进程内部对 `exec()` 的调用将发出 `SIGTRAP` 信号,这可以让父进程在子进程新程序开始运行之前就完全控制它。 - *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 `,gdb 将对指定进程执行操作:`ptrace(PTRACE_ATTACH, pid, 0, 0)` - 远程调试目标机上新创建的进程 - gdb 运行在调试机上,gdbserver 运行在目标机上,两者之间的通信数据格式由 gdb 远程串行协议(Remote Serial Protocol)定义 - RSP 协议数据的基本格式为: `$..........#xx` - gdbserver 的启动方式相当于运行并调试一个新创建的进程 #### 断点的实现 断点的功能是通过内核信号实现的,在 x86 架构上,内核向某个地址打入断点,实际上就是往该地址写入断点指令 `INT 3`,即 `0xCC`。目标程序运行到这条指令之后会触发 `SIGTRAP` 信号,gdb 捕获这个信号,并根据目标程序当前停止的位置查询 gdb 维护的断点链表,若发现在该地址确实存在断点,则可判定为断点命中。 ## gdb 基本操作 ## gdb-peda gdb-peda插件是 ### 安装 ```shell git clone https://github.com/longld/peda.git ~/peda echo "source ~/peda/peda.py" >> ~/.gdbinit echo "DONE! debug your program with gdb and enjoy" ``` ### peda命令