Get Any Process Current Directory

It’s an undocumented api in RTL_USER_PROCESS_PARAMETERS. Original code is found at this StackOverflow answer. After doing some tests, I finally got codes below (x86 only):

RGSS

pid = 17104
require 'api'
handle = Kernel32.OpenProcess(0x001f0fff, 1, pid)
pbi = buf('L6')
len = buf('L')
Ntdll = dll('Ntdll')
Ntdll.NtQueryInformationProcess(handle, 0, pbi, 24, len)
peb = buf('L4LL113')
Kernel32.ReadProcessMemory(handle, pbi.unpack[1], peb, 472, len)
upp = buf('L9SSLL7')
Kernel32.ReadProcessMemory(handle, peb.unpack[4], upp, 72, len)
path = buf("a#{upp.unpack[9] + 2}")
Kernel32.ReadProcessMemory(handle, upp.unpack[11], path, upp.unpack[9], len)
cwd = path.unpack[0].from_ws
kernel32.CloseHandle(handle)

C++

#include <windows.h>
#include <winternl.h>
#include <stdio.h>
#include <wchar.h>

int main(int argc, char **argv) {
  int pid;
  if (argc <= 1) {
    printf_s("usage: %s <pid>", argv[0]);
    return 0;
  }
  else {
    pid = atoi(argv[1]);
  }
  HMODULE dll = LoadLibrary(L"Ntdll");
  typedef NTSTATUS(NTAPI *NtQueryInformationProcess_t)(
    IN HANDLE ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL
  );
  NtQueryInformationProcess_t NtQueryInformationProcess = (NtQueryInformationProcess_t)GetProcAddress(dll, "NtQueryInformationProcess");

  PROCESS_BASIC_INFORMATION   pbi = {};
  RTL_USER_PROCESS_PARAMETERS upp = {};
  PEB   peb = {};
  DWORD len;

  HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);

  int ret = NtQueryInformationProcess(handle, ProcessBasicInformation, &pbi,sizeof(pbi), &len);
  if (ret) {
    wprintf_s(L"Failed (%d)\n", ret);
    return ret;
  }
  ReadProcessMemory(handle, pbi.PebBaseAddress, &peb, sizeof(PEB), &len);
  ReadProcessMemory(handle, peb.ProcessParameters, &upp, sizeof(RTL_USER_PROCESS_PARAMETERS), &len);

  UNICODE_STRING CurrentDirectoryPath = *(UNICODE_STRING*)(upp.Reserved2 + 5);
  WCHAR *path = new WCHAR[CurrentDirectoryPath.Length / 2 + 1];
  ReadProcessMemory(handle, CurrentDirectoryPath.Buffer, path, CurrentDirectoryPath.Length, &len);
  path[CurrentDirectoryPath.Length / 2] = 0;
  wprintf_s(L"%ls\n", path);

  CloseHandle(handle)
  FreeLibrary(dll);
}

C#

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace GetCwdFromPid
{
    class Program
    {
        [DllImport("ntdll.dll", SetLastError = true)]
        static extern int NtQueryInformationProcess(
            IntPtr processHandle,
            int processInformationClass,
            IntPtr processInformation,
            uint processInformationLength,
            IntPtr returnLength
        );

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct PROCESS_BASIC_INFORMATION
        {
            public IntPtr ExitStatus;
            public IntPtr PebBaseAddress;
            public IntPtr AffinityMask;
            public IntPtr BasePriority;
            public UIntPtr UniqueProcessId;
            public IntPtr InheritedFromUniqueProcessId;

            public static int Size {
                get { return Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); }
            }
        }

        [Flags]
        public enum ProcessAccessFlags : uint
        {
            All = 0x001F0FFF
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr OpenProcess(
            ProcessAccessFlags processAccess,
            bool bInheritHandle,
            int processId
        );

        public static IntPtr OpenProcess(Process proc, ProcessAccessFlags flags) {
            return OpenProcess(flags, true, proc.Id);
        }

        private enum PROCESSINFOCLASS : int
        {
            ProcessBasicInformation = 0
        };

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool CloseHandle(IntPtr hHandle);

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct PEB
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2 + 1 + 1 + 4 * 2 + 4)]
            public byte[] Unused1;
            public IntPtr ProcessParameters;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 104 + 4 * 52 + 1 + 128 + 4 * 1 + 4)]
            public byte[] Unused2;

            public static int Size {
                get { return Marshal.SizeOf(typeof(PEB)); }
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct UNICODE_STRING : IDisposable
        {
            public ushort Length;
            public ushort MaximumLength;
            public IntPtr Buffer;

            public UNICODE_STRING(string s) {
                Length = (ushort)(s.Length * 2);
                MaximumLength = (ushort)(Length + 2);
                Buffer = Marshal.StringToHGlobalUni(s);
            }

            public void Dispose() {
                Marshal.FreeHGlobal(Buffer);
                Buffer = IntPtr.Zero;
            }

            public override string ToString() {
                return Marshal.PtrToStringUni(Buffer);
            }

            public static int Size {
                get { return Marshal.SizeOf(typeof(UNICODE_STRING)); }
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct RTL_USER_PROCESS_PARAMETERS
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9 * 4)]
            public byte[] Unused1;
            public UNICODE_STRING CurrentDirectoryPath;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7 * 4)]
            public byte[] Unused2;

            public static int Size {
                get { return Marshal.SizeOf(typeof(RTL_USER_PROCESS_PARAMETERS)); }
            }
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool ReadProcessMemory(
           IntPtr hProcess,
           IntPtr lpBaseAddress,
           IntPtr lpBuffer,
           int nSize,
           IntPtr lpNumberOfBytesRead);

        [DllImport("kernel32.dll")]
        static extern void RtlZeroMemory(IntPtr dst, int length);

        public static string[] ImageNames = { "RPGVXAce", "RPGVX", "RPGXP" };

        public static void Main(string[] args) {
            foreach (string image in ImageNames) {
                TestGetProcessWorkingDirectory(image);
            }
        }

        private static void TestGetProcessWorkingDirectory(string image) {
            Process[] ps = Process.GetProcessesByName(image);
            foreach (Process p in ps) {
                Console.WriteLine($"{p.ProcessName} pid: {p.Id}");

                IntPtr handle = OpenProcess(p, ProcessAccessFlags.All);
                IntPtr pPbi = Marshal.AllocHGlobal(PROCESS_BASIC_INFORMATION.Size);
                IntPtr outLong = Marshal.AllocHGlobal(sizeof(long));

                Console.WriteLine($"handle: {handle}");

                int ret = NtQueryInformationProcess(handle, 0,
                    pPbi, (uint)PROCESS_BASIC_INFORMATION.Size, outLong);

                if (ret != 0) {
                    Console.WriteLine($"Error {ret}");
                    break;
                }

                PROCESS_BASIC_INFORMATION pbi = Marshal.PtrToStructure<PROCESS_BASIC_INFORMATION>(pPbi);
                Console.WriteLine($"peb: {pbi.PebBaseAddress}");

                IntPtr pPeb = Marshal.AllocHGlobal(PEB.Size);
                ReadProcessMemory(handle, pbi.PebBaseAddress, pPeb, PEB.Size, outLong);
                PEB peb = Marshal.PtrToStructure<PEB>(pPeb);

                IntPtr pUpp = Marshal.AllocHGlobal(RTL_USER_PROCESS_PARAMETERS.Size);
                ReadProcessMemory(handle, peb.ProcessParameters, pUpp, RTL_USER_PROCESS_PARAMETERS.Size, outLong);
                RTL_USER_PROCESS_PARAMETERS upp = Marshal.PtrToStructure<RTL_USER_PROCESS_PARAMETERS>(pUpp);

                IntPtr pStr = Marshal.AllocHGlobal(upp.CurrentDirectoryPath.Length + 2);
                RtlZeroMemory(pStr, upp.CurrentDirectoryPath.Length + 2);
                ReadProcessMemory(handle, upp.CurrentDirectoryPath.Buffer, pStr, upp.CurrentDirectoryPath.Length, outLong);
                string cwd = Marshal.PtrToStringUni(pStr);
                Console.WriteLine($"cwd: {cwd}");

                CloseHandle(handle);
                Console.WriteLine();
            }
        }
    }
}

Reference

© 2019 hyrious