Enumerate Local Process Modules via PEB - Delphi

DarkCoderSc personal avatar
DarkCoderSc

Jean-Pierre LESUEUR

uses
 System.SysUtils, Winapi.Windows, Generics.Collections;

// ...

type
  _LDR_DATA_TABLE_ENTRY = record
    InLoadOrderLinks            : TListEntry;
    InMemoryOrderLinks          : TListEntry;
    InInitializationOrderLinkes : TListEntry;

    DllBase                     : PVOID;
    EntryPoint                  : PVOID;
    SizeOfImage                 : ULONG;
    FullDllName                 : TUnicodeString;
    BaseDllName                 : TUnicodeString;
  end;
  TLdrDataTableEntry = _LDR_DATA_TABLE_ENTRY;
  PLdrDataTableEntry = ^TLdrDataTableEntry;

// ...

(* TModuleInformation *)

type
  TModuleInformation = class
  private
    FImagePath   : String;
    FBaseAddress : NativeUInt;
    FEntryPoint  : NativeUInt;
    FSizeOfImage : Cardinal;
  public
    {@C}
    constructor Create(const AImagePath : String; const ABaseAddress, AEntryPoint: NativeUInt; const ASizeOfImage : Cardinal);

    {@G}
    property ImagePath   : String     read FImagePath;
    property BaseAddress : NativeUInt read FBaseAddress;
    property EntryPoint  : NativeUInt read FEntryPoint;
    property SizeOfImage : Cardinal   read FSizeOfImage;
  end;

// ...

constructor TModuleInformation.Create(const AImagePath : String; const ABaseAddress, AEntryPoint: NativeUInt; const ASizeOfImage : Cardinal);
begin
  inherited Create();
  ///

  FImagePath   := AImagePath;
  FBaseAddress := ABaseAddress;
  FEntryPoint  := AEntryPoint;
  FSizeOfImage := ASizeOfImage;
end;

(* Local *)

function GetCurrentProcessPEB() : NativeUInt;
asm
  {$IFDEF WIN64}
    mov rax, gs:[$60];
    mov result, rax;
  {$ELSE}
    mov eax, fs:[$30];
    mov result, eax;
  {$ENDIF}
end;

function EnumerateCurrentProcessModules(var AList : TObjectList<TModuleInformation>) : Cardinal;
begin
  if not Assigned(AList) then
    AList := TObjectList<TModuleInformation>.Create(True)
  else begin
    AList.Clear();

    ///
    if not AList.OwnsObjects then
      AList.OwnsObjects := True;
  end;
  ///

  var pPEBLdrData := PPointer(GetCurrentProcessPEB() + {$IFDEF WIN64}$00000018{$ELSE}$0000000c{$ENDIF});
  var pFirstEntry := PListEntry(NativeUInt(pPEBLdrData^) + {$IFDEF WIN64}$00000010{$ELSE}$0000000c{$ENDIF});

  var pDataTableEntry := PLdrDataTableEntry(pFirstEntry^.Flink);

  repeat
    AList.Add(TModuleInformation.Create(
      string(pDataTableEntry^.FullDllName.Buffer),
      NativeUInt(pDataTableEntry^.DllBase),
      NativeUInt(pDataTableEntry^.EntryPoint),
      pDataTableEntry^.SizeOfImage
    ));

    ///
    pDataTableEntry := PLdrDataTableEntry(pDataTableEntry^.InLoadOrderLinks.Flink);
  until pFirstEntry = pDataTableEntry^.InLoadOrderLinks.Flink;

  ///
  result := AList.Count;
end;

// ...

begin
try
  var AList := TObjectList<TModuleInformation>.Create(True);
  try
    EnumerateCurrentProcessModules(AList);
    ///

    for var AModuleEntry in AList do
      WriteLn(Format('Base:[0x%p], EP:[0x%p], Size:[0x%p] -> %s', [
        Pointer(AModuleEntry.BaseAddress),
        Pointer(AModuleEntry.EntryPoint),
        Pointer(AModuleEntry.SizeOfImage),
        AModuleEntry.ImagePath
      ]));
  finally
    FreeAndNil(AList);
  end;

// ...

Creating and researching code snippets takes time and effort. You’re welcome to share them through your own platforms, but please don’t forget to credit the original author, here: Jean-Pierre LESUEUR.

Depends On

Implemented By Technique


Created

June 2, 2025

Last Revised

June 2, 2025