我正在尝试堆栈缓冲区溢出
struct something {
char A[8];
unsigned short B;
};
int main(void) {
struct something st;
st.B = 1979;
strcpy(st.A, "excessive");
printf("%d\n", st.B);
printf("%s", st.A);
return 0;
}
这非常有效。我得到了输出
101
excessive
但是,字符串"过度"有10个字符,包括空终止符,因此即使我将数组更改为char A[9]
,它也应该溢出。但在这种情况下它不会溢出并输出B的原始值。为什么会这样? \ 0在哪里?
答案 0 :(得分:0)
内存分配遵循所谓的自然对齐;即,内存中的任何项目至少与其自身大小的倍数对齐。
unsigned short B
大小为2个字节(在x86上)。因此,它的内存地址与2
的倍数对齐。当你有A[8]
时,数组会消耗偶数个字节;并且B
从A
的最后一个字节之后的字节开始。这就是你的字符串溢出B
内存的方式。
但是,A[9]
需要奇数个字节,因此B
无法从下一个字节开始,因为它会破坏对齐。编译器将插入一个填充字节,以确保B
的地址是2的倍数。这个额外的字节为\0
字符占用空间,从而幸免于缓冲区溢出。< / p>
答案 1 :(得分:0)
'\0'
被写入结构成员B
。这也解释了为什么在第一个101
语句中得到printf
的原因。它正在工作,因为strcpy
没有粉碎堆栈。这意味着它不会超出为堆栈上的结构变量st
分配的内存。 C
标准保证结构成员按声明的顺序存储。但是,如有必要,在每个struct成员之前添加填充,以确保正确对齐。
此处,strcpy
来电
strcpy(st.A, "excessive");
覆盖结构成员数组A
,并在结构成员B
中写入。幸运的是,此处st
结构的大小足以让"excessive"
复制字符串文字strcpy
。在我的32位计算机上,sizeof st
为10
。 strcpy
复制的字节数也是10
。这也解释了为什么声明
printf("%d\n", st.B);
打印101
而非1979
,因为其内容已被strcpy
调用覆盖。如果您尝试复制长度超过9
字符的字符串,或者如果您在char a[B];
之后声明了数组unsigned short B;
,那么对strcpy
的调用将尝试非法内存访问导致未定义的行为,并且很可能由于段错误导致程序崩溃。
此外,'\0'
称为空字符或空字节,而不是'\n'
的换行符。