@TOC
前言
Hello,各位广大的网友们好。笔者为大二在校大学生,软件学院。为践行费曼学习法,并在前辈的建议下开始写自己的一系列博客,网上对于缓冲区溢出的例子基本在与Linux环境下操作。对未了解Linux内核以及常见操作指令的新手并不友好。鉴于此写下此文。
实验前提
- 实验环境 :windows 10;
- 使用工具 :IDA pro , OllyDBG,vc6.0;
- 目标软件 :test1.exe , test2.exe
所需基础
基础汇编指令
简单C语言基础
数据结构(仅堆栈)
即使上面基础缺失,本文任有大量的注释及解释帮助理解。
概要
通过编写两个C例程序,使用工具OD,IDA pro进行逆向分析。针对汇编语句中CALL机制剖析缓冲区溢出的基本原理。
实验开始
编写实验程序text1.exe , text2.exe
text1.exe中C代码如下:
#include<stdio.h> //引入头文件
#include<string.h>
char name[] = "zhemu"; //定义全局变量
int main() //返回值 主函数main()
{
char buffer[8]; //开辟8个字节的空间用来存储变量name
strcpy(buffer,name); //内置函数(作用):将变量name内容赋值给buffer变量
printf("%s\n",buffer); //输出在控制台
getchar(); //方便观察 作用:等待用户输入按键
return 0; //返回值
}
text1.c中 ,变量name赋值给buffer后并未引起缓冲区溢出
text2.exe中C代码如下:
#include<stdio.h> //引入头文件
#include<string.h>
char name[] = "zhemuzhemu"; //定义全局变量 !!多了个zhemu
int main() //返回值 主函数main()
{
char buffer[8]; //开辟8个字节的空间用来存储变量name
strcpy(buffer,name); //内置函数(作用):将变量name内容赋值给buffer变量
printf("%s\n",buffer); //输出在控制台
getchar(); //方便观察 作用:等待用户输入按键
return 0; //返回值
}
text1.c中 ,变量name赋值给buffer后引起缓冲区溢出
对text1.exe进行逆向分析。
将vc6.0生成的debug版本exe拖入OllyDBG进行逆向分析
OllyDBG载入方式:
1.鼠标拖动exe进入到OD控件区域
2.文件-附加-选择运行中的进程
!由于新版本的Visual Studio,微软加入GS机制,防止缓冲区溢出的情况出现
由上图:004015B0地址为软件初始运行地址,并非main函数的起始地址。可通过F7/F8一步步运行至main函数所在地址。过于繁琐,所以采用IDA快速定位main函数所在地址,如下图:
快速定位main函数所在地址
1.双击进入main
2.右键选择Text View,初始默认为Graph View
可得main函数地址如下图:
4.IDA 快捷键Ctrl+x 定位上一层交叉引用。(简单理解就是从哪里来 ,谁调用它)
由此可知,开始main函数上一个地址00401693,重返OD进行堆栈溢出研究
通过OD中快捷键Ctrl+G快速定位到call函数上一条指令地址,快捷键F2对00401693下断点,快捷键F9让调试直接运行到此处代码。 同时也可以按F4运行于此!
F8步过程序,留意右下角栈区调用情况。
调用CALL函数:
1.将CALL下一个地址压入栈(00401694)
2.jmp CALL
由上图: 压入栈区为地址(00401694)返回地址。重要关键地方
笔者在右下角栈区设置转换为ASCII数据 方便观察
新手可以忽略之前栈平衡以及栈区开辟空间指令,由地址00401030可知,此时与C语言代码对应开始,将ASCII压入栈区,并进行strcpy赋值操作。
对汇编认识可以初步认识此步骤,重点在于调用完CALL之后返回到初始地址下一条
来到call函数调用返回所在位置:
可以看出retn返回的地方栈区栈顶指向的地址为:00401699 call函数调用完下一指令
OK,到此为止我们了解了一个CALL函数调用的基本过程,直接进入text2.exe分析,当堆栈溢出栈区会发生什么呢?
对text2.exe进行逆向分析。
步骤不多累赘与text1.exe完全一摸一样,唯一不一样在于栈区返回地址被溢出(占用)
由上图可知:
1.原本改返回值:00401699地址被占用 且指向错误的地址(或不应该的地址)
缓冲区溢出漏洞原理总结
至此,大家应该已经了解了缓冲区溢出漏洞的原理,它就是因为我们输入了过长的字符,而缓冲区本身又没有有效的验证机制,导致过长的字符将返回地址覆盖掉了,当我们的函数需要返回的时候,由于此时的返回地址是一个无效地址,因此导致程序出错。
那么依据这个原理,假设我们所覆盖的返回地址是一个有效地址,而在该地址处又包含着有效的指令,那么我们的系统就会毫不犹豫地跳到该地址处去执行指令。因此,如果想利用缓冲区溢出的漏洞,我们就可以构造出一个有效地址出来,然后将我们想让计算机执行的代码写入该地址,这样一来,我们就通过程序的漏洞,让计算机执行了我们自己编写的程序。
本文其中例子与实例深受i春秋启发,并进行归纳总结,笔者才学疏浅,若有错误之处欢迎指出