Search code examples
cwindowswinapishellcode

Iterating over PEB DllName shows only exe name


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?


Solution

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