Iterating over PEB DllName shows only exe name

1.9k views Asked by At

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?

1

There are 1 answers

4
RbMm On BEST ANSWER

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);