Get Process Name Method 2 GetMappedFilename

Bellow code demonstrate our to retrieve both current and target process full image path. This technique is very uncommon but works perfectly.

Notice for both techniques you muse translate its physical path to virtual path using this tiny function
// Jean-Pierre LESUEUR (@DarkCoderSc)

function PhysicalToVirtualPath(APath : String) : String;
var i          : integer;
    ADrive     : String;
    ABuffer    : array[0..MAX_PATH-1] of Char;
    ACandidate : String;
begin
  {$I-}
  for I := 0 to 25 do begin
    ADrive := Format('%s:', [Chr(Ord('A') + i)]);
    ///

    if (QueryDosDevice(PWideChar(ADrive), ABuffer, MAX_PATH) = 0) then
      continue;

    ACandidate := String(ABuffer).ToLower();

    if String(Copy(APath, 1, Length(ACandidate))).ToLower() = ACandidate then begin
      Delete(APath, 1, Length(ACandidate));

      result := Format('%s%s', [ADrive, APath]);
    end;
  end;
  {$I+}
end;

And both examples require psapi.pas. To avoid using psapi.pas you could load GetMappedFilename() API dynamically.

Current Process

// Jean-Pierre LESUEUR (@DarkCoderSc)

// ...
uses psAPI;
// ...

function GetCurrentProcessImagePath() : String;
var AFileName : Array[0..MAX_PATH -1] of Char;
begin
    ZeroMemory(@AFileName, MAX_PATH);
    ///

    GetMappedFileName(
                          GetCurrentProcess(),
                          Pointer(GetModuleHandle(nil)),
                          AFileName,
                          MAX_PATH
    );

    result := PhysicalToVirtualPath(UnicodeString(AFileName));
end;

Target Process

When it comes to use that technique for remote process you need to be a bit more tricky, this technique require code injection to retrieve target process hInstance. Then we can use normally GetMappedFileName() and retrieve it full image path.

Notice, you can’t use this technique from a 32bit to 64bit / 64bit to 32bit due to code injection (Some technique exists to bypass this restriction tho).

// Jean-Pierre LESUEUR (@DarkCoderSc)

// ...
uses psAPI;
// ...

function GetProcessImagePath(const AProcessId : Cardinal) : String;
var hProc            : THandle;
    pGetModuleHandle : Pointer;
    AFlags           : Cardinal;
    AThreadId        : Cardinal;
    hThread          : THandle;
    hRemoteInstance  : Cardinal;
    AFileName        : Array[0..MAX_PATH -1] of Char;

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

  {
    Alternatively in the case of Kernel32.dll we could simply call GetModuleHandle('Kernel32.dll')
  }
  pGetModuleHandle := GetProcAddress(LoadLibrary('Kernel32.dll'), 'GetModuleHandleW');
  ///

  if NOT Assigned(pGetModuleHandle) then
    Exit();

  AFlags := PROCESS_CREATE_THREAD or
            PROCESS_QUERY_LIMITED_INFORMATION;

  hProc := OpenProcess(AFlags, false, AProcessId);
  if (hProc = 0) then
    Exit();
  try
    hThread := CreateRemoteThread(
                                    hProc,
                                    nil,
                                    0,
                                    pGetModuleHandle,
                                    Pointer(nil),
                                    0,
                                    AThreadId
    );
    if (hThread = 0) then
      Exit();
    try
      WaitForSingleObject(hThread, INFINITE);

      GetExitCodeThread(hThread, hRemoteInstance);

      ZeroMemory(@AFileName, MAX_PATH);
      ///

      GetMappedFileName(
                            hProc,
                            Pointer(hRemoteInstance),
                            AFileName,
                            MAX_PATH
      );

      result := PhysicalToVirtualPath(UnicodeString(AFileName));
    finally
      CloseHandle(hThread);
    end;
  finally
    CloseHandle(hProc);
  end;
end;

If you have another technique instead of using Code Injection to capture target process hInstance I would love to know :)

comments powered by Disqus