/* * 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.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. /// /// 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.Throw(status); } finally { oa.Dispose(); } return new ProcessHandle(handle, true); } public static ProcessHandle CreateExtended( string fileName, ProcessHandle parentProcess, ProcessCreationFlags creationFlags, bool inheritHandles, string currentDirectory, StartupInfo startupInfo, out ClientId clientId, out ThreadHandle threadHandle ) { return CreateExtended( fileName, parentProcess, creationFlags, true, inheritHandles, EnvironmentBlock.GetCurrent(), currentDirectory, startupInfo, out clientId, out threadHandle ); } public static ProcessHandle CreateExtended( string fileName, ProcessHandle parentProcess, ProcessCreationFlags creationFlags, bool notifyCsr, bool inheritHandles, EnvironmentBlock environment, string currentDirectory, StartupInfo startupInfo, out ClientId clientId, out ThreadHandle threadHandle ) { ProcessHandle phandle; ThreadHandle thandle; SectionImageInformation imageInfo; // If we don't have a desktop, use the current one. if (startupInfo.Desktop == null) startupInfo.Desktop = ProcessHandle.Current.GetPebString(PebOffset.DesktopName); // Open the file, create a section, and create a process. using (var fhandle = new FileHandle( fileName, FileShareMode.Read | FileShareMode.Delete, FileAccess.Execute | (FileAccess)StandardRights.Synchronize )) { using (var shandle = SectionHandle.Create( SectionAccess.All, SectionAttributes.Image, MemoryProtection.Execute, fhandle )) { imageInfo = shandle.GetImageInformation(); phandle = Create( ProcessAccess.All, parentProcess, inheritHandles, shandle ); } } IntPtr peb = phandle.GetBasicInformation().PebBaseAddress; // Copy the process parameters across. NativeUtils.CopyProcessParameters( phandle, peb, creationFlags, FileUtils.GetFileName(fileName), ProcessHandle.Current.GetPebString(PebOffset.DllPath), currentDirectory, fileName, environment, startupInfo.Title != null ? startupInfo.Title : fileName, startupInfo.Desktop != null ? startupInfo.Desktop : "", startupInfo.Reserved != null ? startupInfo.Reserved : "", "", ref startupInfo ); // TODO: Duplicate the console handles (stdin, stdout, stderr). // Create the initial thread. thandle = ThreadHandle.CreateUserThread( phandle, true, imageInfo.StackCommit.Increment(imageInfo.StackReserved).ToInt32(), imageInfo.StackCommit.ToInt32(), imageInfo.TransferAddress, IntPtr.Zero, out clientId ); // Notify CSR. if (notifyCsr) { BaseCreateProcessMsg processMsg = new BaseCreateProcessMsg(); processMsg.ProcessHandle = phandle; processMsg.ThreadHandle = thandle; processMsg.ClientId = clientId; processMsg.CreationFlags = creationFlags; if ((creationFlags & (ProcessCreationFlags.DebugProcess | ProcessCreationFlags.DebugOnlyThisProcess)) != 0) { NtStatus status; status = Win32.DbgUiConnectToDbg(); if (status >= NtStatus.Error) { phandle.Terminate(status); Win32.Throw(status); } processMsg.DebuggerClientId = ThreadHandle.GetCurrentCid(); } // If this is a GUI program, set the 1 and 2 bits to turn the // hourglass cursor on. if (imageInfo.ImageSubsystem == 2) processMsg.ProcessHandle = processMsg.ProcessHandle.Or((1 | 2).ToIntPtr()); // We still have to honor the startup info settings, though. if ((startupInfo.Flags & StartupFlags.ForceOnFeedback) == StartupFlags.ForceOnFeedback) processMsg.ProcessHandle = processMsg.ProcessHandle.Or((1).ToIntPtr()); if ((startupInfo.Flags & StartupFlags.ForceOffFeedback) == StartupFlags.ForceOffFeedback) processMsg.ProcessHandle = processMsg.ProcessHandle.And((1).ToIntPtr().Not()); using (var data = new MemoryAlloc( CsrApiMsg.ApiMessageDataOffset + Marshal.SizeOf(typeof(BaseCreateProcessMsg)) )) { data.WriteStruct(CsrApiMsg.ApiMessageDataOffset, 0, processMsg); Win32.CsrClientCallServer( data, IntPtr.Zero, Win32.CsrMakeApiNumber(Win32.BaseSrvServerDllIndex, (int)BaseSrvApiNumber.BasepCreateProcess), Marshal.SizeOf(typeof(BaseCreateProcessMsg)) ); NtStatus status = (NtStatus)data.ReadStruct().ReturnValue; if (status >= NtStatus.Error) { phandle.Terminate(status); Win32.Throw(status); } } } if ((creationFlags & ProcessCreationFlags.CreateSuspended) == 0) thandle.Resume(); threadHandle = thandle; return phandle; } 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.Throw(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); } } public static ProcessHandle CreateWin32( string applicationName, string commandLine, bool inheritHandles, ProcessCreationFlags creationFlags, EnvironmentBlock environment, string currentDirectory, StartupInfo startupInfo, out ClientId clientId, out ThreadHandle threadHandle ) { ProcessInformation processInformation; startupInfo.Size = Marshal.SizeOf(typeof(StartupInfo)); if (!Win32.CreateProcess( applicationName, commandLine, IntPtr.Zero, IntPtr.Zero, inheritHandles, creationFlags, environment, currentDirectory, ref startupInfo, out processInformation )) Win32.Throw(); clientId = new ClientId(processInformation.ProcessId, processInformation.ThreadId); threadHandle = new ThreadHandle(processInformation.ThreadHandle, true); return new ProcessHandle(processInformation.ProcessHandle, true); } public static ProcessHandle CreateWin32( TokenHandle tokenHandle, string applicationName, string commandLine, bool inheritHandles, ProcessCreationFlags creationFlags, EnvironmentBlock environment, string currentDirectory, StartupInfo startupInfo, out ClientId clientId, out ThreadHandle threadHandle ) { ProcessInformation processInformation; startupInfo.Size = Marshal.SizeOf(typeof(StartupInfo)); if (!Win32.CreateProcessAsUser( tokenHandle, applicationName, commandLine, IntPtr.Zero, IntPtr.Zero, inheritHandles, creationFlags, environment, currentDirectory, ref startupInfo, out processInformation )) Win32.Throw(); clientId = new ClientId(processInformation.ProcessId, processInformation.ThreadId); threadHandle = new ThreadHandle(processInformation.ThreadHandle, true); return new ProcessHandle(processInformation.ProcessHandle, true); } /// /// 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(); } /// /// Gets a pointer to the current process' environment block. /// /// A pointer to the current PEB. public unsafe static Peb* GetCurrentPeb() { return (Peb*)ThreadHandle.GetCurrentTeb()->ProcessEnvironmentBlock; } public unsafe static RtlUserProcessParameters* GetCurrentProcessParameters() { return (RtlUserProcessParameters*)GetCurrentPeb()->ProcessParameters; } 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.OrdinalIgnoreCase)) { 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); } public static ProcessHandle OpenWithAnyAccess(int pid) { try { return new ProcessHandle(pid, OSVersion.MinProcessQueryInfoAccess); } catch { try { return new ProcessHandle(pid, (ProcessAccess)StandardRights.Synchronize); } catch { try { return new ProcessHandle(pid, (ProcessAccess)StandardRights.ReadControl); } catch { try { return new ProcessHandle(pid, (ProcessAccess)StandardRights.WriteDac); } catch { return new ProcessHandle(pid, (ProcessAccess)StandardRights.WriteOwner); } } } } } 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.Throw(); } } /// /// 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.Throw(status); } else { // No name, specify a CID. if ((status = Win32.NtOpenProcess( out handle, access, ref oa, ref clientId )) >= NtStatus.Error) Win32.Throw(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) { IntPtr sizeIntPtr = new IntPtr(size); return this.AllocateMemory(baseAddress, ref sizeIntPtr, type, protection); } /// /// Allocates a memory region in the process' virtual memory. /// /// The base address of the region. /// /// The size of the region. This variable will be modified to contain /// the actual allocated size. /// /// The type of allocation. /// The protection of the region. /// The base address of the allocated pages. public IntPtr AllocateMemory(IntPtr baseAddress, ref IntPtr size, MemoryFlags type, MemoryProtection protection) { NtStatus status; if ((status = Win32.NtAllocateVirtualMemory( this, ref baseAddress, IntPtr.Zero, ref size, type, protection )) >= NtStatus.Error) Win32.Throw(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.Throw(); } /// /// 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. /// This function will work across sessions, unlike CreateThreadWin32. 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. /// This function will work across sessions, unlike CreateThreadWin32. 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. /// This function will work across sessions, unlike CreateThreadWin32. public ThreadHandle CreateThread(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 and notifies the Win32 subsystem. /// /// The address at which to begin execution. /// The parameter to pass to the function. /// A handle to the new thread. public ThreadHandle CreateThreadWin32(IntPtr startAddress, IntPtr parameter) { return this.CreateThreadWin32(startAddress, parameter, false); } /// /// Creates a thread in the process and notifies 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. public ThreadHandle CreateThreadWin32(IntPtr startAddress, IntPtr parameter, bool createSuspended) { int threadId; return this.CreateThreadWin32(startAddress, parameter, createSuspended, out threadId); } /// /// Creates a thread in the process and notifies 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. public ThreadHandle CreateThreadWin32(IntPtr startAddress, IntPtr parameter, bool createSuspended, out int threadId) { IntPtr threadHandle; if ((threadHandle = Win32.CreateRemoteThread( this, IntPtr.Zero, IntPtr.Zero, startAddress, parameter, createSuspended ? ProcessCreationFlags.CreateSuspended : 0, out threadId )) == IntPtr.Zero) Win32.Throw(); 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.Throw(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.Throw(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.Throw(); } /// /// 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.Throw(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.Throw(); 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.Throw(); if (Win32.GetModuleBaseName(this, moduleHandles[i], baseName, baseName.Capacity * 2) == 0) Win32.Throw(); if (Win32.GetModuleFileNameEx(this, moduleHandles[i], fileName, fileName.Capacity * 2) == 0) Win32.Throw(); if (!enumModulesCallback(new ProcessModule( moduleInfo.BaseOfDll, moduleInfo.SizeOfImage, moduleInfo.EntryPoint, 0, baseName.ToString(), FileUtils.GetFileName(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(Peb.LdrOffset), buffer, IntPtr.Size); IntPtr loaderData = *(IntPtr*)buffer; PebLdrData data; // 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; 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, ¤tEntry, 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.GetFileName(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.Throw(status); return isb.Status; } /// /// Frees a memory region in the process' virtual memory. /// /// The address of the region to free. /// The size to free. public void FreeMemory(IntPtr baseAddress, int size) { this.FreeMemory(baseAddress, size, false); } /// /// 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.Throw(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.Throw(); 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. This requires QueryLimitedInformation /// access. /// /// 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.Throw(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 DateTime GetCreateTime() { return DateTime.FromFileTime(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.Throw(); 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 (OSVersion.Architecture == OSArch.Amd64) { 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(Peb.ProcessParametersOffset), 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. using (var memoryAlloc = new MemoryAlloc(length)) { byte* memory = (byte*)memoryAlloc.Memory; this.ReadMemory(envBase, memoryAlloc.Memory, 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 >= length) break; char currentChar; Encoding.Unicode.GetChars(&memory[i], 2, ¤tChar, 1); 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.Throw(); 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 DateTime GetExitTime() { return DateTime.FromFileTime(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.ResizeNew(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.ResizeNew(data.Size * 4); continue; } if (status >= NtStatus.Error) Win32.Throw(status); return new ProcessHandleTraceCollection(data); } Win32.Throw(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(Peb.ProcessHeapOffset), &heap, IntPtr.Size ); return heap; } /// /// Gets the file name of the process' image. This requires /// QueryLimitedInformation access. /// /// A file name, in native format. public string GetImageFileName() { return this.GetInformationUnicodeString(ProcessInformationClass.ProcessImageFileName); } /// /// Gets the file name of the process' image. This requires /// QueryLimitedInformation access. /// /// A file name, in DOS format. public string GetImageFileNameWin32() { return this.GetInformationUnicodeString(ProcessInformationClass.ProcessImageFileNameWin32); } /// /// Gets information about the process in an Int32. /// /// The class of information to retrieve. /// An int. private int GetInformationInt32(ProcessInformationClass infoClass) { if ( KProcessHacker.Instance != null && infoClass == ProcessInformationClass.ProcessIoPriority ) { unsafe { int value; int retLength; KProcessHacker.Instance.KphQueryInformationProcess( this, infoClass, new IntPtr(&value), sizeof(int), out retLength ); return value; } } else { NtStatus status; int value; int retLength; if ((status = Win32.NtQueryInformationProcess( this, infoClass, out value, sizeof(int), out retLength)) >= NtStatus.Error) Win32.Throw(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.Throw(status); return value; } private string GetInformationUnicodeString(ProcessInformationClass infoClass) { NtStatus status; int retLen; Win32.NtQueryInformationProcess(this, infoClass, IntPtr.Zero, 0, out retLen); using (MemoryAlloc data = new MemoryAlloc(retLen)) { if ((status = Win32.NtQueryInformationProcess(this, infoClass, data, retLen, out retLen)) >= NtStatus.Error) Win32.Throw(status); return data.ReadStruct().Read(); } } /// /// 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.Throw(status); return counters; } /// /// Opens the job object associated with the process. /// /// A job object handle. public JobObjectHandle GetJobObject(JobObjectAccess access) { IntPtr handle; handle = JobObjectHandle.Open(this, access); if (handle != IntPtr.Zero) return new JobObjectHandle(handle, true); else return null; } /// /// 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.GetFileName(this.GetImageFileName()); if (fileName.StartsWith(Environment.SystemDirectory, StringComparison.OrdinalIgnoreCase)) { string baseName = fileName.Remove(0, Environment.SystemDirectory.Length).TrimStart('\\').ToLowerInvariant(); 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) { NtStatus status; IntPtr retLength; using (var data = new MemoryAlloc(20)) { if ((status = Win32.NtQueryVirtualMemory( this, address, MemoryInformationClass.MemoryMappedFilenameInformation, data, data.Size.ToIntPtr(), out retLength )) == NtStatus.BufferOverflow) { data.ResizeNew(retLength.ToInt32()); status = Win32.NtQueryVirtualMemory( this, address, MemoryInformationClass.MemoryMappedFilenameInformation, data, data.Size.ToIntPtr(), out retLength ); } if (status >= NtStatus.Error) return null; return FileUtils.GetFileName(data.ReadStruct().Read()); } } /// /// 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.Throw(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(); } /// /// 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.Throw(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.Throw(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(Peb.ProcessParametersOffset), 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 Encoding.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(Peb.ProcessParametersOffset), 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(Encoding.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() { //switch (Win32.GetPriorityClass(this)) //{ // case ProcessPriorityClassWin32.AboveNormal: // return ProcessPriorityClass.AboveNormal; // case ProcessPriorityClassWin32.BelowNormal: // return ProcessPriorityClass.BelowNormal; // case ProcessPriorityClassWin32.High: // return ProcessPriorityClass.High; // case ProcessPriorityClassWin32.Idle: // return ProcessPriorityClass.Idle; // case ProcessPriorityClassWin32.Normal: // return ProcessPriorityClass.Normal; // case ProcessPriorityClassWin32.RealTime: // return ProcessPriorityClass.RealTime; // default: // Win32.Throw(); // // Stupid compiler // return ProcessPriorityClass.Unknown; //} NtStatus status; ProcessPriorityClassStruct priorityClass; int retLength; if ((status = Win32.NtQueryInformationProcess( this, ProcessInformationClass.ProcessPriorityClass, out priorityClass, Marshal.SizeOf(typeof(ProcessPriorityClassStruct)), out retLength )) != NtStatus.Success) Win32.Throw(status); return (ProcessPriorityClass)priorityClass.PriorityClass; } /// /// 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 times for the process. /// /// An array of times: creation time, exit time, kernel time, user time. private LargeInteger[] GetTimes() { LargeInteger[] times = new LargeInteger[4]; if (!Win32.GetProcessTimes(this, out times[0], out times[1], out times[2], out times[3])) Win32.Throw(); 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, Encoding.Unicode.GetBytes(path)); // Vista seems to support non-Win32 threads better than XP can. if (OSVersion.IsAboveOrEqual(WindowsVersion.Vista)) { using (var thandle = this.CreateThread( Loader.GetProcedure("kernel32.dll", "LoadLibraryW"), stringPage )) thandle.Wait(timeout * Win32.TimeMsTo100Ns); } else { using (var thandle = this.CreateThreadWin32( 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 /// QueryInformation access. /// public bool IsBeingDebugged() { return this.GetInformationIntPtr(ProcessInformationClass.ProcessDebugPort) != IntPtr.Zero; } /// /// 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.Throw(); 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.Throw(); 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.Throw(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.Throw(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) { return this.ReadMemory(baseAddress, new IntPtr(buffer), 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 int ReadMemory(IntPtr baseAddress, IntPtr buffer, int length) { int retLength; if (this.Handle == Current) { Win32.RtlMoveMemory(buffer, baseAddress, length.ToIntPtr()); return length; } 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, buffer, length.ToIntPtr(), out retLengthIntPtr )) >= NtStatus.Error) Win32.Throw(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.CreateThread(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.Throw(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.Throw(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.Throw(); } /// /// Sets the process' base priority. /// /// The process' base priority. public void SetBasePriority(int basePriority) { this.SetInformationInt32(ProcessInformationClass.ProcessBasePriority, basePriority); } /// /// 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) { if ( KProcessHacker.Instance != null && infoClass == ProcessInformationClass.ProcessIoPriority ) { unsafe { KProcessHacker.Instance.KphSetInformationProcess( this, infoClass, new IntPtr(&value), sizeof(int) ); } } else { NtStatus status; if ((status = Win32.NtSetInformationProcess( this, infoClass, ref value, sizeof(int))) >= NtStatus.Error) Win32.Throw(status); } } public void SetIoPriority(int ioPriority) { this.SetInformationInt32(ProcessInformationClass.ProcessIoPriority, ioPriority); } /// /// 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(Peb.LdrOffset), buffer, IntPtr.Size ); IntPtr loaderData = *(IntPtr*)buffer; PebLdrData data; 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; int i = 0; while (currentLink != IntPtr.Zero) { if (modules.Count > 0 && currentLink == startLink) break; if (i > 0x800) break; this.ReadMemory(currentLink, ¤tEntry, Marshal.SizeOf(typeof(LdrDataTableEntry))); if (currentEntry.DllBase == baseAddress) { this.WriteMemory(currentLink.Increment(LdrDataTableEntry.LoadCountOffset), &count, 2); break; } currentLink = currentEntry.InLoadOrderLinks.Flink; i++; } } public void SetPagePriority(int pagePriority) { this.SetInformationInt32(ProcessInformationClass.ProcessPagePriority, pagePriority); } /// /// Sets the process' priority boost. /// /// Whether priority boost will be enabled. public void SetPriorityBoost(bool enabled) { // If priority boost is being enabled, we have to not disable it (hence the value of 0). this.SetInformationInt32(ProcessInformationClass.ProcessPriorityBoost, enabled ? 0 : 1); } /// /// Sets the process' priority class. /// /// The process' priority class. public void SetPriorityClass(ProcessPriorityClass priorityClass) { //ProcessPriorityClassWin32 pcWin32; //switch (priorityClass) //{ // case ProcessPriorityClass.AboveNormal: // pcWin32 = ProcessPriorityClassWin32.AboveNormal; // break; // case ProcessPriorityClass.BelowNormal: // pcWin32 = ProcessPriorityClassWin32.BelowNormal; // break; // case ProcessPriorityClass.High: // pcWin32 = ProcessPriorityClassWin32.High; // break; // case ProcessPriorityClass.Idle: // pcWin32 = ProcessPriorityClassWin32.Idle; // break; // case ProcessPriorityClass.Normal: // pcWin32 = ProcessPriorityClassWin32.Normal; // break; // case ProcessPriorityClass.RealTime: // pcWin32 = ProcessPriorityClassWin32.RealTime; // break; // default: // throw new ArgumentException("priorityClass"); //} //if (!Win32.SetPriorityClass(this, pcWin32)) // Win32.Throw(); NtStatus status; ProcessPriorityClassStruct processPriority; processPriority.Foreground = '\0'; processPriority.PriorityClass = Convert.ToChar(priorityClass); if ((status = Win32.NtSetInformationProcess( this, ProcessInformationClass.ProcessPriorityClass, ref processPriority, Marshal.SizeOf(typeof(ProcessPriorityClassStruct)) )) != NtStatus.Success) Win32.Throw(status); } /// /// 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.Throw(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.Throw(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 = FileHandle.CreateWin32(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.Throw(); } /// /// 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(IntPtr baseAddress, byte[] buffer) { unsafe { fixed (byte* dataPtr = buffer) { return WriteMemory(baseAddress, dataPtr, buffer.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* buffer, int length) { return this.WriteMemory(baseAddress, new IntPtr(buffer), 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 int WriteMemory(IntPtr baseAddress, IntPtr buffer, int length) { int retLength; if (this.Handle == Current) { Win32.RtlMoveMemory(baseAddress, buffer, length.ToIntPtr()); return length; } if (KProcessHacker.Instance != null) { KProcessHacker.Instance.KphWriteVirtualMemory(this, baseAddress.ToInt32(), buffer, length, out retLength); } else { NtStatus status; IntPtr retLengthIntPtr; if ((status = Win32.NtWriteVirtualMemory( this, baseAddress, buffer, length.ToIntPtr(), out retLengthIntPtr )) >= NtStatus.Error) Win32.Throw(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( ProcessHandleTracingQuery.HandleTraceOffset, 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 : ILoadedModule { 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 } }