4. GDB 数据检查:打印变量、内存查看(x命令)、显示与自动显示、类型转换打印

在调试会话中,数据检查是定位逻辑错误与内存问题的核心环节。GDB 提供了从高层变量到原始内存字节的完整检查手段。本章将系统讲解 print、x、display 与类型转换打印,帮助你精准捕获程序运行时的数据状态。

GDB 数据检查能力分层 变量层 print / ptype / whatis 内存层 x / 格式 地址 自动跟踪层 display / undisplay 说明: • 变量层:直接查看变量值、类型、结构体成员。 • 内存层:以字节为单位查看任意地址的原始内容,适合检查数组、缓冲区、指针指向区域。 • 自动跟踪层:每次程序暂停时自动打印指定表达式,无需手动输入。 • 类型转换打印:临时改变变量解释方式,用于检查联合体、位域或强制类型转换后的值。

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 单位大小说明

单位符含义字节数
bbyte1
hhalfword2
wword4
ggiant8

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 确认地址有效性。
x/8xb buf 内存布局示例 0x48 0x65 0x6c 0x6c 0x6f 0x00 0x00 0x00 buf+0 buf+1 buf+2 buf+3 buf+4 buf+5 buf+6 buf+7 解读: • 蓝色区域:字符串 "Hello" 的 ASCII 编码(0x48='H', 0x65='e', 0x6c='l', 0x6c='l', 0x6f='o')。 • 红色区域:字符串结束符 '\0'(0x00)。紫色区域:未初始化或填充字节。

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 输出完整定义。结合类型转换打印,可以快速验证内存布局是否符合预期。

类型转换打印工作流 原始变量 (int) 0x40490fdb 类型转换 (float) 0x40490fdb 打印结果 3.14159274 应用场景: • 检查联合体:将同一内存区域按不同成员类型解释。 • 验证指针偏移:将 char* 偏移量转换为 struct* 查看字段。

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:自动跟踪关键变量,减少重复输入,提升调试效率。
  • 类型转换打印:临时改变变量解释方式,适合联合体、位域、指针偏移等复杂场景。
  • 组合使用这些命令,可以构建从高层变量到底层内存的完整数据检查链路。