栈溢出漏洞原理详解与利用

作者: threepwn 合天智汇
0x01 前言和我一样 , 有一些计算机专业的同学可能一直都在不停地码代码 , 却很少关注程序是怎么执行的 , 也不会考虑到自己写的代码是否会存在栈溢出漏洞 , 借此机会我们一起走进栈溢出 。
0x02 程序是怎么运行的在了解栈溢出之前我们先了解一下程序执行过程
程序的执行过程可看作连续的函数调用 。当一个函数执行完毕时 , 程序要回到call指令的下一条指令继续执行 , 函数调用过程通常使用堆栈实现
#include <stdio.h>#include <stdlib.h>int main(int argc, char **argv) {test1(1);test2(2);test3(3);return 0;}int test1(int test1){int a = 6;printf("1");return 1;}int test2(int test2){printf("2");return 2;}int test3(int test3){printf("3");return 3;}编译成32位可执行文件 , 放在ollydbg中就行调试 , 来详细看一下执行过程
因为程序的执行可以看做一个一个函数的执行(main函数也一样) , 因此我们挑选其中一个即可 , 在test1()函数设置断点

栈溢出漏洞原理详解与利用

文章插图
 
F7单步调试第一步mov dword ptr ss:[esp],0x1 , 进行传参 , 简洁明了 。
第二步call mian.00401559 , 进入test() , 这里我们关注一下esp和栈顶值 , 将该指令的下一条指令的地址进行压栈 , 既然有压栈那么就会有出栈 , 这就与函数中的retn指令形成呼应 。
栈溢出漏洞原理详解与利用

文章插图
 
第三步push ebp , 就是把ebp的值进行压栈 , 那么这个ebp是什么呢?有什么用呢?
EBP叫做扩展基址指针寄存器(extended base pointer)  , 里面放一个指针 , 该指针指向系统栈最上面一个栈帧的底部 , 用于C运行库访问栈中的局部变量和参数 。那么这一步的意义就是:保存旧栈帧中的帧基指针以便函数返回时恢复旧栈帧
栈溢出漏洞原理详解与利用

文章插图
 
第四步 , mov ebp , esp , 将esp的值放在ebp中 , 我们再来了解一下什么是esp?
ESP(Extended Stack Pointer)为扩展栈指针寄存器 , 是指针寄存器的一种 , 用于存放函数栈顶指针 , 指向栈的栈顶(下一个压入栈的活动记录的顶部) , 也就是它不停在变 , 刚才提到的ebp指向栈底 , 在函数内部执行过程中是不变 。
那么我们再看一下这一步的作用:
从第三步可以知道esp存储的值是旧栈帧中的帧基指针 , 而esp值栈顶指针 , 随时都在变 , 因此为了函数结束后能恢复 , 把esp值(外层函数栈底地址)保存在本函数栈底ebp中 。简而言之 , 将内部函数ebp的值作为地址 , 它存放外函数的ebp的值 。这一步在末尾也存在逆向指令leave 。
栈溢出漏洞原理详解与利用

文章插图
 
第五步是sub esp,0x28 , 开辟该函数的局部变量空间
紧接着第六步mov dword ptr ss:[ebp-0xC],0x6 , 给变量a一个大小是0xC的空间 , 并且赋值 。
然后就是传参字符1的ascii码 , 调用printf函数 , 把返回值放到eax 。
我们重点来看leave指令 , 可以发现ebp的值恢复了 , esp的值也变了 , 相当于mov esp,ebp;pop ebp
栈溢出漏洞原理详解与利用

文章插图
 
最后执行retn指令 , 至此一个函数执行完毕 , esp和eip的值都被改变 , 相当于pop eip , 然后程序继续执行 。EIP是指令寄存器 , 存放当前指令的下一条指令的地址 。CPU该执行哪条指令就是通过EIP来指示的
栈溢出漏洞原理详解与利用

文章插图
 
0x03 栈溢出分析完这一过程 , 相信大家对函数是怎么执行的应该明朗了 , 那么我们言归正传 , 继续聊一下栈溢出 。首先我们先看一下什么是栈?
栈可以看作是一个漏斗 , 栈底地址大 , 栈顶地址小 , 然后在一个存储单元中 , 按照由小到大进行存储 , 它的目的是赋予程序一个方便的途径来访问特定函数的局部数据 , 并从函数调用者那边传递信息 。


推荐阅读