C语言中字符串的入栈
写在前面
对于C语言中变量入栈的顺序实际上需要具体情况具体分析,不同操作系统下的编译器可能对此有不同的解释,即使对于同一个C的编译器而言,参数设定的不同也会导致编译器调整局部变量的入栈顺序,例如一种栈溢出的防护方案下,int类型的变量可能始终是最后入栈的。
本次实践利用的编译器版本是Windows下的64位编译器: x86_64-pc-cygwin-version 11.2.0
我们在test.c中编写如下代码,申请一个局部的字符数组,然后退出。
#include <stdio.h>
int main() {
char str[5] = "abcdef";
return 0;
}
在命令行编译输出汇编代码:
gcc -S test.c -o test.asm
得到如下的汇编代码(AT&T)
.file "test.c"
.text
.def __main; .scl 2; .type 32; .endef
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $48, %rsp
.seh_stackalloc 48
.seh_endprologue
call __main
movl $1684234849, -7(%rbp)
movw $26213, -3(%rbp)
movb $0, -1(%rbp)
movl $0, %eax
addq $48, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (GNU) 11.2.0"
代码分析:
main之前的内容在说明函数入口与文件信息,暂时不讨论
.seh_*相关的伪指令是Windows提供的一套结构异常化处理的例程,可以暂时不讨论。
不过从指令名字上我们其实也能简单地了解它们在做什么,例如seh_stackalloc 48就是为堆栈段分配了48字节的空间。
详情可以查阅:https://sourceware.org/legacy-ml/binutils/2009-08/msg00193.html
抛开上述影响因素后,我们来看我们关心的部分,call __main指令之后的内容:
movl $1684234849, -5(%rbp)
转16进制后,左边立即数为 6463 6261H,即“abcd”的端序
movw $26213, -3(%rbp)
转16进制后,左边立即数为66 65H,即“ef”的端序
movb $0, -1(%rbp)
将‘\0’压入栈
因此我们的栈空间为
总结一下:将字符串压栈时,连续储存,开头的字符对应低地址,结尾字符对应高地址。
从这个原理上我们再理解一下熟知的缓冲区溢出攻击,如果攻击者输入的字符串大于申请的缓冲区长度,末尾多出来的字符串在栈内会向高地址延伸,覆盖掉栈中原有的数据。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)