# 修改被调试程序的二进制文件
## 例子
#include <stdio.h>
#include <stdlib.h>
void drawing (int n)
{
if (n != 0)
puts ("Try again?\nAll you need is a dollar, and a dream.");
else
puts ("You win $3000!");
}
int main (void)
{
int n;
srand (time (0));
n = rand () % 10;
printf ("Your number is %d\n", n);
drawing (n);
return 0;
}
## 技巧
gdb不仅可以用来调试程序,还可以修改程序的二进制代码。
缺省情况下,gdb是以只读方式加载程序的。可以通过命令行选项指定为可写:
$ gcc -write ./a.out
(gdb) show write
Writing into executable and core files is on.
也可以在gdb中,使用命令设置并重新加载程序:
(gdb) set write on
(gdb) file ./a.out
接下来,查看反汇编:
(gdb) disassemble /mr drawing
Dump of assembler code for function drawing:
5 {
0x0000000000400642 <+0>: 55 push %rbp
0x0000000000400643 <+1>: 48 89 e5 mov %rsp,%rbp
0x0000000000400646 <+4>: 48 83 ec 10 sub $0x10,%rsp
0x000000000040064a <+8>: 89 7d fc mov %edi,-0x4(%rbp)
6 if (n != 0)
0x000000000040064d <+11>: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
0x0000000000400651 <+15>: 74 0c je 0x40065f <drawing+29>
7 puts ("Try again?\nAll you need is a dollar, and a dream.");
0x0000000000400653 <+17>: bf e0 07 40 00 mov $0x4007e0,%edi
0x0000000000400658 <+22>: e8 b3 fe ff ff callq 0x400510 <puts@plt>
0x000000000040065d <+27>: eb 0a jmp 0x400669 <drawing+39>
8 else
9 puts ("You win $3000!");
0x000000000040065f <+29>: bf 12 08 40 00 mov $0x400812,%edi
0x0000000000400664 <+34>: e8 a7 fe ff ff callq 0x400510 <puts@plt>
10 }
0x0000000000400669 <+39>: c9 leaveq
0x000000000040066a <+40>: c3 retq
End of assembler dump.
修改二进制代码(注意大小端和指令长度):
(gdb) set variable *(short*)0x400651=0x0ceb
(gdb) disassemble /mr drawing
Dump of assembler code for function drawing:
5 {
0x0000000000400642 <+0>: 55 push %rbp
0x0000000000400643 <+1>: 48 89 e5 mov %rsp,%rbp
0x0000000000400646 <+4>: 48 83 ec 10 sub $0x10,%rsp
0x000000000040064a <+8>: 89 7d fc mov %edi,-0x4(%rbp)
6 if (n != 0)
0x000000000040064d <+11>: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
0x0000000000400651 <+15>: eb 0c jmp 0x40065f <drawing+29>
7 puts ("Try again?\nAll you need is a dollar, and a dream.");
0x0000000000400653 <+17>: bf e0 07 40 00 mov $0x4007e0,%edi
0x0000000000400658 <+22>: e8 b3 fe ff ff callq 0x400510 <puts@plt>
0x000000000040065d <+27>: eb 0a jmp 0x400669 <drawing+39>
8 else
9 puts ("You win $3000!");
0x000000000040065f <+29>: bf 12 08 40 00 mov $0x400812,%edi
0x0000000000400664 <+34>: e8 a7 fe ff ff callq 0x400510 <puts@plt>
10 }
0x0000000000400669 <+39>: c9 leaveq
0x000000000040066a <+40>: c3 retq
End of assembler dump.
可以看到,条件跳转指令“je”已经被改为无条件跳转“jmp”了。
退出,运行一下:
$ ./a.out
Your number is 2
You win $3000!
详情参见[gdb手册](https://sourceware.org/gdb/onlinedocs/gdb/Patching.html#Patching)
## 贡献者
xmj
- 版权
- 内容
- 调整窗口大小
- 一个gdb会话中同时调试多个程序
- 调试已经运行的进程
- 在匿名空间设置断点
- 在程序地址上打断点
- 在程序入口处打断点
- 在函数的第一条汇编指令打断点
- 在文件行号上打断点
- 使用断点命令改变程序的执行
- 直接执行函数
- 为exec调用设置catchpoint
- 为fork调用设置catchpoint
- 为ptrace调用设置catchpoint
- 为系统调用设置catchpoint
- 为vfork调用设置catchpoint
- 改变字符串的值
- 配置gdb init文件
- 设置源文件查找路径
- 自动反汇编后面要执行的代码
- 显示程序原始机器码
- 显示将要执行的汇编指令
- 打印内存的值
- 退出正在调试的函数
- 为调试进程产生core dump文件
- 得到命令的帮助信息
- 忽略断点
- 信息显示
- 打印函数堆栈帧信息
- 列出函数的名字
- 查看信号处理信息
- 显示共享链接库信息
- 跳转到指定位置执行
- 显示汇编代码窗口
- 显示寄存器窗口
- 加载可执行程序和core dump文件
- 打印程序进程空间信息
- 在Solaris上使用maintenance命令查看线程信息
- 将源程序和汇编指令映射起来
- 修改PC寄存器的值
- 命令行选项的格式
- 信号发生时是否把信号丢给程序处理
- 修改被调试程序的二进制文件
- 支持预处理器宏信息
- 打印STL容器中的内容
- 打印数组的索引下标
- 打印ASCII和宽字符字符串
- 打印数组中任意连续元素值
- 按照派生类型打印对象
- 打印调用栈帧中变量的值
- 打印大数组中的内容
- 打印函数局部变量的值
- 打印程序动态分配内存的信息
- 打印进程内存信息
- 打印寄存器的值
- 信号发生时是否打印信号信息
- 打印源代码行
- 打印静态变量的值
- 查看线程信息
- 打印变量的类型和所在文件
- gdb退出时不显示提示信息
- 在gdb中执行cd和pwd命令
- 在gdb中执行shell命令和make
- 保存已经设置的断点
- 保存历史命令
- 选择函数堆栈帧
- 给程序发送信号
- 设置条件断点
- 打印尾调用堆栈帧信息
- 同时调试父进程和子进程
- 设置汇编指令格式
- 调试子进程
- 指定程序的输入输出设备
- 记录执行gdb的过程
- 输出信息多时不会暂停输出
- 每行打印一个结构体成员
- 设置被调试程序的参数
- 设置命令提示符
- 设置命令提示符
- 设置读观察点
- 设置读写观察点
- 只允许一个线程运行
- 按何种方式解析脚本文件
- 进入不带调试信息的函数
- 设置临时断点
- 设置变量的值
- 设置观察点只针对特定线程生效
- 设置观察点
- 显示gdb版权相关信息
- 不显示线程启动和退出信息
- 显示gdb版本信息
- 启动时不显示提示信息
- 是否进入带调试信息的函数
- 信号发生时是否暂停程序
- 替换查找源文件的目录
- 让catchpoint只触发一次
- 进入和退出图形化调试界面
- 向上或向下切换函数堆栈帧
- 使用“$_”和“$__”变量
- 使用“$_exitcode”变量
- 使用“$_siginfo”变量
- 使用“$_thread”变量
- 使用命令的缩写形式