浅淡栈溢出(一)

栈溢出问题


​ 说到二进制安全,肯定避不开栈溢出,今天就拿栈溢出作为开始,讨论一下它的形成与构造。

1. 程序的内存空间

​ 首先,先介绍一下应用程序内存空间布局,用一张图来说明,简单粗暴。

在这里插入图片描述
简单介绍一下就是,每个应用程序都有4个GB的虚拟空间(在windows编程的虚拟内存中介绍了),高1个GB作为内核空间,用户代码无权访问,所有的应用程序共享这部分空间。而低3个GB的空间是用户代码,有stack ,heap ,MMap(存放动态链接库)等等,本文重点介绍stack。

有关堆,栈在虚拟空间中的概念:

1.栈自底向上增长,尺寸动态变化,默认临界大小是8M。

2.Stack和mmap间有个随机偏移量用于防止栈溢出污染mmap。

3.Mappings区间主要布置动态链接库。

4.默认进程堆自顶向下增长。


2. 栈帧

  • 栈是一种LIFO的数据结构。
  • 应用程序有一到多个用户态栈。
  • 栈自底向上增长,由指令PUSH和POP引起其动态变化。
  • 局部变量布局在栈中。
  • 调用函数时参数由栈传递,返回地址也存储于栈中。
  • 函数调用上下文与局部变量共同组成了栈帧——Stack Frame.

总结一下:栈帧=局部变量+函数调用上下文

再简单点的说:栈帧实际上只是一个通俗的说法,关于栈帧的上下界历来有两种说法,一曰以EBP和ESP之间的栈空间视为栈帧,这也是主流说法;一曰以调用参数和ESP之间的栈空间视为栈帧,我个人更倾向于这种说法,因为它便于理解。


3. 经典栈溢出的手法

经典栈溢出的手法概要:

  • 先决条件:栈局部变量可控,存在溢出(strcpy)。

  • 通过栈空间精心布局,布置shellcode,并用shellcode起始地址覆盖栈帧的ret addr。

存在的问题:

  • Shellcode地址在不同PC上不确定
  • 每次运行地址都会变化(ASLR)

拿一个例子说明:

在这里插入图片描述

出现的问题:

  1. fread时,指定的长度1024超过了buf尺寸。
  2. 输入数据长度可控,在input.txt中。
  3. 可以精心操纵input.txt,对buf进行溢出布置shellcode、覆盖ret addr。
  4. 当返回的地址可以被控制时,问题就大发了。

具体如何实现呢:

  1. 确定栈帧布局,计算出buf到ret addr的offset。
  2. 先对buf填充无效数据,通过调试找出buf的首地址并覆盖ret addr。
  3. 用一段shellcode填充buf,这段shellcode会弹一个shell。

4. you jump ,i jump esp

以上的栈溢出是存在明显问题的。

首先就是硬编码buffer地址的缺陷:

1. 对于不同版本系统来说,硬编码的地址是硬伤。Windows尚有dll装卸引起的“移位”。

​ 2. 在ASLR引入以后,栈每次运行基址都是随机的。

解决方法

一般情况下,ESP寄存器中的地址总是指向系统栈中且不会被溢出的数据破坏,函数返回时,ESP所指向的位置恰好是我们所淹没的返回地址的下一个位置。

在这里插入图片描述

现在问题就转化成了如何寻找“JMP ESP”的字节码地址。

本质:从内存空间的各个映像中寻觅”JMP ESP”的字节码地址。

  • 对Linux来说:
    ELF映像本身
    加载的so

  • 对Windows来说:
    PE映像本身
    加载的dll

要点:尽量择取具有高稳定性的”JMP ESP”指令的地址。

有待补充,未完待续…

发布了29 篇原创文章 · 获赞 1 · 访问量 939

猜你喜欢

转载自blog.csdn.net/qq_40505187/article/details/104622985