Note that this is for listing the DLL from inside the process, and not from a different one.
It is important to use Loader Lock when dealing with the loader internal structures. It will also make you tread-safe, and make sure that nobody will load another DLL while you are processing the current list.
Alternative to part 1 is the WinAPI call EnumProcessModules.
Part 1: listing currently loaded DLLs
#include <Windows.h>
#include <winnt.h>
#include <winternl.h>
void ListCurrentModules()
{
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
NTSTATUS(NTAPI *LdrLockLoaderLock)(ULONG Flags, ULONG *State, UINT_PTR *Cookie)
= (NTSTATUS(NTAPI *)(ULONG, ULONG *, UINT_PTR *))GetProcAddress(ntdll, "LdrLockLoaderLock");
NTSTATUS(NTAPI *LdrUnlockLoaderLock)(ULONG Flags, UINT_PTR Cookie)
= (NTSTATUS(NTAPI *)(ULONG, UINT_PTR))GetProcAddress(ntdll, "LdrUnlockLoaderLock");
UINT_PTR ldrCookie;
LdrLockLoaderLock(0, NULL, &ldrCookie);
#if defined(_M_X64) // x64
auto pTeb = reinterpret_cast<PTEB>(__readgsqword(reinterpret_cast<UINT_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
#elif defined(_M_ARM) // ARM
auto pTeb = reinterpret_cast<PTEB>(_MoveFromCoprocessor(15, 0, 13, 0, 2)); // CP15_TPIDRURW
#else // x86
auto pTeb = reinterpret_cast<PTEB>(__readfsdword(reinterpret_cast<DWORD>(&static_cast<NT_TIB*>(nullptr)->Self)));
#endif
auto pPeb = pTeb->ProcessEnvironmentBlock;
auto pLdrData = pPeb->Ldr;
auto pModListHdr = &pLdrData->InMemoryOrderModuleList;
for (auto pModListCurrent = pModListHdr->Flink; pModListCurrent != pModListHdr; pModListCurrent = pModListCurrent->Flink)
{
// Get current module in list
auto pModEntry = reinterpret_cast<PLDR_DATA_TABLE_ENTRY>(pModListCurrent);
HMODULE moduleHandle = (HMODULE)pModEntry->Reserved2[0]; // ModuleBaseAddress
printf("Module [%S] handle is [%p]\n",
pModEntry->FullDllName.Buffer, moduleHandle);
}
LdrUnlockLoaderLock(0, ldrCookie);
LdrUnlockLoaderLock(0, ldrCookie);
}
Part 2: Getting notifications for DLL loading
I'm using the non official function, LdrRegisterDllNotification.
enum NotificationReasonEnum
{
LDR_DLL_NOTIFICATION_REASON_LOADED = 1,
LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2
};
typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA {
ULONG Flags; //Reserved.
PCUNICODE_STRING FullDllName; //The full path name of the DLL module.
PCUNICODE_STRING BaseDllName; //The base file name of the DLL module.
PVOID DllBase; //A pointer to the base address for the DLL in memory.
ULONG SizeOfImage; //The size of the DLL image, in bytes.
} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;
typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA {
ULONG Flags; //Reserved.
PCUNICODE_STRING FullDllName; //The full path name of the DLL module.
PCUNICODE_STRING BaseDllName; //The base file name of the DLL module.
PVOID DllBase; //A pointer to the base address for the DLL in memory.
ULONG SizeOfImage; //The size of the DLL image, in bytes.
} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
typedef union _LDR_DLL_NOTIFICATION_DATA {
LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;
void NTAPI DllNotificationCallback(NotificationReasonEnum eAction, LDR_DLL_NOTIFICATION_DATA *NotificationData, void *Context)
{
if (eAction == LDR_DLL_NOTIFICATION_REASON_LOADED)
{
HANDLE moduleHandle = NotificationData->Loaded.DllBase;
printf("Module [%S] dynamically loaded handle=%p\n", NotificationData->Loaded.BaseDllName->Buffer, moduleHandle);
}
}
void RegisterDLLNotifications(PVOID Context)
{
HMODULE ntdll = LoadLibraryA("ntdll.dll");
NTSTATUS(NTAPI *LdrRegisterDllNotification)(ULONG Flags, void *NotificationFunction, PVOID Context, PVOID *Cookie)
= (NTSTATUS(NTAPI *)(ULONG, void *, PVOID, PVOID *))GetProcAddress(ntdll, "LdrRegisterDllNotification");
void *cookie;
LdrRegisterDllNotification(0, (void*)DllNotificationCallback, Context, &cookie);
}
Context can be NULL. Surprisingly, cookie is not optional, but you can just discard the value if you don't need to unregister the callback.
No comments:
Post a Comment