4. GDB 数据检查:打印变量、内存查看(x命令)、显示与自动显示、类型转换打印
在调试会话中,数据检查是定位逻辑错误与内存问题的核心环节。GDB 提供了从高层变量到原始内存字节的完整检查手段。本章将系统讲解 print、x、display 与类型转换打印,帮助你精准捕获程序运行时的数据状态。
4.1 打印变量:print 命令
print(简写 p)是 GDB 中最常用的数据检查命令。它接受表达式并输出当前值。
4.1.1 基本用法
(gdb) print var_name
$1 = 42
(gdb) p array[3]
$2 = 7
(gdb) p ptr->field
$3 = {x = 10, y = 20}
GDB 会为每个 print 结果分配一个历史编号($1, $2…),后续可直接引用:
(gdb) p $1 + 100
$4 = 142
4.1.2 格式化打印
使用 /格式 控制输出样式:
| 格式符 | 含义 | 示例 |
|---|---|---|
/x | 十六进制 | p/x 255 → 0xff |
/d | 有符号十进制 | p/d 0xff → 255 |
/u | 无符号十进制 | p/u -1 → 4294967295 |
/o | 八进制 | p/o 64 → 0100 |
/t | 二进制 | p/t 5 → 101 |
/c | 字符 | p/c 65 → 'A' |
/f | 浮点 | p/f 3 → 3.0 |
/s | 字符串(C风格) | p/s buf |
技巧:格式化打印不会修改变量的实际类型,只改变输出呈现方式。检查位掩码时
/t 和 /x 组合使用效果最佳。
4.2 内存查看:x 命令
x(examine)命令用于直接查看内存中的原始字节,格式为:
x /[数量][格式][单位大小] 地址
4.2.1 单位大小说明
| 单位符 | 含义 | 字节数 |
|---|---|---|
b | byte | 1 |
h | halfword | 2 |
w | word | 4 |
g | giant | 8 |
4.2.2 常用组合示例
(gdb) x/10xb buf # 查看 buf 前 10 个字节(十六进制)
0x7fffffffe000: 0x48 0x65 0x6c 0x6c 0x6f 0x00 0x00 0x00
0x7fffffffe008: 0x00 0x00
(gdb) x/5dw &arr # 查看 arr 前 5 个 word(十进制有符号)
0x7fffffffe010: 1 2 3 4 5
(gdb) x/3i $pc # 查看当前指令附近 3 条汇编指令
=> 0x4004f2 <main+10>: mov $0x0,%eax
0x4004f7 <main+15>: pop %rbp
0x4004f8 <main+16>: retq
注意:使用
x 访问非法地址会导致 Cannot access memory at address 0x... 错误。检查指针前先用 print ptr 确认地址有效性。
4.3 显示与自动显示
当需要反复检查某个变量时,手动输入 print 效率低下。GDB 提供 display 机制实现自动跟踪。
4.3.1 display 命令
(gdb) display /格式 表达式
(gdb) display /x counter
1: /x counter = 0x1a
(gdb) display /3i $pc
2: x/3i $pc
=> 0x4004f2 <main+10>: mov $0x0,%eax
0x4004f7 <main+15>: pop %rbp
0x4004f8 <main+16>: retq
每次程序暂停(单步、断点命中、异常),GDB 会自动打印所有已激活的 display 项。
4.3.2 管理 display 列表
| 命令 | 作用 |
|---|---|
info display | 列出所有 display 项及其编号、状态 |
undisplay 编号 | 删除指定编号的 display 项 |
disable display 编号 | 临时禁用(不删除) |
enable display 编号 | 重新启用 |
delete display | 删除所有 display 项 |
实战场景:调试循环时,设置
display i 和 display sum,每次 next 后自动输出迭代变量和累加器,快速定位边界错误。
4.4 类型转换打印
GDB 允许在 print 中使用 C 风格类型转换,临时改变变量解释方式。这在检查联合体、位域、指针偏移或强制类型转换结果时极为有用。
4.4.1 基本语法
(gdb) print (目标类型) 表达式
4.4.2 典型用例
// 将 int 指针当作 char* 查看第一个字节
(gdb) p *(char*)ptr
$5 = 0x48 'H'
// 将结构体指针转换为另一种结构体类型(检查继承/多态)
(gdb) p (struct base*)derived_ptr
$6 = (struct base *) 0x7fffffffe010
// 将整数解释为浮点数
(gdb) p (float)0x40490fdb
$7 = 3.14159274
// 检查联合体中的特定成员
(gdb) p (uint32_t)union_var
$8 = 0x12345678
4.4.3 与 whatis / ptype 配合
(gdb) whatis ptr
type = int *
(gdb) ptype ptr
type = int *
(gdb) ptype struct point
type = struct point {
int x;
int y;
}
whatis 只输出类型名,ptype 输出完整定义。结合类型转换打印,可以快速验证内存布局是否符合预期。
4.5 综合实战:数据检查定位缓冲区溢出
假设程序在 strcpy 后崩溃,使用数据检查命令定位:
(gdb) break strcpy
(gdb) run
Breakpoint 1, __strcpy_ia32 () at ../sysdeps/i386/i686/multiarch/strcpy.S:78
(gdb) print src
$1 = 0x804a008 "AAAA...AAA" # 超长字符串
(gdb) print dst
$2 = 0xbffff2c0 "short" # 目标缓冲区很小
(gdb) x/20xb dst # 查看目标缓冲区周围内存
0xbffff2c0: 0x73 0x68 0x6f 0x72 0x74 0x00 0xff 0xff
0xbffff2c8: 0xff 0xff 0x00 0x00 0x00 0x00 0x00 0x00
(gdb) display /x dst[0]@16 # 自动跟踪目标缓冲区前16字节
1: x/16xb dst
0xbffff2c0: 0x73 0x68 0x6f 0x72 0x74 0x00 0xff 0xff
0xbffff2c8: 0xff 0xff 0x00 0x00 0x00 0x00 0x00 0x00
(gdb) next # 执行 strcpy 后自动显示
1: x/16xb dst
0xbffff2c0: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0xbffff2c8: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
# 明显覆盖了返回地址区域
关键结论:通过
x 查看栈上内存布局,结合 display 自动跟踪,可以实时观察缓冲区溢出如何破坏相邻数据(包括返回地址)。这是手工逆向分析漏洞的经典方法。
4.6 本章小结
- print:灵活打印变量和表达式,支持多种格式符。
- x:直接查看内存原始字节,是检查数组、指针、栈布局的核心工具。
- display:自动跟踪关键变量,减少重复输入,提升调试效率。
- 类型转换打印:临时改变变量解释方式,适合联合体、位域、指针偏移等复杂场景。
- 组合使用这些命令,可以构建从高层变量到底层内存的完整数据检查链路。