GDB的watch命令可精准捕获非法内存写入,应监视变量地址或具体地址(如watch (char)0x7ffff7ff8abc);若无法预设,可用x命令轮询比对内存变化。

用 watch 监视特定内存地址的写入行为
内存被覆盖的本质是某处代码向本不该写的地址执行了写操作。GDB 的 watch 命令能直接捕获这种“非法写入”,比事后查 dump 更准、更早。
关键不是监视变量名,而是监视其**内存位置是否被意外修改**:
- 若已知被覆盖的变量名(如
buf),用watch *(&buf)—— 注意取地址再解引用,确保监控的是该变量所占的整块内存起始位置 - 若只知道地址(比如从崩溃日志看到
0x7ffff7ff8abc被改写),直接watch *(char*)0x7ffff7ff8abc(按字节监视)或watch *(int*)0x7ffff7ff8abc(按整型监视) - 如果覆盖是批量发生的(如
memcpy越界),watch会停在第一次越界写入的位置,而非崩溃点——这才是真正的问题源头
用 x 命令反复比对内存内容变化
当无法提前设 watch(比如程序已运行、或目标地址动态分配),可手动轮询观察内存是否突变。
典型操作流程:
- 先用
print &target_var或info proc mappings确认目标地址(如0x7fffffffe040) - 用
x/16xb 0x7fffffffe040记下初始 16 字节原始值 - 单步执行(
n或s)或运行到可疑函数前后,再执行x/16xb 0x7fffffffe040对比 - 按
Enter可重复上一条x命令,快速查看连续内存段,适合扫数组或结构体
注意:x/4xw 和
看到的是同一块内存的不同切片,单位不一致会导致误判——比如把 4 字节整数当 1 字节字符读,就会看到完全不同的值。</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/gongju/2823" title="C++14">C++14</a>
<p>C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div>
<H3>结合 <code>backtrace 和崩溃前最后一次 x 快照
段错误(SIGSEGV)或总线错误(SIGBUS)发生时,GDB 已中断,但此时被覆盖的内存可能已被后续操作进一步污染。不能只看崩溃栈顶。
更可靠的做法是:
- 在疑似出问题的函数入口设断点(如
break function2),运行至该点 - 用
x查看关键缓冲区(如arr)的当前内容,记录地址和值 - 用
step进入循环体,每步后x一次,直到某次输出明显异常(如字符串末尾多出0xdeadbeef) - 此时立即
bt,确认调用上下文;再info registers看rdi/rsi是否指向了非法地址
很多覆盖问题发生在循环边界判断错误(如 i 应为 <code>i ),而 <code>bt 显示的只是越界后的崩溃点,不是越界发生的那一行。
容易忽略的底层约束:对齐、大小、符号表缺失
watch 和 x 不是万能的,实际中常因以下原因失效:
-
watch在某些架构(如 ARM64)或优化级别(-O2)下可能无法触发——因为编译器把变量存进寄存器,没写内存;必须加-O0 -g编译才能保证变量落内存 - 用
x/8xw查 8 个字,但目标地址未按 4 字节对齐(如地址是0x...abc),GDB 可能报Cannot access memory;此时换x/8xb或调整地址对齐 - 若程序是 stripped 的(无符号表),
print &var会失败,只能靠反汇编(disassemble)找指令中的立即数地址,再x那里
真正难定位的覆盖,往往卡在这类环境细节上:你以为地址是对的,其实它根本没在内存里;你以为值变了,其实只是你读的方式错了。

















