fusu
fusu

Reputation: 324

c++ access PEB_LDR_DATA struct member by offset

I am new to c++ and I am trying to access the InLoadOrderModuleList member in PEB_LDR_DATA structure.

I tried this:

// the ldrData data type is PPEB_LDR_DATA 
PLIST_ENTRY firstitem_InMemoryOrderModuleList = ((PLIST_ENTRY)(pebLdrData + 0x0010)-> Flink);

without success. How should I accessing it?

Upvotes: 0

Views: 1664

Answers (3)

vengy
vengy

Reputation: 2257

Thanks to fusu's answer, the solution is to declare my own custom typedef structures for LDR_DATA_TABLE_ENTRY, PEB_LDR_DATA and PEB. The program outputs

Kernel32.dll Base Address: 0x75C80000

#include <stdio.h>
#include <stdint.h>
#include <windows.h>

typedef struct MY_PEB_LDR_DATA {
    ULONG Length;
    UCHAR Initialized;
    VOID* SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    // ... other fields
} MY_PEB_LDR_DATA, *MY_PPEB_LDR_DATA;

typedef struct MY_LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    // ... other fields
} MY_LDR_DATA_TABLE_ENTRY, *MY_PLDR_DATA_TABLE_ENTRY;

typedef struct MY_PEB {
    BYTE Reserved1[2];
    BYTE BeingDebugged;
    BYTE Reserved2[1];
    PVOID Reserved3[2];
    MY_PPEB_LDR_DATA Ldr;
    // ... other fields
} MY_PEB, *MY_PPEB;

int main()
{
    MY_PPEB peb = (MY_PPEB)__readfsdword(0x30);
    uintptr_t kernel32Base = 0;

    // Order of modules loaded into the process: [image][ntdll][kernel32]
    // Skip first two entries as kernel32.dll is always the third entry.
    PLIST_ENTRY ptr = peb->Ldr->InLoadOrderModuleList.Flink->Flink->Flink;
    MY_PLDR_DATA_TABLE_ENTRY e = CONTAINING_RECORD(ptr, MY_LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
    kernel32Base = (uintptr_t)e->DllBase;

    printf("Kernel32.dll Base Address: 0x%p\n", (void*)kernel32Base);

    return 0;
}

Notes

InLoadOrderModuleList This list contains the modules in the order they were loaded into the process. Typically, the executable itself is the first entry, followed by important system DLLs like ntdll.dll and kernel32.dll.

InMemoryOrderModuleList This list contains the modules in the order they are laid out in memory. It is not necessarily the same as the load order, although it often is similar.

Upvotes: 0

fusu
fusu

Reputation: 324

The solution is to declare typedef structures of LDR_DATA_TABLE_ENTRY and PEB_LDR_DATA with its full structure.

Upvotes: 1

josh poley
josh poley

Reputation: 7479

LIST_ENTRY is how Windows does linked lists internally. There is plenty of information about them online if you need more details, but there are two things you need to know here:

  1. is that the next/back pointers don't point to the head of the object (which is common in most implementations); so in order to get to the head of the object you have to do a fixup on the pointer based on the offset of the LIST_ENTRY member. This is where the CONTAINING_RECORD macro comes into use.
  2. is that you don't want to this fixup on the first LIST_ENTRY in the PEB_LDR_DATA object, think of those as the "head" pointer, and you need to move through the Flink before you get to the data that you care about.

Sample code:

LIST_ENTRY *current_record = NULL;
LIST_ENTRY *start = &(pebLdrData->InLoadOrderModuleList);

// move off the initial list entry to the first actual object
current_record = start->Flink;

while (true)
{
    // find the head of the object
    LDR_DATA_TABLE_ENTRY *module_entry = (LDR_DATA_TABLE_ENTRY*) 
        CONTAINING_RECORD(current_record, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);

    printf("%wZ\n", &module_entry->BaseDllName);

    // advance to the next object
    current_record = current_record->Flink;

    if (current_record == start)
    {
        break;
    }
}

Upvotes: 1

Related Questions