栈溢出介绍
栈溢出介绍
零、前言:
在打pwnable.kr的passcode题目的时候,发现了自己存在一些基础薄弱,需要补充回来,这是栈溢出的笔记。
一、进程内存:
无论什么计算机架构,进程使用的内存按照功能大致分为四部分:
1、代码区:
存储着被转入的执行的二进制代码,处理器会到这个区域获取指令并执行。
2、数据区:
用来存储局部变量
3、堆区:
进程可以在堆区中动态请求一定大小的内存,并在用完之后归还给堆区。
动态分配和回收是堆区的特点。
4、栈区:
用于动态的存储函数之间的调用关系。以保证被调用函数在返回时恢复到主函数中继续执行。
二、栈:栈和系统栈
栈:栈是一种数据结构,是一种先入后出的数据表,按照一定的规则进行添加和删除数据。
系统栈:内存中的栈,由系统自动维护,实现高级语言中的函数调用
三、溢出:
在达缓冲区的数据向小缓冲区复制的过程中,由于没有注意小缓冲区的边界,导致小缓冲区满了,从而覆盖了和小缓冲区相邻内存区域的其他数据而引起的内存问题。
四、X86通用寄存器:
1、
PUSH:压入
POP:弹出,删除
MOV:复制
SUB: sub,esp xxx 即预留xxx字节
2、
ESP:栈顶寄存器,其内存中是一个指针,该指针永远指向系统栈的最上面的一个栈帧的栈顶。
EBP:栈底寄存器,其内存是一个指针,该指针永远指向系统栈最上面的要给栈帧的底部。
EIP:指令寄存器,其内存中是一个指针,该指针永远指向下一条等待执行的指令地址。
3、
EAX:累加器,多种加法乘法指令的默认寄存器
EBX:基地址寄存器,在内存寻址是存放基地址
ECX:计数器,重复(REP)前缀指令和LOOP指令的内定计数器
EDX:被用来放整数除法产生的余数
ESI/EDI:源/目标索引寄存器,在很多字符串操作指令中,DS:ESI指向源串,ES:EDI指向目标串。
五、函数调用过程:
1、步骤:
(1)参数入栈:将参数从右向左依次压入系统栈。
(2)返回地址入栈:将当前代码区调用的下一条指令地址压入栈中,供函数返回时继续执行。
(3)代码区跳转:处理器从当前代码区跳转到被执行函数入口。
(4)栈帧调整:
1)保存当前栈帧状态,以被后面恢复本栈帧使用(push ebp)
2)将当前栈帧切换到新的栈帧(mov ebp,esp)
3)给新栈帧分配空间(esp减去所需空间的大小,抬高栈顶)
2、图例:
1)普通C程序的内存布局:
Text:
包含要执行的程序代码
Data:
包含程序需要的全局数据、资源等
Stack:
包含函数的输入参数,返回地址以及保存函数的局部变量等。
Stack是后进先出的结构。随着函数的调用,它在内存中(从高地址到低地址)向下寻址。
Heap:保存所有动态分配的内存。每当用malloc(动态分配内存)分配获取内存指针时,这个地址就是从堆中分配的。
2)重点关注三大寄存器:EBP(栈底)、ESP(栈顶)、EIP(指向)
当执行一个函数的时候,相关参数以及局部变量等都会被记录在ESP、EBP中间的区域。
一旦函数执行完毕,相关栈帧就会从堆栈中弹出,然后从预先保存好的上下文中进行恢复,以便保持堆栈平衡。
CPU必须要知道函数调用完了后的EIP指向(要去哪里执行),这需要堆栈弹出的过程中进行EIP赋指。
3)main函数调用func()函数的程序:
main函数调用func函数,程序运行后,依次将所有的参数压入栈中,
func函数执行完成后,相应的栈帧依次弹出,此时存储返回值的地址被加载到EIP寄存器中,以继续执行main函数中剩余的部分。
目的就是要控制这个返回值,劫持func函数返回到指定的恶意代码中区。
3、例子A:
(1)代码:
1 #include <stdio.h>
2 #include <string.h>
3
4 void function2(){
5 printf("Execution flow changed\n");
6 }
7 void function1(char *str){
8 char buffer[5];
9 strcpy(buffer,str);
10 }
11 void main(int argc,char *argv[]){
12 function1(argv[1]);
13 printf("%s\n","Executed normally");
14 }
编译:
1 root@kali:~/test# gcc -g -fno-stack-protector -z execstack -o test1 test1.c
1)-g:将调试信息记忆符号等编译到程序中
2)-fno -stack-protestor:关闭堆栈保护机制
3)-z execstack:打开堆栈可执行机制,即关闭堆栈执行保护
运行与崩溃:
(2)审计调试:
PS:strcpy函数没有对参数进行检查边界大小。
gdb调试:
1)使用list显示源代码,然后在strcpy和函数返回的地方下断点
2)反复运行测试,变换参数内容,找到崩溃的地址
EBP和ESP地址
strcpy执行中的EBP和ESP地址
strcpy执行过后的EBP和ESP
3)找到EIP地址
注意:EIP在EBP的下方,即
4)找到function2的开始地址:
(3)利用:
1)利用思路:
2)利用payload:
A: 相对于EBP(找到头晕)
B:相对于ESP(找到头晕+1)
C:直接地址相加:
1 (gdb) run $(python -c 'print "A" *17 + "\xb9\x11\x40\x00"')
4、例子B:
(1)代码:
(2)函数调用过程:
1、压入寄存器eax,ecx,edx等
2、压入参数arg2,arg1
3、压人返回地址eip
4、压入ebp
5、压入temp
6、压入buffer
7、压入ebx,esi和edi等寄存器
(3)利用:
1)思路:
由于局部变量buffer的长度是4个字节,arg1参数可控,
而strcpy函数并没有判断参数的长度,
所以传入的arg1的长度大于4个字节时,多出来的字节将会依次覆盖掉局部变量,ebp,返回地址等
通过精心构造arg1的值,就可以将返回地址覆盖为任意想要的值,挑到shellcode。
2)利用:
5、例子C:修改邻接变量
函数的局部变量在栈中一个挨着一个,如果这些局部比办理中有数组之类的缓冲区,
并且程序中存在数组越界的缺陷,那么越界的数组元素就有可能破坏栈中相邻变量的值,
甚至破坏栈中保存的ebp值、eip值等重要数据。
(1)代码:
1 #include <stdio.h>
2 #include <string.h>
3 #define PASS_WORD "1234567"
4
5 int verify_password(char * password)
6 {
7 int authentitated;
8 char buffer[8];
9 authentitated = strcmp(password,PASS_WORD);
10 strcpy(buffer,password);
11 return authentitated;
12 }
13
14 int main()
15 {
16 int valid_flag = 0;
17 char password[1024] = {0};
18 while (1)
19 {
20 printf("please input password:");
21 scanf("%s",password);
22 valid_flag = verify_password(password);
23 if(valid_flag)
24 {
25 printf("incorrect password!\r\n");
26 }
27 else
28 {
29 printf("Congratulation ! you have passed the verification !\r\n");
30 }
31 }
32 return 0;
33 }
(2)审计:
1)main函数输入password,调用verify_password函数验证输入的password是否等于1234567,相等返回0,否则返回1。
2)strcpy函数存在漏洞和buffer[8],构造利用条件
3)利用缓冲区溢出,修改值,返回0,完成验证。
思路:在verify_password栈帧中
authenticated位于buffer[8]的下方
authenticated是int型,在内存中占4个字节
buffer[8]占8个字节
控制buffer[]填满8个字节,然后越界1个字节,缓冲区溢出,使得原authenticated的1覆盖为0,返回通过。
6、例子C:修改函数返回地址
控制buffer[8]越界,覆盖其他值,修改eip
六、溢出关键函数:
1、输入:
gets
scanf
vscanf
2、输出:
sprintf
3、字符串:
strcpy
strcat
bcopy
4、等
七、栈溢出步骤:
1、寻找危险函数:
2、确定填充长度:计算所能操作的地址和所要覆盖的地址的距离长度
(1)方法:
1)相对于栈底地址
2)相对于栈顶地址
3)直接地址
(2)覆盖:
1)覆盖函数返回地址
2)覆盖变量内容
3)覆盖bss段某变量内容:bss段(指用来存放程序中未初始化的全局变量的一块内存区域,bss段属于静态内存分配。)
4)等
3、利用:
当完全控制这个程序后,使用一个直接存在指令地址来覆盖这个返回地址后,CPU将会转而执行我们的指令。
在uinx/Linux系统中,指令可以执行一个shell,这个shell将会获得和被溢出程序相同的权限。
如该程序是root权限,那么就会获得root shell
八、参考链接:
https://www.cnblogs.com/Donoy/p/5690402.html
https://www.sohu.com/a/226035403_268160
https://www.jianshu.com/p/58d03dd3680a
https://blog.csdn.net/aemperor/article/details/47310593
https://blog.csdn.net/guiguzi1110/article/details/77663046