写在前面
对于C语言中变量入栈的顺序实际上需要具体情况具体分析,不同 *** 作系统下的编译器可能对此有不同的解释,即使对于同一个C的编译器而言,参数设定的不同也会导致编译器调整局部变量的入栈顺序,例如一种栈溢出的防护方案下,int类型的变量可能始终是最后入栈的。
本次实践利用的编译器版本是Windows下的64位编译器: x86_64-pc-cygwin-version 11.2.0
我们在test.c中编写如下代码,申请一个局部的字符数组,然后退出。
#includeint 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 , %rsp .seh_stackalloc 48 .seh_endprologue call __main movl 84234849, -7(%rbp) movw 213, -3(%rbp) movb , -1(%rbp) movl , %eax addq , %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条)