Enum Modules Method 1

You will find below an example of how to enumerate process modules using the well known Windows API CreateToolHelp32Snapshot(), I will cover additional methods soon.

You may notice that when using CreateToolHelp32Snapshot(), first result (row) is generally the Image Path of the process owning module. I ignore that row by checking the value of szExePath with owner process image path.

GetProcessName() is compatible since Windows Vista. It is possible to support Windows XP and below but not in this example.

You will find GetProcessName() and alternatives in separated snippets threads.

(*******************************************************************************
  Author:
    ->  Jean-Pierre LESUEUR (@DarkCoderSc)
        https://github.com/DarkCoderSc
        https://gist.github.com/DarkCoderSc
        https://www.phrozen.io/
  License:
    -> MIT
*******************************************************************************)

unit UntEnumModules;

interface

uses Classes, Windows, tlHelp32, SysUtils, Generics.Collections, psAPI;

function EnumModules(ATargetProcessID : Cardinal) : TList<TModuleEntry32>;

implementation

{-------------------------------------------------------------------------------
  One possible method to get process image path from process id.

  Doesn't support Windows XP and below, Windows XP is dead :-( - One technique
  would be to use GetModuleFileNameExW()
-------------------------------------------------------------------------------}
function GetProcessName(AProcessID : Cardinal) : String;
var hProc      : THandle;
    ALength    : DWORD;
    hDLL       : THandle;

    QueryFullProcessImageNameW : function(
                                            AProcess: THANDLE;
                                            AFlags: DWORD;
                                            AFileName: PWideChar;
                                            var ASize: DWORD): BOOL; stdcall;

const PROCESS_QUERY_LIMITED_INFORMATION = $00001000;
begin
  result := '';
  ///

  if (TOSVersion.Major < 6) then  
    Exit();
  ///
  
  QueryFullProcessImageNameW := nil;
  
  hDLL := LoadLibrary('kernel32.dll');
  if hDLL = 0 then
    Exit();  
  try
    @QueryFullProcessImageNameW := GetProcAddress(hDLL, 'QueryFullProcessImageNameW');
    ///
    
    if Assigned(QueryFullProcessImageNameW) then begin
      hProc := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, AProcessID);
      if hProc = 0 then exit;
      try
        ALength := (MAX_PATH * 2);
        
        SetLength(result, ALength);
        
        if NOT QueryFullProcessImageNameW(hProc, 0, @result[1], ALength) then 
          Exit();

        SetLength(result, ALength); // Get rid of extra junk
      finally
        CloseHandle(hProc);
      end;
    end;
  finally
    FreeLibrary(hDLL);
  end;
end;

{
  Enumerate target process modules (Loaded DLL's)
}
function EnumModules(ATargetProcessID : Cardinal) : TList<TModuleEntry32>;
var ASnap        : THandle;
    AModuleEntry : TModuleEntry32;
    AOwnerPath   : String;

const TH32CS_SNAPMODULE32 = $00000010;

    procedure Append();
    begin
      if AOwnerPath.ToLower = String(AModuleEntry.szExePath).ToLower then
        Exit(); // Ignore
        
      result.Add(AModuleEntry);
    end;

begin
  result := TList<TModuleEntry32>.Create();
  ///

  AOwnerPath := GetProcessName(ATargetProcessId);
  
  ASnap := CreateToolHelp32Snapshot(TH32CS_SNAPMODULE or TH32CS_SNAPMODULE32, ATargetProcessId);
  if ASnap = INVALID_HANDLE_VALUE then begin
    Exit;
  end;
  try
    ZeroMemory(@AModuleEntry, SizeOf(TModuleEntry32));
    
    AModuleEntry.dwSize := SizeOf(TModuleEntry32);
    ///

    if NOT Module32First(ASnap, AModuleEntry) then begin
      Exit();
    end;

    Append();

    while True do begin
      ZeroMemory(@AModuleEntry, SizeOf(TModuleEntry32));
      
      AModuleEntry.dwSize := SizeOf(TModuleEntry32);
      ///

      if NOT Module32Next(ASnap, AModuleEntry) then begin
        Break;
      end;

      Append();
    end;
  finally
    CloseHandle(ASnap);
  end;
end;

end.
comments powered by Disqus