PWN学习
一、看不懂的理论部分
1、栈溢出
寻找危险函数
通过寻找危险函数,我们快速确定程序是否可能有栈溢出,以及有的话,栈溢出的位置在哪里。常见的危险函数如下
- 输入
- gets,直接读取一行,忽略'\x00'
- scanf
- vscanf
- 输出
- sprintf
- 字符串
- strcpy,字符串复制,遇到'\x00'停止
- strcat,字符串拼接,遇到'\x00'停止
- bcopy
确定填充长度
这一部分主要是计算我们所要操作的地址与我们所要覆盖的地址的距离。常见的操作方法就是打开 IDA,根据其给定的地址计算偏移。一般变量会有以下几种索引模式
- 相对于栈基地址的的索引,可以直接通过查看 EBP 相对偏移获得
- 相对应栈顶指针的索引,一般需要进行调试,之后还是会转换到第一种类型。
- 直接地址索引,就相当于直接给定了地址。
一般来说,我们会有如下的覆盖需求
- 覆盖函数返回地址,这时候就是直接看 EBP 即可。
- 覆盖栈上某个变量的内容,这时候就需要更加精细的计算了。
- 覆盖 bss 段某个变量的内容。
- 根据现实执行情况,覆盖特定的变量或地址的内容。
之所以我们想要覆盖某个地址,是因为我们想通过覆盖地址的方法来直接或者间接地控制程序执行流程。
在x86环境中,esp
和ebp
是两个特殊的寄存器,用于管理栈帧和函数调用。
-
esp
寄存器(Extended Stack Pointer)是栈指针寄存器,用于指向栈的顶部。栈是一种后进先出(LIFO)的数据结构,用于存储函数调用的局部变量、函数参数以及其他临时数据。通过修改esp
寄存器的值,可以在栈上进行数据的压栈和弹栈操作。 -
ebp
寄存器(Extended Base Pointer)是基址指针寄存器,也称为帧指针寄存器。它通常用于指向当前函数的栈帧的底部。栈帧是一个用于存储函数的局部变量和其他相关信息的区域。通过保存和恢复ebp
寄存器的值,可以在函数调用之间正确地访问和管理局部变量。
在函数调用过程中,通常会按照以下步骤使用esp
和ebp
寄存器:
-
在函数的入口处,通过将当前的
ebp
值保存到栈上,创建一个新的栈帧。这样可以在函数执行期间保存上一级函数的栈帧信息。 -
将当前的
esp
值赋给ebp
寄存器,以建立当前函数的栈帧。这样可以使用ebp
作为基址指针来访问函数的局部变量和其他相关信息。 -
在函数执行过程中,通过修改
esp
寄存器的值来分配和释放栈上的空间,以便存储局部变量和临时数据。 -
在函数退出之前,通过恢复之前保存在栈上的
ebp
值,销毁当前函数的栈帧,并将控制权返回到上一级函数。
使用esp
和ebp
寄存器可以有效地管理函数调用和局部变量,确保栈帧的正确访问和释放,以及函数之间的正确返回。
在x86架构的环境中,有一些常见的寄存器用于存储和处理数据。以下是x86架构中常见的寄存器:
-
通用寄存器(General-Purpose Registers):
- EAX (Accumulator Register):用于存放函数的返回值或者临时数据。
- EBX (Base Register):通常用作基址寄存器,用于存放内存地址。
- ECX (Count Register):通常用作计数器,在循环操作中使用。
- EDX (Data Register):用于存放数据,也用于一些特定操作指令的数据输入和输出。
-
指针寄存器(Pointer Registers):
- ESP (Stack Pointer Register):指向栈的顶部,用于管理函数调用时的栈帧。
- EBP (Base Pointer Register):用于指向当前函数的栈帧的底部。
-
索引寄存器(Index Registers):
- ESI (Source Index Register):通常用于存放源数据的地址,在数据传输操作中使用。
- EDI (Destination Index Register):通常用于存放目标数据的地址,在数据传输操作中使用。
-
标志寄存器(Flag Register):
- EFLAGS:用于存储各种条件码和控制标志位,如进位标志、零标志、符号标志等。
-
段寄存器(Segment Registers):
- CS (Code Segment):用于存放代码段的基地址。
- DS (Data Segment):用于存放数据段的基地址。
- SS (Stack Segment):用于存放栈段的基地址。
- ES (Extra Segment):通常用作附加数据段的基地址。