Enumerate Process via NtQuerySystemInformation - Delphi

DarkCoderSc personal avatar
DarkCoderSc

Jean-Pierre LESUEUR

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

// ...

const
  SYSTEM_PROCESS_INFORMATION_CLASS = 5;

// ...

type
  UNICODE_STRING = record
    Length         : USHORT;
    MaximumLength  : USHORT;
    Buffer         : PWideChar;
  end;
  TUnicodeString = UNICODE_STRING;
  PUnicodeString = ^TUnicodeString;

  SYSTEM_PROCESS_INFORMATION = record
    NextEntryOffset              : ULONG;
    NumberOfThreads              : ULONG;
    WorkingSetPrivateSize        : LARGE_INTEGER;
    HardFaultCount               : ULONG;
    NumberOfThreadsHighWaterMark : ULONG;
    CycleTime                    : ULONGLONG;
    CreateTime                   : _FILETIME;
    UserTimer                    : LARGE_INTEGER;
    KernelTime                   : LARGE_INTEGER;
    ModuleName                   : TUnicodeString;
    BasePriority                 : LONG;
    ProcessID                    : NativeUInt;
    InheritedFromProcessId       : NativeUInt;
    HandleCount                  : ULONG;
    SessionId                    : ULONG;
    UniqueProcessKey             : ULONG_PTR;
    PeakVirtualSize              : ULONG_PTR;
    VirtualSize                  : ULONG_PTR;
    PageFaultCount               : ULONG;
    PeakWorkingSetSize           : ULONG_PTR;
    WorkingSetSize               : ULONG_PTR;
    QuotePeakPagedPoolUsage      : ULONG_PTR;
    QuotaPagedPoolUsage          : ULONG_PTR;
    QuotaPeakNonPagedPoolUsage   : ULONG_PTR;
    QuotaNonPagedPoolUsage       : ULONG_PTR;
    PagefileUsage                : ULONG_PTR;
    PeakPagefileUsage            : ULONG_PTR;
    PrivatePageCount             : ULONG_PTR;
    ReadOperationCount           : LARGE_INTEGER;
    WriteOperationCount          : LARGE_INTEGER;
    OtherOperationCount          : LARGE_INTEGER;
    ReadTransferCount            : LARGE_INTEGER;
    WriteTransferCount           : LARGE_INTEGER;
    OtherTransferCount           : LARGE_INTEGER
  end;
  TSystemProcessInformation = SYSTEM_PROCESS_INFORMATION;
  PSystemProcessInformation = ^TSystemProcessInformation;

// ...

function NtQuerySystemInformation(
  SystemInformationClass  : DWORD;
  SystemInformation       : Pointer;
  SystemInformationLength : DWORD;
  var ReturnLength        : DWORD
) : Cardinal; stdcall; external 'NTDLL.DLL';

// ...

function EnumerateProcess_NtQuerySystemInformation(var AList : TDictionary<Cardinal, String>) : Cardinal;
begin
  result := 0;

  if not Assigned(AList) then
    AList := TDictionary<Cardinal, String>.Create()
  else
    AList.Clear();
  ///

  var AReturnLength : DWORD;

  var ARet := NtQuerySystemInformation(SYSTEM_PROCESS_INFORMATION_CLASS, nil, 0, AReturnLength);
  if (ARet <> 0) and (ARet <> $C0000004) (* STATUS_INFO_LENGTH_MISMATCH *) then
    raise EWindowsException.Create('NtQuerySystemInformation(1)');

  var pFirstRow : PSystemProcessInformation;

  GetMem(pFirstRow, AReturnLength);
  try
    ARet := NtQuerySystemInformation(SYSTEM_PROCESS_INFORMATION_CLASS, pFirstRow, AReturnLength, AReturnLength);
    if ARet <> 0 then
      raise EWindowsException.Create('NtQuerySystemInformation(2)');

    var pNextRow := pFirstRow;
    while True do begin
      var AProcessName := '';
      if pNextRow^.ModuleName.Length = 0 then
        AProcessName := 'Unknown'
      else
        AProcessName := String(pNextRow^.ModuleName.Buffer);

      AList.Add(pNextRow^.ProcessID, AProcessName);

      if pNextRow^.NextEntryOffset = 0 then
        break;

      ///
      pNextRow := Pointer(NativeUInt(pNextRow) + pNextRow.NextEntryOffset);
    end;
  finally
    FreeMem(pFirstRow, AReturnLength);
  end;

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

// ...

var AList := TDictionary<Cardinal, String>.Create();
try
  EnumerateProcess_NtQuerySystemInformation(AList);

  for var AProcessId in AList.Keys do begin
    var AImagePath : String;
    if not AList.TryGetValue(AProcessId, AImagePath) then
      continue;

    WriteLn(Format('%s (%d)', [
      AImagePath,
      AProcessId
    ]));
  end;
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.

Implemented By Technique


Created

April 24, 2025

Last Revised

April 24, 2025