/* * Process Hacker - * process handle * * Copyright (C) 2008-2009 wj32 * * This file is part of Process Hacker. * * Process Hacker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Process Hacker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Process Hacker. If not, see . */ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; namespace ProcessHacker { public partial class Win32 { /// /// Represents a handle to a Windows process. /// /// The idea of a ProcessHandle class is /// different to the class; /// instead of opening the process with the right permissions every /// time a query or set function is called, this lets the users control /// when they want to open handles with certain permissions. This /// means that handles can be cached (by the users). public class ProcessHandle : Win32Handle, IWithToken { /// /// Specifies an offset in a process' process environment block (PEB). /// public enum PebOffset { /// /// The current directory of the process. This may, as the name /// implies, change very often. /// CurrentDirectoryPath = 0x24, /// /// A copy of the PATH environment variable for the process. /// DllPath = 0x30, /// /// The image file name, in kernel format (e.g. \\?\C:\..., /// \SystemRoot\..., \Device\Harddisk1\...). /// ImagePathName = 0x38, /// /// The command used to start the program, including arguments. /// CommandLine = 0x40, /// /// Usually blank. /// WindowTitle = 0x70, /// /// For interactive programs, contains the window station and /// desktop name of the first thread that was started, e.g. /// WinSta0\Default. /// DesktopName = 0x78, /// /// Usually blank. /// ShellInfo = 0x80, /// /// Usually blank. /// RuntimeData = 0x88 } /// /// Specifies the DEP status of a process. /// [Flags] public enum DepStatus { /// /// DEP is enabled. /// Enabled = 0x1, /// /// DEP is permanently enabled or disabled and cannot /// be enabled or disabled. /// Permanent = 0x2, /// /// DEP is enabled with DEP-ATL thunk emulation disabled. /// AtlThunkEmulationDisabled = 0x4 } /// /// Creates a process handle using an existing handle. /// The handle will not be closed automatically. /// /// The handle value. /// The process handle. public static ProcessHandle FromHandle(int Handle) { return new ProcessHandle(Handle, false); } internal ProcessHandle(int Handle, bool Owned) : base(Handle, Owned) { } /// /// Creates a new process handle. /// /// The ID of the process to open. public ProcessHandle(int pid) : this(pid, PROCESS_RIGHTS.PROCESS_ALL_ACCESS) { } /// /// Creates a new process handle. /// /// The ID of the process to open. /// The desired access to the process. public ProcessHandle(int pid, PROCESS_RIGHTS access) { if (Program.KPH != null) this.Handle = Program.KPH.KphOpenProcess(pid, access); else this.Handle = OpenProcess(access, 0, pid); if (this.Handle == 0) ThrowLastWin32Error(); } /// /// Allocates a memory region in the process' virtual memory. /// /// The base address of the region. /// The size of the region. /// The protection of the region. /// The base address of the allocated pages. public int AllocMemory(int address, int size, MEMORY_PROTECTION protection) { int newAddress; if ((newAddress = VirtualAllocEx(this, address, size, MEMORY_STATE.MEM_COMMIT, protection)) == 0) ThrowLastWin32Error(); return newAddress; } /// /// Allocates a memory region in the process' virtual memory. The function decides where /// to allocate the memory. /// /// The size of the region. /// The protection of the region. /// The base address of the allocated pages. public int AllocMemory(int size, MEMORY_PROTECTION protection) { return this.AllocMemory(0, size, protection); } /// /// Creates a remote thread in the process. /// /// The address at which to begin execution (e.g. a function). The /// function must be accessible from the remote process; that is, it must be in its /// virtual address space, either copied using AllocMemory or loaded as module using /// LoadLibrary. /// /// The parameter to pass to the function. /// The ID of the new thread. public int CreateThread(int startAddress, int parameter) { int threadId; if (!CreateRemoteThread(this, 0, 0, startAddress, parameter, 0, out threadId)) ThrowLastWin32Error(); return threadId; } /// /// Creates a remote thread in the process, returning a handle to the new thread. /// /// The address at which to begin execution (e.g. a function). The /// function must be accessible from the remote process; that is, it must be in its /// virtual address space, either copied using AllocMemory or loaded as module using /// LoadLibrary. /// /// The parameter to pass to the function. /// The desired access to the new thread. /// A handle to the new thread. public ThreadHandle CreateThread(int startAddress, int parameter, PROCESS_RIGHTS access) { return new ThreadHandle(this.CreateThread(startAddress, parameter)); } /// /// Frees a memory region in the process' virtual memory. /// /// The address of the region to free. /// The size to free. /// Specifies whether or not to only /// reserve the memory instead of freeing it. public void FreeMemory(int address, int size, bool reserveOnly) { // size needs to be 0 if we're freeing if (!reserveOnly) size = 0; if (!VirtualFreeEx(this, address, size, reserveOnly ? MEMORY_STATE.MEM_DECOMMIT : MEMORY_STATE.MEM_RELEASE)) ThrowLastWin32Error(); } /// /// Gets the process' basic information through the undocumented Native API function /// ZwQueryInformationProcess. This function requires the PROCESS_QUERY_LIMITED_INFORMATION /// permission. /// /// A PROCESS_BASIC_INFORMATION structure. public PROCESS_BASIC_INFORMATION GetBasicInformation() { PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION(); int retLen; if (ZwQueryInformationProcess(this, PROCESS_INFORMATION_CLASS.ProcessBasicInformation, ref pbi, Marshal.SizeOf(pbi), out retLen) != 0) ThrowLastWin32Error(); return pbi; } /// /// Gets the command line used to start the process. This requires /// the PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ permissions. /// /// A string. public string GetCommandLine() { return this.GetPebString(PebOffset.CommandLine); } /// /// Gets the process' DEP policy. /// /// A DEPStatus enum. /// This function does not work on /// Windows XP SP2 or lower, since they do not /// have the GetProcessDEPPolicy function. It is possible /// to use ZwQueryInformationProcess with ProcessExecuteFlags, /// but it doesn't seem to work. public DepStatus GetDepStatus() { DEPFLAGS flags; int perm; if (!GetProcessDEPPolicy(this, out flags, out perm)) ThrowLastWin32Error(); return ((flags & DEPFLAGS.PROCESS_DEP_ENABLE) != 0 ? DepStatus.Enabled : 0) | ((flags & DEPFLAGS.PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) != 0 ? (DepStatus.Enabled | DepStatus.AtlThunkEmulationDisabled) : 0) | ((perm != 0) ? DepStatus.Permanent : 0); } /// /// Gets the process' exit code. /// /// A number. public int GetExitCode() { int exitCode; if (!GetExitCodeProcess(this, out exitCode)) ThrowLastWin32Error(); return exitCode; } /// /// Gets a GUI handle count. /// /// If true, returns the number of USER handles. Otherwise, returns /// the number of GDI handles. /// A handle count. public int GetGuiResources(bool userObjects) { return Win32.GetGuiResources(this, userObjects); } /// /// Gets the file name of the process' image. This requires the /// PROCESS_QUERY_LIMITED_INFORMATION permission. /// /// A file name, in DOS (normal) format. public string GetImageFileName() { System.Text.StringBuilder sb = new System.Text.StringBuilder(1024); int len = 1024; if (!QueryFullProcessImageName(this, false, sb, ref len)) ThrowLastWin32Error(); return Misc.GetRealPath(sb.ToString(0, len)); } /// /// Gets the modules loaded by the process. This requires the /// PROCESS_QUERY_INFORMATION and PROCESS_VM_READ permissions. /// /// public ProcessModule[] GetModules() { IntPtr[] moduleHandles; int requiredSize; EnumProcessModules(this, null, 0, out requiredSize); moduleHandles = new IntPtr[requiredSize / 4]; if (!EnumProcessModules(this, moduleHandles, requiredSize, out requiredSize)) ThrowLastWin32Error(); ProcessModule[] moduleList = new ProcessModule[moduleHandles.Length]; for (int i = 0; i < moduleHandles.Length; i++) { MODULEINFO moduleInfo = new MODULEINFO(); StringBuilder baseName = new StringBuilder(0x400); StringBuilder fileName = new StringBuilder(0x400); if (!GetModuleInformation(this, moduleHandles[i], ref moduleInfo, Marshal.SizeOf(moduleInfo))) ThrowLastWin32Error(); if (GetModuleBaseName(this, moduleHandles[i], baseName, baseName.Capacity * 2) == 0) ThrowLastWin32Error(); if (GetModuleFileNameEx(this, moduleHandles[i], fileName, fileName.Capacity * 2) == 0) ThrowLastWin32Error(); moduleList[i] = new ProcessModule( moduleInfo.BaseOfDll, moduleInfo.SizeOfImage, moduleInfo.EntryPoint, baseName.ToString(), Misc.GetRealPath(fileName.ToString()) ); } return moduleList; } /// /// Gets the file name of the process' image, in device name format. This /// requires the PROCESS_QUERY_LIMITED_INFORMATION permission. /// /// A file name, in device/native format. public string GetNativeImageFileName() { int retLen; ZwQueryInformationProcess(this, PROCESS_INFORMATION_CLASS.ProcessImageFileName, IntPtr.Zero, 0, out retLen); using (MemoryAlloc data = new MemoryAlloc(retLen)) { if (ZwQueryInformationProcess(this, PROCESS_INFORMATION_CLASS.ProcessImageFileName, data, retLen, out retLen) != 0) ThrowLastWin32Error(); UNICODE_STRING str = data.ReadStruct(); return ReadUnicodeString(str); } } /// /// Gets the process' parent's process ID. This requires /// the PROCESS_QUERY_LIMITED_INFORMATION permission. /// /// The process ID. public int GetParentPID() { return this.GetBasicInformation().InheritedFromUniqueProcessId; } /// /// Reads a UNICODE_STRING from the process' process environment block. /// /// The offset to the UNICODE_STRING structure. /// A string. public string GetPebString(PebOffset offset) { int pebBaseAddress = 0x7ffd7000; // get the real PEB address of the process if we can. try { pebBaseAddress = this.GetBasicInformation().PebBaseAddress; } catch { } /* read address of parameter information block * * PEB * off field * +00 BOOLEAN InheritedAddressSpace; * +01 BOOLEAN ReadImageFileExecOptions; * +02 BOOLEAN BeingDebugged; * +03 BOOLEAN Spare; * +04 HANDLE Mutant; * +08 PVOID ImageBaseAddress; * +0c PVOID LoaderData; * +10 PRTL_USER_PROCESS_PARAMETERS ProcessParameters; */ int paramInfoAddrI = Misc.BytesToInt(this.ReadMemory(pebBaseAddress + 0x10, 4), Misc.Endianness.Little); // Read length (in bytes) of string. The offset of the UNICODE_STRING structure is // specified in the enum. // // UNICODE_STRING // off field // +00 USHORT Length; // +02 USHORT MaximumLength; // +04 PWSTR Buffer; ushort strLength = Misc.BytesToUShort( this.ReadMemory(paramInfoAddrI + (int)offset, 2), Misc.Endianness.Little); byte[] stringData = new byte[strLength]; // read address of string int strAddr = Misc.BytesToInt( this.ReadMemory(paramInfoAddrI + (int)offset + 0x4, 4), Misc.Endianness.Little); // read string and decode it return System.Text.UnicodeEncoding.Unicode.GetString( this.ReadMemory(strAddr, strLength)).TrimEnd('\0'); } /// /// Gets whether the process is currently being debugged. This requires /// the PROCESS_QUERY_INFORMATION permission. /// /// A boolean value. public bool IsBeingDebugged() { bool debugged; if (!Win32.CheckRemoteDebuggerPresent(this, out debugged)) ThrowLastWin32Error(); return debugged; } /// /// Determines whether the process is running in a job. /// /// A boolean. /// According to this function, almost every single /// process is in a job! This function does not tell us /// the name of the job though. public bool IsInJob() { bool result; if (!IsProcessInJob(this, 0, out result)) ThrowLastWin32Error(); return result; } /// /// Reads data from the process' virtual memory. /// /// The offset at which to begin reading. /// The length, in bytes, to read. /// An array of bytes public byte[] ReadMemory(int offset, int length) { byte[] buf = new byte[length]; int readLen; if (!ReadProcessMemory(this, offset, buf, length, out readLen)) ThrowLastWin32Error(); return buf; } /// /// Resumes the process. This requires the PROCESS_SUSPEND_RESUME permission. /// public void Resume() { //if (Program.KPH != null) //{ // Program.KPH.KphResumeProcess(this); //} //else //{ if (ZwResumeProcess(this) != 0) ThrowLastWin32Error(); //} } /// /// Suspends the process. This requires the PROCESS_SUSPEND_RESUME permission. /// public void Suspend() { //if (Program.KPH != null) //{ // Program.KPH.KphSuspendProcess(this); //} //else //{ if (ZwSuspendProcess(this) != 0) ThrowLastWin32Error(); //} } /// /// Terminates the process. This requires the PROCESS_TERMINATE permission. /// public void Terminate() { this.Terminate(0); } /// /// Terminates the process, specifying the exit code. This requires the /// PROCESS_TERMINATE permission. /// /// The exit code. public void Terminate(int ExitCode) { if (Program.KPH != null) { Program.KPH.KphTerminateProcess(this, ExitCode); } else { if (!TerminateProcess(this, ExitCode)) ThrowLastWin32Error(); } } /// /// Waits for the process to terminate. /// /// The timeout of the wait. /// Either WAIT_OBJECT_0, WAIT_TIMEOUT or WAIT_FAILED. public int Wait(int Timeout) { return WaitForSingleObject(this, Timeout); } /// /// Writes data to the process' virtual memory. /// /// The offset at which to begin writing. /// The data to write. /// The length, in bytes, that was written. public int WriteMemory(int offset, byte[] data) { int writLen; if (!WriteProcessMemory(this, offset, data, data.Length, out writLen)) ThrowLastWin32Error(); return writLen; } /// /// Opens and returns a handle to the process' token. This requires the /// PROCESS_QUERY_LIMITED_INFORMATION permission. /// /// A handle to the process' token. public TokenHandle GetToken() { return GetToken(TOKEN_RIGHTS.TOKEN_ALL_ACCESS); } /// /// Opens and returns a handle to the process' token. This requires the /// PROCESS_QUERY_LIMITED_INFORMATION permission. /// /// The desired access to the token. /// A handle to the process' token. public TokenHandle GetToken(TOKEN_RIGHTS access) { return new TokenHandle(this, access); } } public class ProcessModule { internal ProcessModule(IntPtr baseAddress, int size, IntPtr entryPoint, string baseName, string fileName) { this.BaseAddress = baseAddress; this.Size = size; this.EntryPoint = entryPoint; this.BaseName = baseName; this.FileName = fileName; } public IntPtr BaseAddress { get; private set; } public int Size { get; private set; } public IntPtr EntryPoint { get; private set; } public string BaseName { get; private set; } public string FileName { get; private set; } } } }