1.1 函数调用栈情况

缓冲区溢出2.攻击方式 3.防护
缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量溢出的数据覆盖在合法数据上 。
理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符 , 但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹配 , 这就为缓冲区溢出埋下隐患 。
【1.1 函数调用栈情况】缓冲区溢出有堆缓冲区和栈缓冲区溢出 , 二者有些不同 , 大部分情况下都是讨论栈溢出 。
1.原理 1.1 函数调用栈情况
程序运行时 , 为了实现函数之间的相互隔离 , 需要在调用新函数时保存当前函数的状态 , 这些信息全在栈上 , 为此引入栈帧 。每一个栈帧保存者一个未运行完的函数的信息 , 包括局部变量等等 。栈帧的边界由ebp/rbp(栈底指针)和esp/rsp(栈顶指针)确定 。
先看一看函数调用时的栈情况 , 以func1调用func2为例 。假如func2有2个形参 。
当func1调用 func2会执行如下汇编码
push arg2push arg1call func2add esp, 8
一般一个函数(func2)的起始和终止汇编代码会有如下操作
push ebpmov ebp, espsub esp, xxx...mov esp, ebppop ebpretn
func1调用func2之前 , 栈中只有func1的局部变量 。
func2执行完sub esp, xxx后整个栈空间布局如下 , 此时ebp指向的是func1的ebp 。从func2返回地址到func1的局部变量都属于func1的栈帧 。

func2局部变量
func1的ebp
func2返回地址(func1某条指令)
func2 2个参数
func1局部变量
1.2 缓冲区溢出
void function(char *str) {char buffer[16];strcpy(buffer,str); }
上述代码从str向复制数据 , 当str长度超过16时 , 就会溢出 。问题根源在于没有限制复制数据长度 , 存在类似的问题还有() , () , () , gets() , scanf()等 。
不过随便溢出并不能造成很大的危害 , 不能达到攻击目的 。所以一般攻击者需要利用缓冲区溢出漏洞运行危险函数(比如("/bin/sh");)获取对面shell 。
2.攻击方式 2.1 利用
将希望执行的指令输入到栈空间中 , 利用跳板指令jmp esp执行 。jmp esp在.dll中可以找到 。
正常情况下 , 栈空间内容为(从上到下增长)
栈空间
栈变量
ebp
返回地址
函数形参
现在存在溢出风险的缓冲区在栈变量中 , 那么想执行应该怎么做呢?如何利用jmp esp呢?
一般来说函数调用最后几句有
pop ebpretn(或pop eip)
retn之后esp指向函数形参 , 而eip此时假如指向esp , 那么cpu就会来函数形参区取指令 。就可以通过 (栈变量 + ebp + jmp esp指令地址 + )这样的组合 , 一路覆盖栈变量 , ebp , 返回地址和函数形参 。即返回地址用jmp esp的地址取代 。jmp esp在动态链接库里有出现 , 其地址需要搜索下 。
2.2 跳到其它函数
假如源代码中存在可以利用的函数 , 比如如下代码
#include#includevoid copyout(const char *input){char buf[10];strcpy(buf, input);printf("%s \n", buf);}void bar(){system("/bin/sh");printf("hacked done\n");}int main(int argc, char *argv[]){copyout(argv[1]);return 0;}