I'm trying to get a list of the loaded modules within my application (pertaining to security/shellcode so please refrain from WINAPI calls). I'm iterating over the PEB->Ldr doubly linked list of modules, but every time I print the DLL's name, it simply prints the name and path of the currently executing application.
In others' code, I've seen that they just case the current LIST_ENTRY
pointer to be a PLDR_DATA_TABLE_ENTRY
and you can directly call FullDllName
that way. However, to actually get the base address, for example, you need to call Reserved2[0]
instead of DllBase
which is understandable due to the fact that the LIST_ENTRY
is 8 bytes into the struct, but it doesn't explain why you can call FullDllName
directly.
Here's an example. Note the return (HMODULE)pLdrDataTableEntry->Reserved2[0];
I'm using Windows 10 x64 in x86 Release Mode with Visual Studio 2015.
void ListModules(void) {
PPEB lpPeb = __readfsdword(0x30);
PPEB_LDR_DATA lpLdr = lpPeb->Ldr;
PLIST_ENTRY lpFirst, lpCurrent;
lpFirst = lpCurrent = lpLdr->InMemoryOrderModuleList.Flink;
do {
PLDR_DATA_TABLE_ENTRY lpDataTable = CONTAINING_RECORD(lpFirst, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
wprintf(L"%s ~ %d ~ 0x%08x\n", lpDataTable->FullDllName.Buffer, lpDataTable->DllBase, (DWORD)lpCurrent);
lpCurrent= lpCurrent ->Flink;
} while (lpCurrent && lpFirst != lpCurrent);
}
The output I receive is just multiple references to the current application name:
C:\Programs\Project\file.exe ~ 2818048 ~ 0x00d53a18
C:\Programs\Project\file.exe ~ 2818048 ~ 0x00d53930
C:\Programs\Project\file.exe ~ 2818048 ~ 0x00d53da8
C:\Programs\Project\file.exe ~ 2818048 ~ 0x00d54078
C:\Programs\Project\file.exe ~ 2818048 ~ 0x00d54a68
C:\Programs\Project\file.exe ~ 2818048 ~ 0x00d54910
C:\Programs\Project\file.exe ~ 2818048 ~ 0x7743fbf4
This is very likely do to MSDN's infamous undocumentation, but how can I resolve this, preferably in a 'standard' way that wouldn't require defining my own structure, although I'm certainly not against it.
Am I not iterating correctly?
look at to this line
PLDR_DATA_TABLE_ENTRY lpDataTable = CONTAINING_RECORD(lpFirst, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
but lpFirst
you not changing in loop ! so and got the same records all time. you need change lpFirst
to lpCurrent
also
while (lpCurrent && lpFirst != lpCurrent);
lpCurrent
never become the NULL - this is circle list, so condition must be
while (lpFirst != lpCurrent)
also must be not
lpFirst = lpLdr->InMemoryOrderModuleList.Flink;
but
lpFirst = &lpLdr->InMemoryOrderModuleList;
of course access to this list must be synchronized (LdrpLoaderLock
critical section) but, if you want anyway ..
!! this is not recommended to use !! only for demo/test
void ListModules() {
PPEB lpPeb = (PPEB)((_TEB*)NtCurrentTeb())->ProcessEnvironmentBlock;
PPEB_LDR_DATA lpLdr = lpPeb->Ldr;
PLIST_ENTRY lpHead = &lpLdr->InMemoryOrderModuleList, lpCurrent = lpHead;
while ((lpCurrent = lpCurrent ->Flink) != lpHead)
{
PLDR_DATA_TABLE_ENTRY lpDataTable = CONTAINING_RECORD(lpCurrent, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
DbgPrint("%p %wZ\n", lpDataTable->DllBase, &lpDataTable->FullDllName);
}
}
but even in shellcode better first got pointers to some api, and then use it. for example LdrEnumerateLoadedModules
this is not recommendation to use. demo only for OP
void CALLBACK EnumModules(PLDR_DATA_TABLE_ENTRY mod, PVOID /*UserData*/, PBOOLEAN bStop )
{
*bStop = FALSE;
DbgPrint("%p %wZ\n", mod->DllBase, &mod->FullDllName);
}
LdrEnumerateLoadedModules(0, EnumModules, 0);