Skip to content

Instantly share code, notes, and snippets.

@odzhan
Created February 13, 2024 21:47
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save odzhan/d677d9d5b6b31d8cabf9277bc14a3856 to your computer and use it in GitHub Desktop.
Save odzhan/d677d9d5b6b31d8cabf9277bc14a3856 to your computer and use it in GitHub Desktop.
Delegate NT DLL Table
//
// 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