栈内存读取与缓冲区溢出疑问:为何未读到相邻buf2的值?
我写了一段带缓冲区溢出漏洞的代码,当输入小于16的参数时,程序会输出超出预期的内容。我定义了三个栈上的缓冲区buf、buf2和buf3,认为它们是连续存储的,但程序输出的是随机值而非我预期的buf2的值(我本以为能得到buf2里的1)。试了不同输入值,程序始终输出全0,这是为什么?栈上的缓冲区难道不是连续的吗?
附代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 16 unsigned char *isThisVulnerable(unsigned char* src, int size) { unsigned char* retval = (unsigned char*)malloc(size*sizeof(unsigned char)); return memcpy((void*)retval, (const void*)src, (size_t)size); } int main(int argc, char **argv) { int size = SIZE, i = SIZE, k = 0; if(argc > 1) { size = atoi(argv[1]); } unsigned char buf2[size]; unsigned char buf[size]; unsigned char buf3[size]; for(k=0;k<size;k++){ buf[k] = 0; buf2[k] = 1; buf3[k] = 2; } unsigned char *result = (unsigned char*)isThisVulnerable(buf, i); printf("%02X", result[0]); for(k=1;k<i;k++) { if(k%16 == 0) printf("\n"); else if(k%4 == 0) printf("\t"); printf("%02X", result[k]); } printf("\n\n"); return 0; }
1. 你没触发栈上的缓冲区溢出
你的代码里所谓的“溢出”根本不是发生在栈上。isThisVulnerable函数是用malloc在堆上分配内存,然后把buf的内容拷贝过去——当你传入小于16的size时,memcpy会从buf开始拷贝16字节,这时候溢出的是堆上的retval缓冲区,和栈上的buf2完全不沾边。你打印的是堆上的result内容,自然看不到buf2的值。
2. 栈上变量的存储顺序无保证
就算你真的在栈上做溢出,栈上局部变量的排列顺序也不是你想的那样。C标准没有规定局部变量在栈上的存储顺序,编译器会根据优化规则、内存对齐要求调整顺序,甚至可能在变量之间插入填充字节。你定义的buf2、buf、buf3,实际在栈上可能是反向排列,或者中间隔了空字节,完全没法按“连续相邻”来假设。
3. 输出全0只是巧合
当size小于16时,buf只有前size个字节是0,后面的字节是栈上的未知数据。memcpy把这些数据拷贝到堆上的result里,你看到全0只是因为buf后面的栈区域刚好是0(可能是栈帧初始化后的残留值),这是环境相关的巧合,换个编译器或系统就会输出随机值。而buf2的数据根本没被拷贝过来,因为它和buf在栈上的位置不一定相邻,就算相邻,顺序也可能和你想的相反。
4. 测试栈溢出的正确方式
如果想测试栈上的缓冲区溢出,得直接操作栈上的缓冲区。比如在main里定义一个固定大小的数组,然后用memcpy往里面写超过它长度的数据,或者修改isThisVulnerable让它直接操作栈上的变量。但要注意,这种行为属于C语言的未定义行为,不同环境下结果差异极大,甚至可能直接崩溃。
内容的提问来源于stack exchange,提问作者cipos

