4. GDB寄存器查看与修改
任务目标
编写一个简单C程序,通过GDB在断点处查看寄存器值,并使用set $eax=...修改寄存器,观察修改后对程序执行流程或结果的影响,从而理解寄存器在程序执行中的作用。
环境与依赖
- Linux环境(本文以Ubuntu/Debian为例)
- GCC编译器(含
-g调试信息) - GDB调试器
示例程序
创建一个名为reg_demo.c的文件,内容如下:
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
int result;
// 断点位置1:查看a和b的寄存器映射
result = a + b; // 断点1
// 断点位置2:修改eax后观察result变化
printf("result = %d\n", result); // 断点2
return 0;
}
编译与调试
gcc -g -o reg_demo reg_demo.c
gdb ./reg_demo
GDB操作步骤
- 在GDB中设置断点:
break 8(对应result = a + b;行) - 运行程序:
run - 查看所有寄存器:
info registers - 查看特定寄存器:
info registers eax ebx ecx edx - 查看寄存器与变量关系:
info registers eax后,用print $eax确认值 - 单步执行:
next(执行完加法后,result已存入寄存器或内存) - 查看result当前值:
print result(预期为30) - 修改寄存器:
set $eax = 100(注意:此时eax可能已不再保存result,需结合反汇编确认)
验证修改寄存器对执行的影响
为了清晰演示寄存器修改的影响,我们使用一个更直接的场景——修改循环计数器或条件跳转相关寄存器。修改代码如下:
#include <stdio.h>
int main() {
int i;
for (i = 0; i < 5; i++) {
printf("i = %d\n", i);
}
return 0;
}
重新编译并调试:
gcc -g -o loop_demo loop_demo.c
gdb ./loop_demo
break 6(printf行)runinfo registers ecx——观察循环变量i可能存储在ecx或栈上,需用print i确认- 若i存储在寄存器中(如ecx),执行
set $ecx = 3,然后continue,观察循环次数变化 - 若i在栈上,则用
set {int} &i = 3修改变量,但本案例聚焦寄存器修改,因此需通过disassemble main查看汇编,找到i对应的寄存器
反汇编辅助定位寄存器
(gdb) disassemble main
在循环场景中,通常ecx或edx可能被用作计数器。通过汇编代码确认后,直接修改对应寄存器即可改变循环次数。
验证方法与判定标准
- 查看寄存器:
info registers能列出所有寄存器及其十六进制值,且eax等通用寄存器值在断点处与代码逻辑一致(如函数参数、返回值等)。 - 修改寄存器:使用
set $eax = 100后,立即用print $eax确认值变为100。 - 影响程序执行:在循环示例中,若将计数器寄存器改为大于等于5的值,程序将提前退出循环,输出行数减少;若改为负数或0,则循环不执行。观察输出即可判定修改生效。
- 注意:修改寄存器可能影响程序状态,若修改了返回地址或栈指针可能导致崩溃,这是正常现象,说明寄存器对执行路径有直接控制力。
常见问题
- 修改eax后程序无变化:可能因为该寄存器在当前断点处已不再被使用,或变量已存入内存。需结合
disassemble和info registers确认哪个寄存器正承载关键值。 - info registers输出过长:可用
info all-registers查看全部,或指定寄存器名查看单个。 - 修改浮点寄存器:使用
set $st0 = 3.14等,但需注意浮点栈寄存器用法不同。
预期结果示例
在循环示例中,若反汇编发现ecx保存循环变量,且断点处ecx=0,执行set $ecx=3后继续运行,程序将只打印i=3和i=4两行(因为循环条件i<5,修改后从3开始)。这是由代码逻辑直接推出的确定性结果,可用于验证修改是否生效。