缓冲区溢出原理及小实验"/>
缓冲区溢出原理及小实验
缓冲区溢出利用
缓冲区溢出原理
-
使用非类型安全的语言(C或者C++),当缓冲区操作时不对边界做出检查,通俗的说就是strcpy,memcpy等串操作和内存块拷贝操作缓冲区时,大数据往小空间上拷贝,造成溢出。
-
函数调用时栈空间的布局为缓冲区溢出提供了条件
当程序中函数被调用时,系统总是先将被调用函数所需的参数以逆序的方式入栈,然后将调用函数后面那条指令的地址(返回地址)入栈。随后控制转入被调用的函数去执行,程序一般在将需要保存的寄存器的值入栈后开始为被调用函数内的局部变量分配所需的存储空间,从而形成图示堆栈结构。
-
如上图,局部变量如果是字符串数组,在进行缓冲区空间分配时,栈内同样符合小端方式对齐的原则,即shellcode[0]在最低端地址上,shellcode[100]在高端地址上,由于被调函数的局部变量分配在返回地址EIP(附近如下图所示:),所以一旦发生溢出,EIP就会被覆盖掉,从而导致程序的运行流程发生变化。Shellcode是指能完成特殊任务的自包含的二进制代码,根据不同的任务可能是发出一条系统调用或建立一个高权限的Shell, Shellcode因此得名。
结论
由于程序缺少必要的边界检查,如果局部变量中有字符数组存在,只要赋予该数组的字符串足够长,就能将上面的返回地址覆盖掉。字符数组超出了开始为其分配的空间大小,缓冲区溢出就发生了。
精心构造溢出所用的字符串,将4个字节返回地址替换成别有用心的地址,当函数返回时,我们就能引导程序到我们指定的代码去执行,从而获得程序控制权。
如果地址A所指定的内存空间事先存放了设计好的攻击代码,那么攻击就会随之发生。
实验验证
使用缓冲区溢出打开记事本
实验环境
win2003,ollydbg,vc++6.0
实验步骤
-
首先使用vc++6.0建立控制台工程
-
写代码,主函数很简单,2行代码就搞定了,用memcpy函数造缓冲区溢出,即大数据往小空间上拷贝,关键的问题是shellcode数组的构造,shellcode数组需要完成的功能是覆盖掉返回地址EIP后,转向攻击代码,即打开notepad.exe。
主函数就两行代码,只要shellcode的大小超过buffer,缓冲区溢出就发生了!
void main(){char shellcode[16];char buffer[12];memcpy(buffer,shellcode,sizeof(shellcode));}
-
构造shellcode
shellcode的内容为打开记事本程序,打开Ollyice,写入打开记事本的汇编代码
然后复制出来
写入shellcode代码里
编译运行会报错,报错的原因下面会解释
然后打开Ollyice开始调
框中可以看出,3个连续的push,是memcpy函数,第一个进去的是数组大小,那么离它最近的retn就是我们要找的EIP,F9执行,此时堆栈区出现一个上面我们见过的地址ADB85004
再按F8进行单步调试,
可以看到会报错。地址不可读!
现在是溢出已经发生,但是我们没有控制好函数的返回地址,让它进入了一个不能读写的地址上去进行,所以会报错
那么我们该如何做呢?
解决办法是添加一组0x90,即空操作,对应汇编nop
编译运行,OLLYICE下断点调试
来看看这个返回地址在什么位置
看到已经跨过了shellcode的有效地址开头,多跑了4个,于是再添加4个0x90即空指针,编译执行然后调试
还是报错
意识到retn指令相当于pop eip指令即将栈顶的值(esp指向的内容)复制给eip,esp+4
于是我们将栈顶指向的值,即4个9090909改为0012FF34即下一步要执行的地址,这样eip的内容就换为0012FF34,从而执行我们的shellcode,代码如下
当然这个shellcode不具有通用性,甚至同样的xp系统不一样的机器都不行,其他的堆栈区可能有所不同
那么如何提高通用性呢?jmp esp指令可以解决,但是为了程序通用性,最好在系统必要的动态链接库中去找(如kernel32),点击OLLYICE上方的M图标进入内存区域,在kernel32.dll 查找FFE4(其对应汇编指令 JMP ESP)
找到7C86467B地址下存有FFE4,所以我们可以用该地址替代掉0012FF34
最终结果如下,克服了通用性,如果均为XP操作系统,该EXE均能打开notepad
那么其它系统呢?肯定不行,其他如Shellcode区域里的0X7C8623AD为WinExec在XP下的函数地址,0X7C81CAFA为ExitProcess的函数地址,并且0X7C86467B地址在高版本操作系统下也不一定对应FFE4这条JMP ESP指令。
所以要使此代码在其他windows系统上通用,我们需要将硬编码修改为动态获取
如何动态获取呢?LoadLibrary和GetProcAddress函数就派上用场了,我们只需要利用这两个函数联合调用,就可以获取想要的API函数地址,然后把该地址反填回shellcode,那FFE4对应的0X7C86467B如何更改呢?简单粗暴的方法就是在本模块中找到FFE
修改代码为
#include <windows.h>
char shellcode[] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x7B, 0x46, 0x86, 0x7C, 0x68, 0x70, 0x61, 0x64, 0x00, 0x68, 0x6E, 0x6F, 0x74, 0x65,0x6A, 0x01, 0x8B, 0xC4, 0x83, 0xC0, 0x04, 0x50, 0xB8, 0xAD,0x23, 0x86, 0x7c, 0xFF, 0xD0, 0x6A, 0x00, 0xB8, 0xFA, 0xCA,0x81, 0x7C, 0xFF, 0xD0
};
void test(){char buffer[12] ;memcpy (buffer, shellcode, sizeof (shellcode));
}void main()
{DWORD base = 0x00400000;DWORD a1 = (DWORD)GetProcAddress(LoadLibrary("kernel32.dll"),"WinExec");DWORD a2 = (DWORD)GetProcAddress(LoadLibrary("kernel32.dll"),"ExitProcess");while (1){if (*(WORD*)base == 0xe4ff)break;base ++;}*(DWORD*)&shellcode[16] = base;//表示从数组第16字节开始的4个字节被base覆盖 *(DWORD*)(shellcode+39) = a1;*(DWORD*)(shellcode+48) = a2;test() ;
}
更多推荐
缓冲区溢出原理及小实验
发布评论