/* * 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.Collections.ObjectModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; using ProcessHacker.Native.Api; using ProcessHacker.Native.Security; namespace ProcessHacker.Native.Objects { /// /// 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 sealed class ProcessHandle : NativeHandle, IWithToken { /// /// The callback for enumerating process memory regions. /// /// The basic information for the memory region. /// Return true to continue enumerating; return false to stop. public delegate bool EnumMemoryDelegate(MemoryBasicInformation info); /// /// The callback for enumerating process modules. /// /// The module information. /// Return true to continue enumerating; return false to stop. public delegate bool EnumModulesDelegate(ProcessModule module); private static readonly ProcessHandle _current = new ProcessHandle(new IntPtr(-1), false); /// /// Gets a handle to the current process. /// public static ProcessHandle Current { get { return _current; } } /// /// Creates a process and an initial thread. /// /// The desired access to the new process. /// The path to an executable image file. /// Specify true to inherit handles, otherwise false. /// A handle to the new thread. /// A handle to the new process. public static ProcessHandle Create(ProcessAccess access, string fileName, bool inheritHandles, out ThreadHandle threadHandle) { using (var fhandle = new FileHandle( fileName, (FileAccess)StandardRights.Synchronize | FileAccess.Execute | FileAccess.ReadData, FileShareMode.Delete | FileShareMode.Read, FileCreationDisposition.OpenAlways )) { using (var shandle = SectionHandle.Create( SectionAccess.All, SectionAttributes.Image, MemoryProtection.Execute, fhandle )) { ProcessHandle phandle = Create(access, ProcessHandle.Current, inheritHandles, shandle); threadHandle = ThreadHandle.CreateUserThread( phandle, false, shandle.GetImageInformation().TransferAddress, IntPtr.Zero ); return phandle; } } } /// /// Creates a process. /// /// The desired access to the new process. /// The process to inherit the address space and handles from. /// Specify true to inherit handles, otherwise false. /// A section of an executable image. /// A handle to the new process. public static ProcessHandle Create( ProcessAccess access, ProcessHandle parentProcess, bool inheritHandles, SectionHandle sectionHandle) { return Create(access, null, 0, null, parentProcess, inheritHandles, sectionHandle, null); } /// /// Creates a process. /// /// The desired access to the new process. /// The name of the process. /// The flags to use when creating the object. /// A handle to the directory in which to place the object. /// The process to inherit the address space and handles from. /// Specify true to inherit handles, otherwise false. /// A section of an executable image. /// A debug object to attach the process to. /// A handle to the new process. public static ProcessHandle Create( ProcessAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, ProcessHandle parentProcess, bool inheritHandles, SectionHandle sectionHandle, DebugObjectHandle debugPort ) { NtStatus status; ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); IntPtr handle; try { if ((status = Win32.NtCreateProcess( out handle, access, ref oa, parentProcess ?? IntPtr.Zero, inheritHandles, sectionHandle ?? IntPtr.Zero, debugPort ?? IntPtr.Zero, IntPtr.Zero )) >= NtStatus.Error) Win32.ThrowLastError(status); } finally { oa.Dispose(); } return new ProcessHandle(handle, true); } public static ProcessHandle CreateUserProcess(string fileName, out ClientId clientId, out ThreadHandle threadHandle) { NtStatus status; UnicodeString fileNameStr = new UnicodeString(fileName); RtlUserProcessParameters processParams = new RtlUserProcessParameters(); RtlUserProcessInformation processInfo; processParams.Length = Marshal.SizeOf(processParams); processParams.MaximumLength = processParams.Length; processParams.ImagePathName = new UnicodeString(fileName); processParams.CommandLine = new UnicodeString(fileName); Win32.RtlCreateEnvironment(true, out processParams.Environment); try { if ((status = Win32.RtlCreateUserProcess( ref fileNameStr, 0, ref processParams, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, false, IntPtr.Zero, IntPtr.Zero, out processInfo )) >= NtStatus.Error) Win32.ThrowLastError(status); clientId = processInfo.ClientId; threadHandle = new ThreadHandle(processInfo.Thread, true); return new ProcessHandle(processInfo.Process, true); } finally { fileNameStr.Dispose(); processParams.ImagePathName.Dispose(); processParams.CommandLine.Dispose(); Win32.RtlDestroyEnvironment(processParams.Environment); } } /// /// 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(IntPtr handle) { return new ProcessHandle(handle, false); } /// /// Gets a handle to the current process. /// /// A process handle. public static ProcessHandle GetCurrent() { return Current; } /// /// Gets the ID of the current process. /// /// The ID of the current process. public static int GetCurrentId() { return Win32.GetCurrentProcessId(); } private static int GetPebOffset(PebOffset offset) { switch (offset) { case PebOffset.CommandLine: return RtlUserProcessParameters.CommandLineOffset; case PebOffset.CurrentDirectoryPath: return RtlUserProcessParameters.CurrentDirectoryOffset; case PebOffset.DesktopName: return RtlUserProcessParameters.DesktopInfoOffset; case PebOffset.DllPath: return RtlUserProcessParameters.DllPathOffset; case PebOffset.ImagePathName: return RtlUserProcessParameters.ImagePathNameOffset; case PebOffset.RuntimeData: return RtlUserProcessParameters.RuntimeDataOffset; case PebOffset.ShellInfo: return RtlUserProcessParameters.ShellInfoOffset; case PebOffset.WindowTitle: return RtlUserProcessParameters.WindowTitleOffset; default: throw new ArgumentException("offset"); } } /// /// Opens processes with the specified name. /// /// The names of the processes to open. /// The desired access to the processes. /// An array of process handles. public static ProcessHandle[] OpenByName(string processName, ProcessAccess access) { var processes = Windows.GetProcesses(); List processHandles = new List(); foreach (var process in processes.Values) { if (string.Equals(process.Name, processName, StringComparison.InvariantCultureIgnoreCase)) { try { processHandles.Add(new ProcessHandle(process.Process.ProcessId, access)); } catch { } } } return processHandles.ToArray(); } /// /// Opens a handle to the current process. /// /// The desired access to the current process. /// A handle. public static ProcessHandle OpenCurrent(ProcessAccess access) { return new ProcessHandle(GetCurrentId(), access); } private ProcessHandle(IntPtr handle, bool owned) : base(handle, owned) { } /// /// Opens a process. /// /// The ID of the process to open. public ProcessHandle(int pid) : this(pid, ProcessAccess.All) { } /// /// Opens a process. /// /// The ID of the process to open. /// The desired access to the process. public ProcessHandle(int pid, ProcessAccess access) { // If we have KPH, use it. if (KProcessHacker.Instance != null) { try { this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenProcess(pid, access)); } catch (WindowsException) { // This would only happen if the process is DRM-protected or if // some part of ObReferenceObjectByHandle is hooked. We can // open the process with SYNCHRONIZE access and set the granted access // using KPH. this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenProcess(pid, (ProcessAccess)StandardRights.Synchronize)); KProcessHacker.Instance.KphSetHandleGrantedAccess(this.Handle, (int)access); } } else { this.Handle = Win32.OpenProcess(access, false, pid); } if (this.Handle == IntPtr.Zero) { this.MarkAsInvalid(); Win32.ThrowLastError(); } } /// /// Opens a thread's process. /// /// A handle to a thread. /// The desired access to the process. public ProcessHandle(ThreadHandle threadHandle, ProcessAccess access) { if (KProcessHacker.Instance == null) throw new NotSupportedException(); this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenThreadProcess(threadHandle, access)); } /// /// Opens a process. /// /// The name of the process. /// The flags to use when opening the object. /// /// A handle to the directory in which the object is located. /// /// A Client ID structure describing the process. /// The desired access to the process. public ProcessHandle( string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, ClientId clientId, ProcessAccess access ) { NtStatus status; ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); IntPtr handle; try { // NtOpenProcess fails when both a client ID and a name is specified. if (name != null) { // Name specified, don't specify a CID. if ((status = Win32.NtOpenProcess( out handle, access, ref oa, IntPtr.Zero )) >= NtStatus.Error) Win32.ThrowLastError(status); } else { // No name, specify a CID. if ((status = Win32.NtOpenProcess( out handle, access, ref oa, ref clientId )) >= NtStatus.Error) Win32.ThrowLastError(status); } } finally { oa.Dispose(); } this.Handle = handle; } /// /// Opens a process. /// /// The name of the process. /// The desired access to the process. public ProcessHandle(string name, ProcessAccess access) : this(name, 0, null, new ClientId(), access) { } /// /// Opens a process. /// /// A Client ID structure describing the process. /// The desired access to the process. public ProcessHandle(ClientId clientId, ProcessAccess access) : this(null, 0, null, clientId, access) { } /// /// 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 IntPtr AllocateMemory(int size, MemoryProtection protection) { return this.AllocateMemory(size, MemoryFlags.Commit, protection); } /// /// Allocates a memory region in the process' virtual memory. The function decides where /// to allocate the memory. /// /// The size of the region. /// The type of allocation. /// The protection of the region. /// The base address of the allocated pages. public IntPtr AllocateMemory(int size, MemoryFlags type, MemoryProtection protection) { return this.AllocateMemory(IntPtr.Zero, size, type, protection); } /// /// Allocates a memory region in the process' virtual memory. /// /// The base address of the region. /// The size of the region. /// The type of allocation. /// The protection of the region. /// The base address of the allocated pages. public IntPtr AllocateMemory(IntPtr baseAddress, int size, MemoryFlags type, MemoryProtection protection) { NtStatus status; IntPtr sizeIntPtr = size.ToIntPtr(); if ((status = Win32.NtAllocateVirtualMemory( this, ref baseAddress, IntPtr.Zero, ref sizeIntPtr, type, protection )) >= NtStatus.Error) Win32.ThrowLastError(status); return baseAddress; } /// /// Assigns the process to a job object. The job handle must have the /// JOB_OBJECT_ASSIGN_PROCESS permission and the process handle must have /// the PROCESS_SET_QUOTA and PROCESS_TERMINATE permissions. /// /// The job object to assign the process to. public void AssignToJobObject(JobObjectHandle job) { if (!Win32.AssignProcessToJobObject(job, this)) Win32.ThrowLastError(); } /// /// Creates a thread in the process but does not notify the Win32 subsystem. /// /// The address at which to begin execution. /// The parameter to pass to the function. /// A handle to the new thread. /// This function will work across sessions, unlike CreateThread. public ThreadHandle CreateNativeThread(IntPtr startAddress, IntPtr parameter) { return this.CreateNativeThread(startAddress, parameter, false); } /// /// Creates a thread in the process but does not notify the Win32 subsystem. /// /// The address at which to begin execution. /// The parameter to pass to the function. /// Whether to create the thread suspended. /// A handle to the new thread. /// This function will work across sessions, unlike CreateThread. public ThreadHandle CreateNativeThread(IntPtr startAddress, IntPtr parameter, bool createSuspended) { int threadId; return this.CreateNativeThread(startAddress, parameter, createSuspended, out threadId); } /// /// Creates a thread in the process but does not notify the Win32 subsystem. /// /// The address at which to begin execution. /// The parameter to pass to the function. /// Whether to create the thread suspended. /// The ID of the new thread. /// A handle to the new thread. /// This function will work across sessions, unlike CreateThread. public ThreadHandle CreateNativeThread(IntPtr startAddress, IntPtr parameter, bool createSuspended, out int threadId) { ClientId cid; ThreadHandle thandle = ThreadHandle.CreateUserThread( this, createSuspended, 0, 0, startAddress, parameter, out cid ); threadId = cid.ThreadId; return thandle; } /// /// Creates a thread in the process. /// /// The address at which to begin execution. /// The parameter to pass to the function. /// A handle to the new thread. public ThreadHandle CreateThread(IntPtr startAddress, IntPtr parameter) { return this.CreateThread(startAddress, parameter, false); } /// /// Creates a thread in the process. /// /// The address at which to begin execution. /// The parameter to pass to the function. /// Whether to create the thread suspended. /// A handle to the new thread. public ThreadHandle CreateThread(IntPtr startAddress, IntPtr parameter, bool createSuspended) { int threadId; return this.CreateThread(startAddress, parameter, createSuspended, out threadId); } /// /// Creates a thread in the process. /// /// The address at which to begin execution. /// The parameter to pass to the function. /// Whether to create the thread suspended. /// The ID of the new thread. /// A handle to the new thread. public ThreadHandle CreateThread(IntPtr startAddress, IntPtr parameter, bool createSuspended, out int threadId) { IntPtr threadHandle; if ((threadHandle = Win32.CreateRemoteThread( this, IntPtr.Zero, IntPtr.Zero, startAddress, parameter, createSuspended ? CreationFlags.CreateSuspended : 0, out threadId)) == IntPtr.Zero) Win32.ThrowLastError(); return new ThreadHandle(threadHandle, true); } /// /// Debugs the process with the specified debug object. This requires /// PROCESS_SUSPEND_RESUME access. /// /// A handle to a debug object. public void Debug(DebugObjectHandle debugObjectHandle) { NtStatus status; if ((status = Win32.NtDebugActiveProcess(this, debugObjectHandle)) >= NtStatus.Error) Win32.ThrowLastError(status); } /// /// Disables the collection of handle stack traces. This requires /// PROCESS_SET_INFORMATION access. Note that this function is only /// available on Windows Vista and above. /// public void DisableHandleTracing() { NtStatus status; // Length 0 and NULL disables handle tracing. if ((status = Win32.NtSetInformationProcess( this, ProcessInformationClass.ProcessHandleTracing, IntPtr.Zero, 0 )) >= NtStatus.Error) Win32.ThrowLastError(status); } /// /// Removes as many pages as possible from the process' working set. This requires the /// PROCESS_QUERY_INFORMATION and PROCESS_SET_INFORMATION permissions. /// public void EmptyWorkingSet() { if (!Win32.EmptyWorkingSet(this)) Win32.ThrowLastError(); } /// /// Enables the collection of handle stack traces. This requires /// PROCESS_SET_INFORMATION access. /// public void EnableHandleTracing() { NtStatus status; ProcessHandleTracingEnable phte = new ProcessHandleTracingEnable(); if ((status = Win32.NtSetInformationProcess( this, ProcessInformationClass.ProcessHandleTracing, ref phte, Marshal.SizeOf(phte) )) >= NtStatus.Error) Win32.ThrowLastError(status); } /// /// Enumerates the memory regions of the process. /// /// The callback for the enumeration. public void EnumMemory(EnumMemoryDelegate enumMemoryCallback) { IntPtr address = IntPtr.Zero; MemoryBasicInformation mbi = new MemoryBasicInformation(); int mbiSize = Marshal.SizeOf(mbi); while (Win32.VirtualQueryEx(this, address, out mbi, mbiSize) != 0) { if (!enumMemoryCallback(mbi)) break; address = address.Increment(mbi.RegionSize); } } /// /// Enumerates the modules loaded by the process. /// /// The callback for the enumeration. public void EnumModules(EnumModulesDelegate enumModulesCallback) { this.EnumModulesNative(enumModulesCallback); } /// /// Enumerates the modules loaded by the process using PSAPI. /// /// The callback for the enumeration. private void EnumModulesApi(EnumModulesDelegate enumModulesCallback) { IntPtr[] moduleHandles; int requiredSize; Win32.EnumProcessModules(this, null, 0, out requiredSize); moduleHandles = new IntPtr[requiredSize / 4]; if (!Win32.EnumProcessModules(this, moduleHandles, requiredSize, out requiredSize)) Win32.ThrowLastError(); for (int i = 0; i < moduleHandles.Length; i++) { ModuleInfo moduleInfo = new ModuleInfo(); StringBuilder baseName = new StringBuilder(0x400); StringBuilder fileName = new StringBuilder(0x400); if (!Win32.GetModuleInformation(this, moduleHandles[i], moduleInfo, Marshal.SizeOf(moduleInfo))) Win32.ThrowLastError(); if (Win32.GetModuleBaseName(this, moduleHandles[i], baseName, baseName.Capacity * 2) == 0) Win32.ThrowLastError(); if (Win32.GetModuleFileNameEx(this, moduleHandles[i], fileName, fileName.Capacity * 2) == 0) Win32.ThrowLastError(); if (!enumModulesCallback(new ProcessModule( moduleInfo.BaseOfDll, moduleInfo.SizeOfImage, moduleInfo.EntryPoint, 0, baseName.ToString(), FileUtils.FixPath(fileName.ToString()) ))) break; } } /// /// Enumerates the modules loaded by the process by reading the NT loader data. /// /// The callback for the enumeration. private unsafe void EnumModulesNative(EnumModulesDelegate enumModulesCallback) { byte* buffer = stackalloc byte[IntPtr.Size]; // Get the loader data table address. this.ReadMemory(this.GetBasicInformation().PebBaseAddress.Increment(Win32.PebLdrOffset), buffer, IntPtr.Size); IntPtr loaderData = *(IntPtr*)buffer; PebLdrData* data = stackalloc PebLdrData[1]; // Read the loader data table structure. this.ReadMemory(loaderData, data, Marshal.SizeOf(typeof(PebLdrData))); if (!data->Initialized) throw new Exception("Loader data is not initialized."); IntPtr currentLink = data->InLoadOrderModuleList.Flink; IntPtr startLink = currentLink; LdrDataTableEntry* currentEntry = stackalloc LdrDataTableEntry[1]; int i = 0; while (currentLink != IntPtr.Zero) { // Stop when we have reached the beginning of the linked list. if (i > 0 && currentLink == startLink) break; // Safety guard. if (i > 0x800) break; // Read the loader data table entry. this.ReadMemory(currentLink, currentEntry, Marshal.SizeOf(typeof(LdrDataTableEntry))); // Check if the entry is valid. if (currentEntry->DllBase != IntPtr.Zero) { string baseDllName = null; string fullDllName = null; // Read the two strings. try { baseDllName = currentEntry->BaseDllName.Read(this).TrimEnd('\0'); } catch { } try { fullDllName = FileUtils.FixPath(currentEntry->FullDllName.Read(this).TrimEnd('\0')); } catch { } // Execute the callback. if (!enumModulesCallback(new ProcessModule( currentEntry->DllBase, currentEntry->SizeOfImage, currentEntry->EntryPoint, currentEntry->Flags, baseDllName, fullDllName ))) break; } currentLink = currentEntry->InLoadOrderLinks.Flink; i++; } } /// /// Flushes the process' virtual memory. /// /// The base address of the region to flush. /// The size of the region to flush. /// A NT status value. public NtStatus FlushMemory(IntPtr baseAddress, int size) { NtStatus status; IntPtr sizeIntPtr = size.ToIntPtr(); IoStatusBlock isb; if ((status = Win32.NtFlushVirtualMemory( this, ref baseAddress, ref sizeIntPtr, out isb )) >= NtStatus.Error) Win32.ThrowLastError(status); return isb.Status; } /// /// 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(IntPtr baseAddress, int size, bool reserveOnly) { NtStatus status; IntPtr sizeIntPtr = size.ToIntPtr(); // Size needs to be 0 if we're freeing. if (!reserveOnly) sizeIntPtr = IntPtr.Zero; if ((status = Win32.NtFreeVirtualMemory( this, ref baseAddress, ref sizeIntPtr, reserveOnly ? MemoryFlags.Decommit : MemoryFlags.Release )) >= NtStatus.Error) Win32.ThrowLastError(status); } /// /// Gets the processor affinity for the process. /// /// The processor affinity for the process. public long GetAffinityMask() { long systemMask; return this.GetAffinityMask(out systemMask); } /// /// Gets the processor affinity for the process. /// /// Receives the processor affinity mask for the system. /// The processor affinity for the process. public long GetAffinityMask(out long systemMask) { IntPtr processMaskTemp; IntPtr systemMaskTemp; if (!Win32.GetProcessAffinityMask(this, out processMaskTemp, out systemMaskTemp)) Win32.ThrowLastError(); systemMask = systemMaskTemp.ToInt64(); return processMaskTemp.ToInt64(); } /// /// Gets the base priority of the process. /// public int GetBasePriority() { return this.GetInformationInt32(ProcessInformationClass.ProcessBasePriority); } /// /// Gets the process' basic information through the undocumented Native API function /// NtQueryInformationProcess. This function requires the PROCESS_QUERY_LIMITED_INFORMATION /// permission. /// /// A PROCESS_BASIC_INFORMATION structure. public ProcessBasicInformation GetBasicInformation() { NtStatus status; ProcessBasicInformation pbi; int retLen; if ((status = Win32.NtQueryInformationProcess(this, ProcessInformationClass.ProcessBasicInformation, out pbi, Marshal.SizeOf(typeof(ProcessBasicInformation)), out retLen)) >= NtStatus.Error) Win32.ThrowLastError(status); 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() { if (!this.IsPosix()) return this.GetPebString(PebOffset.CommandLine); else return this.GetPosixCommandLine(); } /// /// Gets the process' cookie (a random value). /// public int GetCookie() { return this.GetInformationInt32(ProcessInformationClass.ProcessCookie); } /// /// Gets the creation time of the process. /// public long GetCreateTime() { return this.GetTimes()[0]; } /// /// Gets the number of processor cycles consumed by the process' threads. /// public ulong GetCycleTime() { ulong cycles; if (!Win32.QueryProcessCycleTime(this, out cycles)) Win32.ThrowLastError(); return cycles; } /// /// Opens the debug object associated with the process. /// /// A debug object handle. public DebugObjectHandle GetDebugObject() { IntPtr handle; handle = this.GetDebugObjectHandle(); // Check if we got a handle. If we didn't the process is not being debugged. if (handle == IntPtr.Zero) return null; return new DebugObjectHandle(handle, true); } internal IntPtr GetDebugObjectHandle() { return this.GetInformationIntPtr(ProcessInformationClass.ProcessDebugObjectHandle); } /// /// Gets the process' DEP policy. /// /// A DepStatus enum. public DepStatus GetDepStatus() { MemExecuteOptions options; // If we're on 64-bit and the process isn't under // WOW64, it must be under permanent DEP. if (IntPtr.Size == 8) { if (!this.IsWow64()) return DepStatus.Enabled | DepStatus.Permanent; } options = (MemExecuteOptions)this.GetInformationInt32(ProcessInformationClass.ProcessExecuteFlags); DepStatus depStatus = 0; // Check if execution of data pages is enabled. if ((options & MemExecuteOptions.ExecuteEnable) == MemExecuteOptions.ExecuteEnable) return 0; // Check if execution of data pages is disabled. if ((options & MemExecuteOptions.ExecuteDisable) == MemExecuteOptions.ExecuteDisable) depStatus = DepStatus.Enabled; // ExecuteDisable and ExecuteEnable are both disabled in OptOut mode. else if ((options & MemExecuteOptions.ExecuteDisable) == 0 && (options & MemExecuteOptions.ExecuteEnable) == 0) depStatus = DepStatus.Enabled; if ((options & MemExecuteOptions.DisableThunkEmulation) == MemExecuteOptions.DisableThunkEmulation) depStatus |= DepStatus.AtlThunkEmulationDisabled; if ((options & MemExecuteOptions.Permanent) == MemExecuteOptions.Permanent) depStatus |= DepStatus.Permanent; return depStatus; } /// /// Gets the process' environment variables. This requires the /// PROCESS_QUERY_INFORMATION and PROCESS_VM_READ permissions. /// /// A dictionary of variables. public unsafe IDictionary GetEnvironmentVariables() { IntPtr pebBaseAddress = this.GetBasicInformation().PebBaseAddress; byte* buffer = stackalloc byte[IntPtr.Size]; // Get a pointer to the process parameters block. this.ReadMemory(pebBaseAddress.Increment(Win32.PebProcessParametersOffset), buffer, IntPtr.Size); IntPtr processParameters = *(IntPtr*)buffer; // Get a pointer to the environment block. this.ReadMemory(processParameters.Increment(RtlUserProcessParameters.EnvironmentOffset), buffer, IntPtr.Size); IntPtr envBase = *(IntPtr*)buffer; int length = 0; { MemoryBasicInformation mbi = this.QueryMemory(envBase); if (mbi.Protect == MemoryProtection.NoAccess) throw new WindowsException(); length = mbi.RegionSize.Decrement(envBase.Decrement(mbi.BaseAddress)).ToInt32(); } // Now we read in the entire region of memory // And yes, some memory is wasted. byte[] memory = this.ReadMemory(envBase, length); /* The environment variables block is a series of Unicode strings separated by * two null bytes. The entire block is terminated by four null bytes. */ Dictionary vars = new Dictionary(); StringBuilder currentVariable = new StringBuilder(); int i = 0; while (true) { if (i >= memory.Length) break; char currentChar = UnicodeEncoding.Unicode.GetChars(memory, i, 2)[0]; i += 2; if (currentChar == '\0') { // Two nulls in a row, the env. block is finished. if (currentVariable.Length == 0) break; string[] s = currentVariable.ToString().Split(new char[] { '=' }, 2); if (!vars.ContainsKey(s[0]) && s.Length > 1) vars.Add(s[0], s[1]); currentVariable = new StringBuilder(); } else { currentVariable.Append(currentChar); } } return vars; } /// /// Gets the process' exit code. /// /// A number. public int GetExitCode() { int exitCode; if (!Win32.GetExitCodeProcess(this, out exitCode)) Win32.ThrowLastError(); return exitCode; } /// /// Gets the process' exit status. /// /// A NT status value. public NtStatus GetExitStatus() { return this.GetBasicInformation().ExitStatus; } /// /// Gets the exit time of the process. /// public long GetExitTime() { return this.GetTimes()[1]; } /// /// 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 ? 1 : 0); } /// /// Gets the number of handles opened by the process. /// public int GetHandleCount() { return this.GetInformationInt32(ProcessInformationClass.ProcessHandleCount); } /// /// Gets the handles owned by the process. /// /// An array of handle information structures. public ProcessHandleInformation[] GetHandles() { int returnLength = 0; int attempts = 0; using (var data = new MemoryAlloc(0x1000)) { while (true) { try { KProcessHacker.Instance.KphQueryProcessHandles(this, data, data.Size, out returnLength); } catch (WindowsException ex) { if (attempts > 3) throw ex; if ( ex.Status == NtStatus.BufferTooSmall && returnLength > data.Size ) data.Resize(returnLength); attempts++; continue; } int handleCount = data.ReadInt32(0); ProcessHandleInformation[] handles = new ProcessHandleInformation[handleCount]; for (int i = 0; i < handleCount; i++) handles[i] = data.ReadStruct(sizeof(int), i); return handles; } } } /// /// Gets a collection of handle stack traces. This requires /// PROCESS_QUERY_INFORMATION access. /// /// A collection of handle stack traces. public ProcessHandleTraceCollection GetHandleTraces() { return this.GetHandleTraces(IntPtr.Zero); } /// /// Gets a collection of handle stack traces. This requires /// PROCESS_QUERY_INFORMATION access. /// /// /// A handle to the stack trace to retrieve. If this parameter is /// zero, all stack traces will be retrieved. /// /// A collection of handle stack traces. public ProcessHandleTraceCollection GetHandleTraces(IntPtr handle) { NtStatus status = NtStatus.Success; int retLength; using (var data = new MemoryAlloc(0x10000)) { var query = new ProcessHandleTracingQuery(); // If Handle is not NULL, NtQueryInformationProcess will // get a specific stack trace. Otherwise, it will get // all of the stack traces. query.Handle = handle; data.WriteStruct(query); for (int i = 0; i < 8; i++) { status = Win32.NtQueryInformationProcess( this, ProcessInformationClass.ProcessHandleTracing, data, data.Size, out retLength ); if (status == NtStatus.InfoLengthMismatch) { data.Resize(data.Size * 4); continue; } if (status >= NtStatus.Error) Win32.ThrowLastError(status); return new ProcessHandleTraceCollection(data); } Win32.ThrowLastError(status); return null; // Silences the compiler. } } /// /// Gets the process' default heap. /// /// A pointer to a heap. public unsafe IntPtr GetHeap() { IntPtr heap; this.ReadMemory( this.GetBasicInformation().PebBaseAddress.Increment(Win32.PebProcessHeapOffset), &heap, IntPtr.Size ); return heap; } /// /// 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() { var sb = new StringBuilder(1024); int len = 1024; if (!Win32.QueryFullProcessImageName(this, false, sb, ref len)) Win32.ThrowLastError(); return FileUtils.FixPath(sb.ToString(0, len)); } /// /// Gets information about the process in an Int32. /// /// The class of information to retrieve. /// An int. private int GetInformationInt32(ProcessInformationClass infoClass) { NtStatus status; int value; int retLength; if ((status = Win32.NtQueryInformationProcess( this, infoClass, out value, sizeof(int), out retLength)) >= NtStatus.Error) Win32.ThrowLastError(status); return value; } /// /// Gets information about the process in an IntPtr. /// /// The class of information to retrieve. /// An IntPtr. private IntPtr GetInformationIntPtr(ProcessInformationClass infoClass) { NtStatus status; IntPtr value; int retLength; if ((status = Win32.NtQueryInformationProcess( this, infoClass, out value, IntPtr.Size, out retLength)) >= NtStatus.Error) Win32.ThrowLastError(status); return value; } /// /// Gets the process' I/O priority, ranging from 0-7. /// /// public int GetIoPriority() { return this.GetInformationInt32(ProcessInformationClass.ProcessIoPriority); } /// /// Gets I/O statistics for the process. /// /// A IoCounters structure. public IoCounters GetIoStatistics() { NtStatus status; IoCounters counters; int retLength; if ((status = Win32.NtQueryInformationProcess( this, ProcessInformationClass.ProcessIoCounters, out counters, Marshal.SizeOf(typeof(IoCounters)), out retLength )) >= NtStatus.Error) Win32.ThrowLastError(status); return counters; } /// /// Opens the job object associated with the process. /// /// A job object handle. public JobObjectHandle GetJobObject(JobObjectAccess access) { try { return new JobObjectHandle(this, access); } catch (WindowsException ex) { if (ex.Status == NtStatus.ProcessNotInJob) return null; else throw ex; } } /// /// Gets the type of well-known process. /// /// A known process type. public KnownProcess GetKnownProcessType() { if (this.GetBasicInformation().UniqueProcessId.Equals(4)) return KnownProcess.System; string fileName = FileUtils.DeviceFileNameToDos(this.GetNativeImageFileName()); if (fileName.ToLower().StartsWith(Environment.SystemDirectory.ToLower())) { string baseName = fileName.Remove(0, Environment.SystemDirectory.Length).TrimStart('\\').ToLower(); switch (baseName) { case "smss.exe": return KnownProcess.SessionManager; case "csrss.exe": return KnownProcess.WindowsSubsystem; case "wininit.exe": return KnownProcess.WindowsStartup; case "services.exe": return KnownProcess.ServiceControlManager; case "lsass.exe": return KnownProcess.LocalSecurityAuthority; case "lsm.exe": return KnownProcess.LocalSessionManager; default: return KnownProcess.None; } } else { return KnownProcess.None; } } /// /// Gets the main module of the process. This requires the /// PROCESS_QUERY_INFORMATION and PROCESS_VM_READ permissions. /// /// A ProcessModule. public ProcessModule GetMainModule() { ProcessModule mainModule = null; this.EnumModules((module) => { mainModule = module; return false; }); return mainModule; } /// /// Gets the name of a file which the process has mapped. /// /// The address of the mapped section. /// A filename. public string GetMappedFileName(IntPtr address) { StringBuilder sb = new StringBuilder(0x400); int length = Win32.GetMappedFileName(this, address, sb, sb.Capacity); if (length > 0) { string fileName = sb.ToString(0, length); if (fileName.StartsWith("\\")) fileName = FileUtils.DeviceFileNameToDos(fileName); System.IO.FileInfo fi = new System.IO.FileInfo(fileName); return fi.ToString(); } return null; } /// /// Gets memory statistics for the process. /// /// A VmCounters structure. public VmCounters GetMemoryStatistics() { NtStatus status; VmCounters counters; int retLength; if ((status = Win32.NtQueryInformationProcess( this, ProcessInformationClass.ProcessVmCounters, out counters, Marshal.SizeOf(typeof(VmCounters)), out retLength )) >= NtStatus.Error) Win32.ThrowLastError(status); return counters; } /// /// Gets the modules loaded by the process. This requires the /// PROCESS_QUERY_INFORMATION and PROCESS_VM_READ permissions. /// /// An array of ProcessModule objects. public ProcessModule[] GetModules() { List modules = new List(); this.EnumModules((module) => { modules.Add(module); return true; }); return modules.ToArray(); } /// /// 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() { NtStatus status; int retLen; Win32.NtQueryInformationProcess(this, ProcessInformationClass.ProcessImageFileName, IntPtr.Zero, 0, out retLen); using (MemoryAlloc data = new MemoryAlloc(retLen)) { if ((status = Win32.NtQueryInformationProcess(this, ProcessInformationClass.ProcessImageFileName, data, retLen, out retLen)) >= NtStatus.Error) Win32.ThrowLastError(status); return data.ReadStruct().Read(); } } /// /// Opens the next linked process. /// /// The desired access to the next process. /// A process handle. public ProcessHandle GetNextProcess(ProcessAccess access) { NtStatus status; IntPtr handle; if ((status = Win32.NtGetNextProcess( this, access, 0, 0, out handle )) >= NtStatus.Error) Win32.ThrowLastError(status); if (handle != IntPtr.Zero) return new ProcessHandle(handle, true); else return null; } /// /// Opens the next linked thread belonging to the process. /// /// A thread handle. You may specify null. /// The desired access to the next thread. /// A thread handle. public ThreadHandle GetNextThread(ThreadHandle threadHandle, ThreadAccess access) { NtStatus status; IntPtr handle; if ((status = Win32.NtGetNextThread( this, threadHandle != null ? threadHandle : IntPtr.Zero, access, 0, 0, out handle )) >= NtStatus.Error) Win32.ThrowLastError(status); if (handle != IntPtr.Zero) return new ThreadHandle(handle, true); else return null; } /// /// Gets the process' page priority, ranging from 0-7. /// public int GetPagePriority() { return this.GetInformationInt32(ProcessInformationClass.ProcessPagePriority); } /// /// 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.ToInt32(); } /// /// Reads a UNICODE_STRING from the process' process environment block. /// /// The offset to the UNICODE_STRING structure. /// A string. public unsafe string GetPebString(PebOffset offset) { byte* buffer = stackalloc byte[IntPtr.Size]; IntPtr pebBaseAddress = this.GetBasicInformation().PebBaseAddress; // Read the address of parameter information block. this.ReadMemory(pebBaseAddress.Increment(Win32.PebProcessParametersOffset), buffer, IntPtr.Size); IntPtr processParameters = *(IntPtr*)buffer; // The offset of the UNICODE_STRING structure is specified in the enum. int realOffset = GetPebOffset(offset); // Read the UNICODE_STRING structure. UnicodeString pebStr; this.ReadMemory(processParameters.Increment(realOffset), &pebStr, Marshal.SizeOf(typeof(UnicodeString))); // read string and decode it return UnicodeEncoding.Unicode.GetString( this.ReadMemory(pebStr.Buffer, pebStr.Length), 0, pebStr.Length); } /// /// Gets the command line used to start the process. This /// function is only valid for POSIX processes. /// /// A command line string. public unsafe string GetPosixCommandLine() { byte* buffer = stackalloc byte[IntPtr.Size]; IntPtr pebBaseAddress = this.GetBasicInformation().PebBaseAddress; this.ReadMemory(pebBaseAddress.Increment(Win32.PebProcessParametersOffset), buffer, IntPtr.Size); IntPtr processParameters = *(IntPtr*)buffer; // Read the command line UNICODE_STRING structure. UnicodeString commandLineUs; this.ReadMemory( processParameters.Increment(GetPebOffset(PebOffset.CommandLine)), &commandLineUs, Marshal.SizeOf(typeof(UnicodeString)) ); IntPtr stringAddr = commandLineUs.Buffer; /* * In the POSIX subsystem the command line is actually split up into bits, as in * argv. In the command line string we don't actually have the command line - * instead, it is filled with pointers to each command line part. For example: * CommandLine.Buffer = 0x12345678 * at 0x12345678 we have: * 0x12346000 0x12347000 0x12348000 0x00000000 0x12349000 * ^ at 0x12346000: "cat" (ASCII) * ^ at 0x12347000: "-o" (ASCII) * ^ at 0x12348000: "myfile" (ASCII) * ^ signifies that there are no more pointers * ^ pointer to environment block * - from this we can work out * how much memory to read */ // Get the list of pointers. List strPointers = new List(); bool zeroReached = false; int i = 0; while (true) { this.ReadMemory(stringAddr.Increment(i), buffer, IntPtr.Size); IntPtr value = *(IntPtr*)buffer; if (value != IntPtr.Zero) strPointers.Add(value); i += IntPtr.Size; if (zeroReached) break; else if (value == IntPtr.Zero) zeroReached = true; } // Work out the size of the command line and read the data. IntPtr lastPointer = strPointers[strPointers.Count - 1]; int partsSize = lastPointer.Decrement(strPointers[0]).ToInt32(); // FIXME: Lazy; optimize later. StringBuilder commandLine = new StringBuilder(); for (i = 0; i < strPointers.Count - 1; i++) { byte[] data = this.ReadMemory(strPointers[i], partsSize); commandLine.Append(ASCIIEncoding.ASCII.GetString(data, 0, Array.IndexOf(data, 0)) + " "); } string commandLineStr = commandLine.ToString(); if (commandLineStr.EndsWith(" ")) commandLineStr = commandLineStr.Remove(commandLineStr.Length - 1, 1); return commandLineStr; } /// /// Gets the process' priority class. /// /// A ProcessPriorityClass enum. public ProcessPriorityClass GetPriorityClass() { int priority = Win32.GetPriorityClass(this); if (priority == 0) Win32.ThrowLastError(); return (ProcessPriorityClass)priority; } /// /// Gets the process' unique identifier. /// public int GetProcessId() { return this.GetBasicInformation().UniqueProcessId.ToInt32(); } /// /// Gets the process' session ID. /// public int GetSessionId() { return this.GetInformationInt32(ProcessInformationClass.ProcessSessionInformation); } /// /// Gets an array of FileTimes for the process. /// /// An array of times: creation time, exit time, kernel time, user time. private FileTime[] GetTimes() { FileTime[] times = new FileTime[4]; if (!Win32.GetProcessTimes(this, out times[0], out times[1], out times[2], out times[3])) Win32.ThrowLastError(); return times; } /// /// Opens and returns a handle to the process' token. This requires /// PROCESS_QUERY_LIMITED_INFORMATION access. /// /// A handle to the process' token. public TokenHandle GetToken() { return this.GetToken(TokenAccess.All); } /// /// Opens and returns a handle to the process' token. This requires /// PROCESS_QUERY_LIMITED_INFORMATION access. /// /// The desired access to the token. /// A handle to the process' token. public TokenHandle GetToken(TokenAccess access) { return new TokenHandle(this, access); } /// /// Forces the process to load the specified library. /// /// The path to the library. public void InjectDll(string path) { this.InjectDll(path, 0xffffffff); } /// /// Forces the process to load the specified library. /// /// The path to the library. /// The timeout, in milliseconds, for the process to load the library. public void InjectDll(string path, uint timeout) { IntPtr stringPage = this.AllocateMemory(path.Length * 2 + 2, MemoryProtection.ReadWrite); this.WriteMemory(stringPage, UnicodeEncoding.Unicode.GetBytes(path)); using (var thandle = this.CreateThread( Loader.GetProcedure("kernel32.dll", "LoadLibraryW"), stringPage )) thandle.Wait(timeout * Win32.TimeMsTo100Ns); this.FreeMemory(stringPage, path.Length * 2 + 2, false); } /// /// Gets whether the process is currently being debugged. This requires /// the PROCESS_QUERY_INFORMATION permission. /// public bool IsBeingDebugged() { bool debugged = false; if (!Win32.CheckRemoteDebuggerPresent(this, ref debugged)) Win32.ThrowLastError(); return debugged; } /// /// Gets whether the process is being debugged. /// public bool IsBeingDebuggedNative() { return this.GetInformationInt32(ProcessInformationClass.ProcessDebugFlags) != 0; } /// /// Gets whether the system will crash upon the process being terminated. /// public bool IsCritical() { return this.GetInformationInt32(ProcessInformationClass.ProcessBreakOnTermination) != 0; } /// /// Determines whether the process is running in a job. /// /// A boolean. public bool IsInJob() { bool result; if (!Win32.IsProcessInJob(this, IntPtr.Zero, out result)) Win32.ThrowLastError(); return result; } /// /// Determines whether the process is running in the specified job. /// /// The job object to check. /// A boolean. public bool IsInJob(JobObjectHandle jobObjectHandle) { bool result; if (!Win32.IsProcessInJob(this, jobObjectHandle, out result)) Win32.ThrowLastError(); return result; } /// /// Gets whether the process is a NTVDM process. /// public bool IsNtVdmProcess() { return this.GetInformationInt32(ProcessInformationClass.ProcessWx86Information) != 0; } /// /// Gets whether the process is using the POSIX subsystem. /// public unsafe bool IsPosix() { int subsystem; IntPtr pebBaseAddress = this.GetBasicInformation().PebBaseAddress; this.ReadMemory(pebBaseAddress.Increment(Peb.ImageSubsystemOffset), &subsystem, sizeof(int)); return subsystem == 7; } /// /// Gets whether the process has priority boost enabled. /// public bool IsPriorityBoostEnabled() { return this.GetInformationInt32(ProcessInformationClass.ProcessPriorityBoost) == 0; } /// /// Gets whether the process is running under WOW64. /// public bool IsWow64() { return this.GetInformationIntPtr(ProcessInformationClass.ProcessWow64Information) != IntPtr.Zero; } /// /// Sets the protection for a page in the process. /// /// The address to modify. /// The number of bytes to modify. /// The new memory protection. /// The old memory protection. public MemoryProtection ProtectMemory(IntPtr baseAddress, int size, MemoryProtection protection) { NtStatus status; IntPtr sizeIntPtr = size.ToIntPtr(); MemoryProtection oldProtection; if ((status = Win32.NtProtectVirtualMemory( this, ref baseAddress, ref sizeIntPtr, protection, out oldProtection )) >= NtStatus.Error) Win32.ThrowLastError(status); return oldProtection; } /// /// Gets information about the memory region starting at the specified address. /// /// The address to query. /// A MEMORY_BASIC_INFORMATION structure. public MemoryBasicInformation QueryMemory(IntPtr baseAddress) { NtStatus status; MemoryBasicInformation mbi; IntPtr retLength; if ((status = Win32.NtQueryVirtualMemory( this, baseAddress, MemoryInformationClass.MemoryBasicInformation, out mbi, Marshal.SizeOf(typeof(MemoryBasicInformation)).ToIntPtr(), out retLength )) >= NtStatus.Error) Win32.ThrowLastError(status); return mbi; } /// /// 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(IntPtr baseAddress, int length) { byte[] buffer = new byte[length]; this.ReadMemory(baseAddress, buffer, length); return buffer; } /// /// Reads data from the process' virtual memory. /// /// The offset at which to begin reading. /// The buffer to write to. /// The length to read. /// The number of bytes read. public unsafe int ReadMemory(IntPtr baseAddress, byte[] buffer, int length) { fixed (byte* bufferPtr = buffer) return this.ReadMemory(baseAddress, bufferPtr, length); } /// /// Reads data from the process' virtual memory. /// /// The offset at which to begin reading. /// The buffer to write to. /// The length to read. /// The number of bytes read. public unsafe int ReadMemory(IntPtr baseAddress, void* buffer, int length) { int retLength; if (KProcessHacker.Instance != null) { KProcessHacker.Instance.KphReadVirtualMemory(this, baseAddress.ToInt32(), buffer, length, out retLength); } else { NtStatus status; IntPtr retLengthIntPtr; if ((status = Win32.NtReadVirtualMemory( this, baseAddress, new IntPtr(buffer), length.ToIntPtr(), out retLengthIntPtr )) >= NtStatus.Error) Win32.ThrowLastError(status); retLength = retLengthIntPtr.ToInt32(); } return retLength; } /// /// Calls the specified function in the context of the process. /// /// The function to call. /// The arguments to pass to the function. public ThreadHandle RemoteCall(IntPtr address, IntPtr[] arguments) { IntPtr rtlExitUserThread = Loader.GetProcedure("ntdll.dll", "RtlExitUserThread"); // Create a suspended thread at RtlExitUserThread. var thandle = this.CreateNativeThread(rtlExitUserThread, IntPtr.Zero, true); // Do the remote call on this thread. thandle.RemoteCall(this, address, arguments, true); // Resume the thread. It will execute the remote call then exit. thandle.Resume(); return thandle; } /// /// Stops debugging the process attached to the specified debug object. This requires /// PROCESS_SUSPEND_RESUME access. /// /// The debug object which was used to debug the process. public void RemoveDebug(DebugObjectHandle debugObjectHandle) { NtStatus status; if ((status = Win32.NtRemoveProcessDebug(this, debugObjectHandle)) >= NtStatus.Error) Win32.ThrowLastError(status); } /// /// Resumes the process. This requires PROCESS_SUSPEND_RESUME access. /// public void Resume() { if (KProcessHacker.Instance != null && OSVersion.HasPsSuspendResumeProcess) { KProcessHacker.Instance.KphResumeProcess(this); } else { NtStatus status; if ((status = Win32.NtResumeProcess(this)) >= NtStatus.Error) Win32.ThrowLastError(status); } } /// /// Sets the processor affinity for the process. /// /// The processor affinity mask. public void SetAffinityMask(long processMask) { if (!Win32.SetProcessAffinityMask(this, new IntPtr(processMask))) Win32.ThrowLastError(); } /// /// Sets whether the system will crash upon the process being terminated. /// This function requires SeTcbPrivilege. /// /// Whether the system will crash upon the process being terminated. public void SetCritical(bool critical) { this.SetInformationInt32(ProcessInformationClass.ProcessBreakOnTermination, critical ? 1 : 0); } /// /// Sets the process' DEP policy. /// /// The DEP options. public void SetDepStatus(DepStatus depStatus) { MemExecuteOptions executeOptions = 0; if ((depStatus & DepStatus.Enabled) == DepStatus.Enabled) executeOptions |= MemExecuteOptions.ExecuteDisable; else executeOptions |= MemExecuteOptions.ExecuteEnable; if ((depStatus & DepStatus.AtlThunkEmulationDisabled) == DepStatus.AtlThunkEmulationDisabled) executeOptions |= MemExecuteOptions.DisableThunkEmulation; if ((depStatus & DepStatus.Permanent) == DepStatus.Permanent) executeOptions |= MemExecuteOptions.Permanent; KProcessHacker.Instance.SetExecuteOptions(this, executeOptions); } /// /// Sets information about the process in an Int32. /// /// The class of information to set. /// The value to set. private void SetInformationInt32(ProcessInformationClass infoClass, int value) { NtStatus status; if ((status = Win32.NtSetInformationProcess( this, infoClass, ref value, sizeof(int))) >= NtStatus.Error) Win32.ThrowLastError(status); } /// /// Sets the reference count of a module. /// /// The base address of the module. /// The new reference count. public unsafe void SetModuleReferenceCount(IntPtr baseAddress, ushort count) { byte* buffer = stackalloc byte[IntPtr.Size]; this.ReadMemory( this.GetBasicInformation().PebBaseAddress.Increment(Win32.PebLdrOffset), buffer, IntPtr.Size ); IntPtr loaderData = *(IntPtr*)buffer; PebLdrData* data = stackalloc PebLdrData[1]; this.ReadMemory(loaderData, data, Marshal.SizeOf(typeof(PebLdrData))); if (!data->Initialized) throw new Exception("Loader data is not initialized."); List modules = new List(); IntPtr currentLink = data->InLoadOrderModuleList.Flink; IntPtr startLink = currentLink; LdrDataTableEntry* currentEntry = stackalloc LdrDataTableEntry[1]; int i = 0; while (currentLink != IntPtr.Zero) { if (modules.Count > 0 && currentLink == startLink) break; if (i > 0x800) break; this.ReadMemory(currentLink, currentEntry, Marshal.SizeOf(typeof(LdrDataTableEntry))); if (currentEntry->DllBase == baseAddress) { this.WriteMemory(currentLink.Increment(LdrDataTableEntry.LoadCountOffset), &count, 2); break; } currentLink = currentEntry->InLoadOrderLinks.Flink; i++; } } /// /// Sets the process' priority class. /// /// The process' priority. public void SetPriorityClass(ProcessPriorityClass priority) { if (!Win32.SetPriorityClass(this, (int)priority)) Win32.ThrowLastError(); } /// /// Suspends the process. This requires PROCESS_SUSPEND_RESUME access. /// public void Suspend() { if (KProcessHacker.Instance != null && OSVersion.HasPsSuspendResumeProcess) { KProcessHacker.Instance.KphSuspendProcess(this); } else { NtStatus status; if ((status = Win32.NtSuspendProcess(this)) >= NtStatus.Error) Win32.ThrowLastError(status); } } /// /// Terminates the process. This requires PROCESS_TERMINATE access. /// public void Terminate() { this.Terminate(NtStatus.Success); } /// /// Terminates the process. This requires PROCESS_TERMINATE access. /// /// The exit status. public void Terminate(NtStatus exitStatus) { if (KProcessHacker.Instance != null) { KProcessHacker.Instance.KphTerminateProcess(this, exitStatus); } else { NtStatus status; if ((status = Win32.NtTerminateProcess(this, exitStatus)) >= NtStatus.Error) Win32.ThrowLastError(status); } } /// /// Writes a minidump of the process to the specified file. /// /// The destination file. public void WriteDump(string fileName) { // taskmgr uses these flags this.WriteDump(fileName, MinidumpType.WithFullMemory | MinidumpType.WithHandleData | MinidumpType.WithUnloadedModules | MinidumpType.WithFullMemoryInfo | MinidumpType.WithThreadInfo ); } /// /// Writes a minidump of the process to the specified file. /// /// The destination file. /// The type of minidump to write. public void WriteDump(string fileName, MinidumpType type) { using (var fhandle = new FileHandle(fileName, FileAccess.GenericWrite)) this.WriteDump(fhandle, type); } /// /// Writes a minidump of the process to the specified file. /// /// A handle to the destination file. /// The type of minidump to write. public void WriteDump(FileHandle fileHandle, MinidumpType type) { if (!Win32.MiniDumpWriteDump( this, this.GetProcessId(), fileHandle, type, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero )) Win32.ThrowLastError(); } /// /// 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 unsafe int WriteMemory(IntPtr baseAddress, byte[] data) { fixed (byte* dataPtr = data) { return WriteMemory(baseAddress, dataPtr, data.Length); } } /// /// Writes data to the process' virtual memory. /// /// The offset at which to begin writing. /// The data to write. /// The length to be written. /// The length, in bytes, that was written. public unsafe int WriteMemory(IntPtr baseAddress, void* data, int length) { int retLength; if (KProcessHacker.Instance != null) { KProcessHacker.Instance.KphWriteVirtualMemory(this, baseAddress.ToInt32(), data, length, out retLength); } else { NtStatus status; IntPtr retLengthIntPtr; if ((status = Win32.NtWriteVirtualMemory( this, baseAddress, new IntPtr(data), length.ToIntPtr(), out retLengthIntPtr )) >= NtStatus.Error) Win32.ThrowLastError(status); retLength = retLengthIntPtr.ToInt32(); } return retLength; } } /// /// Represents a stack trace collected during a handle trace event. /// public class ProcessHandleTrace { private ClientId _clientId; private IntPtr _handle; private IntPtr[] _stack; private HandleTraceType _type; internal ProcessHandleTrace(ProcessHandleTracingEntry entry) { _clientId = entry.ClientId; _handle = entry.Handle; _type = entry.Type; // Find the first occurrence of a NULL to find where the trace stops. int zeroIndex = Array.IndexOf(entry.Stacks, IntPtr.Zero); // If there was no NULL, copy the entire array. if (zeroIndex == -1) zeroIndex = entry.Stacks.Length; // Copy the actual stack trace, excluding NULLs. _stack = new IntPtr[zeroIndex]; Array.Copy(entry.Stacks, 0, _stack, 0, zeroIndex); } /// /// The client ID of the thread which produced the event. /// public ClientId ClientId { get { return _clientId; } } /// /// The handle value associated with the event. /// public IntPtr Handle { get { return _handle; } } /// /// A stack trace of the thread at the time of the event. /// public IntPtr[] Stack { get { return _stack; } } /// /// The type of handle trace event. /// public HandleTraceType Type { get { return _type; } } } /// /// Represents a collection of handle trace events. /// public class ProcessHandleTraceCollection : ReadOnlyCollection { private IntPtr _handle; internal ProcessHandleTraceCollection(MemoryAlloc data) : base(new List()) { if (data.Size < Marshal.SizeOf(typeof(ProcessHandleTracingQuery))) throw new ArgumentException("Data memory allocation is too small."); // Read the structure. var query = data.ReadStruct(); _handle = query.Handle; // Get the handle traces. IList traces = this.Items; for (int i = 0; i < query.TotalTraces; i++) { var entry = data.ReadStruct( Win32.ProcessHandleTracingQueryHandleTraceOffset, i ); traces.Add(new ProcessHandleTrace(entry)); } } /// /// A unique handle representing the collection. /// public IntPtr Handle { get { return _handle; } } } /// /// Represents a module loaded by a process. /// public class ProcessModule { public ProcessModule( IntPtr baseAddress, int size, IntPtr entryPoint, LdrpDataTableEntryFlags flags, string baseName, string fileName ) { this.BaseAddress = baseAddress; this.Size = size; this.EntryPoint = entryPoint; this.Flags = flags; this.BaseName = baseName; this.FileName = fileName; } /// /// The base address of the module. /// public IntPtr BaseAddress { get; private set; } /// /// The size of the module. /// public int Size { get; private set; } /// /// The entry point of the module (usually its DllMain function). /// public IntPtr EntryPoint { get; private set; } /// /// The flags set by the NT loader for this module. /// public LdrpDataTableEntryFlags Flags { get; private set; } /// /// The base name of the module (e.g. module.dll). /// public string BaseName { get; private set; } /// /// The file name of the module (e.g. C:\Windows\system32\module.dll). /// public string FileName { get; private set; } } /// /// 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 } /// /// A well-known Windows process. /// public enum KnownProcess { /// /// The process is not well-known. /// None, /// /// System Idle Process. /// Idle, /// /// NT Kernel & System. /// System, /// /// Windows Session Manager (smss) /// SessionManager, /// /// Client Server Runtime Process (csrss) /// WindowsSubsystem, /// /// Windows Start-Up Application (wininit) /// WindowsStartup, /// /// Services and Controller app (services) /// ServiceControlManager, /// /// Local Security Authority Process (lsass) /// LocalSecurityAuthority, /// /// Local Session Manager Service (lsm) /// LocalSessionManager } /// /// 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, /// /// A copy of the PATH environment variable for the process. /// DllPath, /// /// The image file name, in kernel format (e.g. \\?\C:\..., /// \SystemRoot\..., \Device\Harddisk1\...). /// ImagePathName, /// /// The command used to start the program, including arguments. /// CommandLine, /// /// Usually blank. /// WindowTitle, /// /// For interactive programs, contains the window station and /// desktop name of the first thread that was started, e.g. /// WinSta0\Default. /// DesktopName, /// /// Usually blank. /// ShellInfo, /// /// Usually blank. /// RuntimeData } }