x/s调试字符串需先确认真实地址:std::string须查内部指针(如_M_dataplus._M_p),C风格字符串注意栈/堆生命周期,用x/16cb验字节、p var.c_str()更可靠,并留意ASLR、对齐、内存映射等底层细节。

x/s 是最直接的方式,但实际调试中经常显示不全、截断或报错——根本原因不是命令写错了,而是字符串地址没找对,或者内存里压根没存完整字符串。
怎么确认字符串真正存储的地址
别直接对 std::string 变量名用 x/s,C++ 的 std::string 是对象,不是裸指针。你看到的变量本身只存控制块(比如 __data_ 字段),真实字符数据在堆上。
- 先用
p &var看变量地址,再用ptype var确认类型;如果是std::string,得深入成员:比如p var._M_dataplus._M_p(GCC libstdc++)或p var.__r_.first().__data_(LLVM libc++) - 对 C 风格字符串(
char *或char[]),&arr或ptr本身通常就是地址,但要注意:如果是指向栈上局部数组,函数返回后地址就失效了 - 用
info proc mappings或vmmap(在支持的 GDB 版本中)确认该地址是否落在可读内存段内,避免Cannot access memory
x/s 显示不全或乱码怎么办
x/s 默认从地址开始读,直到遇到第一个 <p><code>x/s 默认从地址开始读,直到遇到第一个 \0 停止。但它不会校验前面是否有有效内容,也不处理嵌入式 \0 —— 所以常见现象是只显示前几个字符,或直接报错。
<p><code>x/s 默认从地址开始读,直到遇到第一个 \0 停止。但它不会校验前面是否有有效内容,也不处理嵌入式 \0 —— 所以常见现象是只显示前几个字符,或直接报错。 —— 所以常见现象是只显示前几个字符,或直接报错。
- 先用
x/16cb addr查看原始字节,确认\0位置和编码(比如是否含非 ASCII 字符) - 如果知道长度,用
x/<n>s addr</n>强制读取最多n字节(注意:n是字节数,不是字符数;UTF-8 下一个中文占 3 字节) - 遇到截断,试试
x/32xb addr看连续内存,手动定位字符串起始与结束边界 -
x/s不会自动跳过 padding 或结构体内存对齐空隙,所以对结构体成员字符串,必须用偏移后的真实地址,不能只靠&struct.member直接推
查看动态分配的字符串(malloc/new)
堆上字符串地址易变,且可能被释放,x/s 成功率低。关键不是“怎么查”,而是“查之前确保它还活着”。
- 在
free()或delete之前下断点,再执行x/s ptr - 用
heap命令(需安装gef或pwndbg)快速定位堆块头、检查size字段是否匹配预期长度 - 若怀疑越界写导致字符串头被破坏,用
x/8wx ptr-16往前看 16 字,常能发现被覆盖的 malloc header 或相邻变量 - 对
std::string,优先用p var.c_str()获取稳定指针,再x/s—— 比直接扒内部字段更可靠
容易被忽略的底层细节
小端序、ASLR、内存映射粒度这些不显眼的东西,往往让 x/s 看起来“时灵时不灵”。
- 地址末位是奇数?
x/s可能失败 —— 多数架构要求字符串起始地址对齐到字节边界,但某些 ARM 模式下未对齐访问会触发异常 - 程序开了 PIE?每次运行地址都变,
print &str的结果不能硬编码复用,要用符号+偏移方式重算 -
x/s读的是当前进程地址空间,不是 core dump 里的静态快照;如果从 core 加载,需确认gdb prog core中的内存映射是否完整加载(info target查看) - 状态延续(sticky formatting)会影响后续
x:比如上次用了x/4xw,再输x/10会默认按w单位读 10 个字 —— 这对字符串毫无意义,务必显式写x/10s


















