ProcessHelper.cs
|
using System;
using System.Runtime.InteropServices; using System.Text; public class TTProcessHelper { [StructLayout(LayoutKind.Sequential)] private struct PROCESS_BASIC_INFORMATION { public IntPtr Reserved1; public IntPtr PebBaseAddress; public IntPtr Reserved2_0; public IntPtr Reserved2_1; public IntPtr UniqueProcessId; public IntPtr Reserved3; } [DllImport("ntdll.dll")] private static extern int NtQueryInformationProcess( IntPtr hProcess, int pic, ref PROCESS_BASIC_INFORMATION pbi, int cb, out int pSize); [DllImport("kernel32.dll")] private static extern bool ReadProcessMemory( IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); [DllImport("kernel32.dll")] private static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); [DllImport("kernel32.dll")] private static extern bool CloseHandle(IntPtr hObject); [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); public const int SW_HIDE = 0; public const int SW_SHOW = 5; public const int SW_MINIMIZE = 6; public const int SW_RESTORE = 9; private const int PROCESS_QUERY_INFORMATION = 0x0400; private const int PROCESS_VM_READ = 0x0010; /// <summary> /// Reads the current working directory of a process by inspecting its PEB. /// Works for same-user processes without elevation. /// Returns null on failure. /// </summary> public static string GetWorkingDirectory(int pid) { IntPtr hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (hProcess == IntPtr.Zero) return null; try { var pbi = new PROCESS_BASIC_INFORMATION(); int size; if (NtQueryInformationProcess(hProcess, 0, ref pbi, Marshal.SizeOf(pbi), out size) != 0) return null; bool is64 = IntPtr.Size == 8; // Read ProcessParameters pointer from PEB // x64: offset 0x20, x86: offset 0x10 int paramOffset = is64 ? 0x20 : 0x10; byte[] ptrBuf = new byte[IntPtr.Size]; int bytesRead; if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress + paramOffset, ptrBuf, ptrBuf.Length, out bytesRead)) return null; IntPtr processParameters = is64 ? (IntPtr)BitConverter.ToInt64(ptrBuf, 0) : (IntPtr)BitConverter.ToInt32(ptrBuf, 0); // CurrentDirectory.DosPath is a UNICODE_STRING // x64: offset 0x38, x86: offset 0x24 in RTL_USER_PROCESS_PARAMETERS int cwdOffset = is64 ? 0x38 : 0x24; // Read UNICODE_STRING structure (Length: ushort, MaxLength: ushort, padding, Buffer: IntPtr) int usSize = is64 ? 16 : 8; // UNICODE_STRING size with alignment byte[] usBuf = new byte[usSize]; if (!ReadProcessMemory(hProcess, processParameters + cwdOffset, usBuf, usBuf.Length, out bytesRead)) return null; ushort len = BitConverter.ToUInt16(usBuf, 0); if (len == 0 || len > 2048) return null; IntPtr strPtr = is64 ? (IntPtr)BitConverter.ToInt64(usBuf, 8) : (IntPtr)BitConverter.ToInt32(usBuf, 4); byte[] strBuf = new byte[len]; if (!ReadProcessMemory(hProcess, strPtr, strBuf, len, out bytesRead)) return null; string result = Encoding.Unicode.GetString(strBuf, 0, bytesRead); // Remove trailing backslash (except for root like C:\) if (result.Length > 3 && result.EndsWith("\\")) result = result.Substring(0, result.Length - 1); return result; } catch { return null; } finally { CloseHandle(hProcess); } } } |