Created
February 13, 2024 21:47
-
-
Save odzhan/d677d9d5b6b31d8cabf9277bc14a3856 to your computer and use it in GitHub Desktop.
Delegate NT DLL Table
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
// | |
// How to locate the NT Delegate Callback Table in x86 builds of ntdll.dll | |
// | |
// @modexpblog | |
// | |
#define PHNT_VERSION PHNT_THRESHOLD | |
#include <phnt_windows.h> | |
#include <phnt.h> | |
#include <cstdio> | |
#include <cstdint> | |
#include <cstdlib> | |
#include <cstring> | |
typedef union _W32_T { | |
LPVOID p; | |
BYTE b[4]; | |
DWORD w; | |
LPVOID *pp; | |
} W32_T; | |
typedef struct _NT_DELEGATE_CALLBACK { | |
PCHAR Name; | |
W32_T Function; | |
} NT_DELEGATE_CALLBACK, *PNT_DELEGATE_CALLBACK; | |
// | |
// Structure based on x86 version of NTDLL | |
// | |
typedef struct _NT_DELEGATE_TABLE { | |
NT_DELEGATE_CALLBACK LdrInitializeThunk; | |
NT_DELEGATE_CALLBACK RtlUserThreadStart; | |
NT_DELEGATE_CALLBACK RtlDispatchAPC; | |
NT_DELEGATE_CALLBACK KiUserExceptionDispatcher; | |
NT_DELEGATE_CALLBACK KiUserApcDispatcher; | |
NT_DELEGATE_CALLBACK KiUserCallbackDispatcher; | |
NT_DELEGATE_CALLBACK KiRaiseUserExceptionDispatcher; | |
NT_DELEGATE_CALLBACK LdrSystemDllInitBlock; | |
NT_DELEGATE_CALLBACK LdrpChildNtdll; | |
NT_DELEGATE_CALLBACK LdrParentInterlockedPopEntrySList; | |
NT_DELEGATE_CALLBACK LdrParentRtlInitializeNtUserPfn; | |
NT_DELEGATE_CALLBACK LdrParentRtlResetNtUserPfn; | |
NT_DELEGATE_CALLBACK LdrParentRtlRetrieveNtUserPfn; | |
} NT_DELEGATE_TABLE, *PNT_DELEGATE_TABLE; | |
PIMAGE_SECTION_HEADER | |
GetSectionFromRva(PIMAGE_NT_HEADERS NtHeaders, DWORD Rva) { | |
auto Section = IMAGE_FIRST_SECTION(NtHeaders); | |
for (DWORD i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) { | |
if (Rva >= Section->VirtualAddress && | |
Rva < Section->VirtualAddress + Section->SizeOfRawData) | |
{ | |
return Section; | |
} | |
Section++; | |
} | |
return NULL; | |
} | |
// | |
// Determines if pointer address resides within the .text section of dll. | |
// | |
BOOL | |
_IsExecPtr(PVOID dllbase, PVOID ptr) { | |
// | |
// null pointer? exit | |
// | |
if (!ptr) return FALSE; | |
auto nt = (PIMAGE_NT_HEADERS)((PBYTE)dllbase + ((PIMAGE_DOS_HEADER)dllbase)->e_lfanew); | |
ULONG_PTR img_start = (ULONG_PTR)dllbase; | |
ULONG_PTR img_end = (img_start + nt->OptionalHeader.SizeOfImage); | |
ULONG_PTR img_ptr = (ULONG_PTR)ptr; | |
// | |
// If the pointer is not within range of image, exit. | |
// | |
if (!(img_ptr > img_start && img_ptr < img_end)) return FALSE; | |
// | |
// If there's no section for RVA, exit | |
// | |
auto rva = (DWORD)(img_ptr - img_start); | |
auto s = GetSectionFromRva(nt, rva); | |
if (!s) return FALSE; | |
// | |
// If the name of section is ".text", we will assume it's executable code. | |
// | |
return (*(PDWORD)s->Name == *(PDWORD)".text"); | |
} | |
// | |
// | |
// | |
BOOL | |
GetNtDelegate(PNT_DELEGATE_CALLBACK Callback) { | |
auto m = (PBYTE)GetModuleHandleW(L"ntdll"); | |
auto nt = (PIMAGE_NT_HEADERS)(m + ((PIMAGE_DOS_HEADER)m)->e_lfanew); | |
auto sh = IMAGE_FIRST_SECTION(nt); | |
for (DWORD i=0; i<nt->FileHeader.NumberOfSections; i++) { | |
if (*(PDWORD)sh[i].Name != *(PDWORD)".text") continue; | |
auto rva = sh[i].VirtualAddress; | |
auto cnt = (sh[i].Misc.VirtualSize - sizeof(ULONG_PTR)) / sizeof(ULONG_PTR); | |
auto ptr = (PULONG_PTR)(m + rva); | |
for (DWORD j=0; j<cnt; j++) { | |
if (!_IsExecPtr(m, (LPVOID)ptr[j])) continue; | |
auto api = (PCHAR)ptr[j]; | |
if (!strcmp(api, Callback->Name)) { | |
Callback->Function.p = (PVOID)ptr[j + 1]; | |
return TRUE; | |
} | |
} | |
break; | |
} | |
return FALSE; | |
} | |
void | |
GetNtDelegateTable(PNT_DELEGATE_TABLE Table) { | |
GetNtDelegate(&Table->LdrInitializeThunk); | |
GetNtDelegate(&Table->RtlUserThreadStart); | |
GetNtDelegate(&Table->RtlDispatchAPC); | |
GetNtDelegate(&Table->KiUserExceptionDispatcher); | |
GetNtDelegate(&Table->KiUserApcDispatcher); | |
GetNtDelegate(&Table->KiUserCallbackDispatcher); | |
GetNtDelegate(&Table->KiRaiseUserExceptionDispatcher); | |
GetNtDelegate(&Table->LdrSystemDllInitBlock); | |
GetNtDelegate(&Table->LdrpChildNtdll); | |
GetNtDelegate(&Table->LdrParentInterlockedPopEntrySList); | |
GetNtDelegate(&Table->LdrParentRtlInitializeNtUserPfn); | |
GetNtDelegate(&Table->LdrParentRtlResetNtUserPfn); | |
GetNtDelegate(&Table->LdrParentRtlRetrieveNtUserPfn); | |
} | |
NT_DELEGATE_TABLE NtDelegateTable = { | |
{("LdrInitializeThunk"), NULL}, | |
{("RtlUserThreadStart"), NULL}, | |
{("RtlDispatchAPC"), NULL}, | |
{("KiUserExceptionDispatcher"), NULL}, | |
{("KiUserApcDispatcher"), NULL}, | |
{("KiUserCallbackDispatcher"), NULL}, | |
{("KiRaiseUserExceptionDispatcher"), NULL}, | |
{("LdrSystemDllInitBlock"), NULL}, | |
{("LdrpChildNtdll"), NULL}, | |
{("LdrParentInterlockedPopEntrySList"), NULL}, | |
{("LdrParentRtlInitializeNtUserPfn"), NULL}, | |
{("LdrParentRtlResetNtUserPfn"), NULL}, | |
{("LdrParentRtlRetrieveNtUserPfn"), NULL} | |
}; | |
int | |
main(void) { | |
GetNtDelegateTable(&NtDelegateTable); | |
printf("LdrInitializeThunk : %p\n", NtDelegateTable.LdrInitializeThunk.Function.p); | |
printf("RtlUserThreadStart : %p\n", NtDelegateTable.RtlUserThreadStart.Function.p); | |
printf("RtlDispatchAPC : %p\n", NtDelegateTable.RtlDispatchAPC.Function.p); | |
printf("KiUserExceptionDispatcher : %p\n", NtDelegateTable.KiUserExceptionDispatcher.Function.p); | |
printf("KiUserApcDispatcher : %p\n", NtDelegateTable.KiUserApcDispatcher.Function.p); | |
printf("KiUserCallbackDispatcher : %p\n", NtDelegateTable.KiUserCallbackDispatcher.Function.p); | |
printf("KiRaiseUserExceptionDispatcher : %p\n", NtDelegateTable.KiRaiseUserExceptionDispatcher.Function.p); | |
printf("LdrSystemDllInitBlock : %p\n", NtDelegateTable.LdrSystemDllInitBlock.Function.p); | |
printf("LdrpChildNtdll : %p\n", NtDelegateTable.LdrpChildNtdll.Function.p); | |
printf("LdrParentInterlockedPopEntrySList : %p\n", NtDelegateTable.LdrParentInterlockedPopEntrySList.Function.p); | |
printf("LdrParentRtlInitializeNtUserPfn : %p\n", NtDelegateTable.LdrParentRtlInitializeNtUserPfn.Function.p); | |
printf("LdrParentRtlResetNtUserPfn : %p\n", NtDelegateTable.LdrParentRtlResetNtUserPfn.Function.p); | |
printf("LdrParentRtlRetrieveNtUserPfn : %p\n", NtDelegateTable.LdrParentRtlRetrieveNtUserPfn.Function.p); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment