因为不同的程序所用栈和所用堆的比重不同,将两者的公用空间联用是一个高效的做法
一般情况下,栈是从高地址向低地址增长,堆是从低地址向高地址增长
程序员习惯从低向高去读写内存
(所谓“指针指向下一个位置”,会默认“下一个位置默认”为高地址)
所以在设计的时候就将堆布局为就是从低向高增长,而栈对此并不敏感,所以会出现现代程序的堆栈布局
当向栈中写入了超过了限定长度的数据,溢出的数据会覆盖到指定区域,如果把想要执行的地址覆盖到到原来函数的返回地址,那么返回时候,就会执行被覆盖的地址的函数
这也是“黑客程序栈溢出攻击”的通用做法
下面举个最简单的例子:
#include<stdio.h>
int fun1()
{
int a;
gets((char *)&a);
return 0;
}
int fun2()
{
printf("Stack overflow, function jump!\n");
return 0;
}
int main(int argc, char *argv[])
{
fun1();
return 0;
}
注意这里利用了一个C语言里面非常危险的函数gets(),这个函数没有边界检查,只能检测4字节的大小(32位,即为4个字符),当输入字符大于数量这个的时候就会发生越界
以上程序,当输入4个A的时候,查看堆栈情况,可以看到没有异常
当输入7个A的时候,意味着超过了3个存储空间(A在16进制中是41)
注意到BP区域被侵占了,BP为栈的基地址
(之前的R代表是64位,同理E代表32位,不加代表16位)
字符超了就直接超越了栈的基地址空间 ,将别的空间入侵了
那么直接填写12个A,就直接超越了基地址空间!
再多的字符就到了下一个空间,就是IP,是函数返回时要跳转的地址
如果把这个地址改为fun2的地址就可以实现跳转到我们需要它执行的位置了
下面是原理图:
IP为指令指针寄存器 指向将要执行的下一条指令的地址,是我们要入侵的地方
BP为栈底基地址
去寻找func2的函数入口:
可以找到0x000000000040157f是func2的入口地址
在gets处输入AAAAAAAAAAAA+0x000000000040157f直接穿透栈底,把fun2的地址注入到下一步要执行的地址空间
(注意大小端和地址格式的转换)
执行!!!然后就翻车了,不过程序在这里卡住了,说明它去找那个地址了,但是我们提供的地址不对,估计是地址输入格式不对什么的,这里学艺不精,没复现出效果,原谅一下哈,但是思路是没问题的😂
那就换一个编译器,发现不能使用了
哈哈,看来微软的编译器是非常安全的,都直接禁用了这个函数