/* * Process Hacker - * thread 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.Diagnostics; using System.Runtime.InteropServices; using ProcessHacker.Native.Api; using ProcessHacker.Native.Security; namespace ProcessHacker.Native.Objects { /// /// Represents a handle to a Windows thread. /// public class ThreadHandle : Win32Handle, IWithToken { public static ThreadHandle Create( ThreadAccess access, string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, ProcessHandle processHandle, out ClientId clientId, ref Context threadContext, ref InitialTeb initialTeb, bool createSuspended ) { NtStatus status; ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); IntPtr handle; try { if ((status = Win32.NtCreateThread( out handle, access, ref oa, processHandle, out clientId, ref threadContext, ref initialTeb, createSuspended )) >= NtStatus.Error) Win32.ThrowLastError(status); } finally { oa.Dispose(); } return new ThreadHandle(handle, true); } /// /// Creates a thread handle using an existing handle. /// The handle will not be closed automatically. /// /// The handle value. /// The thread handle. public static ThreadHandle FromHandle(IntPtr handle) { return new ThreadHandle(handle, false); } /// /// Gets a handle to the current thread. /// /// A thread handle. public static ThreadHandle GetCurrent() { return new ThreadHandle(new IntPtr(-2), false); } public static void RegisterTerminationPort(PortHandle portHandle) { NtStatus status; if ((status = Win32.NtRegisterThreadTerminatePort(portHandle)) >= NtStatus.Error) Win32.ThrowLastError(status); } public static void TestAlert() { NtStatus status; if ((status = Win32.NtTestAlert()) >= NtStatus.Error) Win32.ThrowLastError(status); } internal ThreadHandle(IntPtr handle, bool owned) : base(handle, owned) { } /// /// Creates a new thread handle. /// /// The ID of the thread to open. public ThreadHandle(int tid) : this(tid, ThreadAccess.All) { } /// /// Creates a new thread handle. /// /// The ID of the thread to open. /// The desired access to the thread. public ThreadHandle(int tid, ThreadAccess access) { if (KProcessHacker.Instance != null) { try { this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenThread(tid, access)); } catch (WindowsException) { // Open the thread with minimum access (SYNCHRONIZE) and set the granted access. this.Handle = new IntPtr(KProcessHacker.Instance.KphOpenThread(tid, (ThreadAccess)StandardRights.Synchronize)); KProcessHacker.Instance.KphSetHandleGrantedAccess(this.Handle, (int)access); } } else { this.Handle = Win32.OpenThread(access, false, tid); } if (this.Handle == IntPtr.Zero) Win32.ThrowLastError(); } public ThreadHandle( string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, ClientId clientId, ThreadAccess access ) { NtStatus status; ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); IntPtr handle; try { if (clientId.ProcessId == 0 && clientId.ThreadId == 0) { if ((status = Win32.NtOpenThread( out handle, access, ref oa, IntPtr.Zero )) >= NtStatus.Error) Win32.ThrowLastError(status); } else { if ((status = Win32.NtOpenThread( out handle, access, ref oa, ref clientId )) >= NtStatus.Error) Win32.ThrowLastError(status); } } finally { oa.Dispose(); } this.Handle = handle; } public ThreadHandle(string name, ThreadAccess access) : this(name, 0, null, new ClientId(), access) { } /// /// Puts the thread in an alerted state. /// public void Alert() { NtStatus status; if ((status = Win32.NtAlertThread(this)) >= NtStatus.Error) Win32.ThrowLastError(status); } /// /// Resumes the thread in an alerted state. /// public int AlertResume() { NtStatus status; int suspendCount; if ((status = Win32.NtAlertResumeThread(this, out suspendCount)) >= NtStatus.Error) Win32.ThrowLastError(status); return suspendCount; } /// /// Gets the thread's base priority. /// public int GetBasePriority() { return this.GetInformationInt32(ThreadInformationClass.ThreadBasePriority); } /// /// Gets the thread's basic information. /// /// A THREAD_BASIC_INFORMATION structure. public ThreadBasicInformation GetBasicInformation() { NtStatus status; ThreadBasicInformation basicInfo = new ThreadBasicInformation(); int retLen; if ((status = Win32.NtQueryInformationThread(this, ThreadInformationClass.ThreadBasicInformation, ref basicInfo, Marshal.SizeOf(basicInfo), out retLen)) >= NtStatus.Error) Win32.ThrowLastError(status); return basicInfo; } /// /// Gets the thread's context. /// /// A CONTEXT struct. public Context GetContext(ContextFlags flags) { Context context = new Context(); context.ContextFlags = flags; this.GetContext(ref context); return context; } /// /// Gets the thread's context. /// /// A Context structure. The ContextFlags must be set appropriately. public unsafe void GetContext(ref Context context) { if (KProcessHacker.Instance != null) { fixed (Context* contextPtr = &context) KProcessHacker.Instance.KphGetContextThread(this, contextPtr); } else { if (!Win32.GetThreadContext(this, ref context)) Win32.ThrowLastError(); } } /// /// Gets the number of processor cycles consumed by the thread. /// public ulong GetCycleTime() { ulong cycles; if (!Win32.QueryThreadCycleTime(this, out cycles)) Win32.ThrowLastError(); return cycles; } /// /// Gets the thread's exit code. /// /// A number. public int GetExitCode() { int exitCode; if (!Win32.GetExitCodeThread(this, out exitCode)) Win32.ThrowLastError(); return exitCode; } private int GetInformationInt32(ThreadInformationClass infoClass) { NtStatus status; int value; int retLength; if ((status = Win32.NtQueryInformationThread( this, infoClass, out value, sizeof(int), out retLength)) >= NtStatus.Error) Win32.ThrowLastError(status); return value; } /// /// Gets the thread's I/O priority. /// public int GetIoPriority() { return this.GetInformationInt32(ThreadInformationClass.ThreadIoPriority); } /// /// Gets the last system call the thread made. /// /// A system call number. public int GetLastSystemCall() { int firstArgument; return this.GetLastSystemCall(out firstArgument); } /// /// Gets the last system call the thread made. /// /// The first argument to the last system call. /// A system call number. public unsafe int GetLastSystemCall(out int firstArgument) { NtStatus status; int* data = stackalloc int[2]; int retLength; if ((status = Win32.NtQueryInformationThread( this, ThreadInformationClass.ThreadLastSystemCall, data, sizeof(int) * 2, out retLength)) >= NtStatus.Error) Win32.ThrowLastError(status); firstArgument = data[0]; return data[1]; } /// /// Gets the thread's page priority. /// public int GetPagePriority() { return this.GetInformationInt32(ThreadInformationClass.ThreadPagePriority); } /// /// Gets the thread's priority. /// public int GetPriority() { return this.GetInformationInt32(ThreadInformationClass.ThreadPriority); } /// /// Gets the thread's priority level. /// /// A ThreadPriorityLevel enum. public ThreadPriorityLevel GetPriorityLevel() { int priority = Win32.GetThreadPriority(this); if (priority == 0x7fffffff) Win32.ThrowLastError(); return (ThreadPriorityLevel)priority; } /// /// Gets the thread's Win32 start address. /// public int GetWin32StartAddress() { return this.GetInformationInt32(ThreadInformationClass.ThreadQuerySetWin32StartAddress); } public void Impersonate(ThreadHandle clientThreadHandle, SecurityImpersonationLevel impersonationLevel) { NtStatus status; SecurityQualityOfService securityQos = new SecurityQualityOfService(impersonationLevel, false, false); if ((status = Win32.NtImpersonateThread(this, clientThreadHandle, ref securityQos)) >= NtStatus.Error) Win32.ThrowLastError(status); } public void ImpersonateAnonymous() { NtStatus status; if ((status = Win32.NtImpersonateAnonymousToken(this)) >= NtStatus.Error) Win32.ThrowLastError(status); } /// /// Gets whether the system will break (crash) upon the thread terminating. /// public bool IsCritical() { return this.GetInformationInt32(ThreadInformationClass.ThreadBreakOnTermination) != 0; } /// /// Gets whether any I/O request packets (IRPs) are still pending for the thread. /// public bool IsIoPending() { return this.GetInformationInt32(ThreadInformationClass.ThreadIsIoPending) != 0; } /// /// Gets whether the thread is the last in its process. /// public bool IsLastThread() { return this.GetInformationInt32(ThreadInformationClass.ThreadAmILastThread) != 0; } /// /// Gets whether priority boost is enabled for the thread. /// public bool IsPriorityBoostEnabled() { return this.GetInformationInt32(ThreadInformationClass.ThreadPriorityBoost) == 0; } /// /// Gets whether the thread has terminated. /// public bool IsTerminated() { return this.GetInformationInt32(ThreadInformationClass.ThreadIsTerminated) != 0; } /// /// Adds an user-mode asynchronous procedure call (APC) to the thread's APC queue. /// This requires the THREAD_SET_CONTEXT permission. /// /// The address of the APC procedure. /// The parameter to pass to the procedure. public void QueueApc(IntPtr address, IntPtr parameter) { if (!Win32.QueueUserAPC(address, this, parameter)) Win32.ThrowLastError(); } public void QueueApc(IntPtr address, IntPtr param1, IntPtr param2, IntPtr param3) { NtStatus status; if ((status = Win32.NtQueueApcThread( this, address, param1, param2, param3 )) >= NtStatus.Error) Win32.ThrowLastError(status); } /// /// Sets the thread's context. /// /// A CONTEXT struct. public unsafe void SetContext(Context context) { if (KProcessHacker.Instance != null) { KProcessHacker.Instance.KphSetContextThread(this, &context); } else { if (!Win32.SetThreadContext(this, ref context)) Win32.ThrowLastError(); } } public void SetCritical(bool critical) { this.SetInformationInt32(ThreadInformationClass.ThreadBreakOnTermination, critical ? 1 : 0); } private void SetInformationInt32(ThreadInformationClass infoClass, int value) { NtStatus status; if ((status = Win32.NtSetInformationThread( this, infoClass, ref value, sizeof(int))) >= NtStatus.Error) Win32.ThrowLastError(status); } /// /// Sets the thread's priority level. /// /// The priority of the thread. public void SetPriorityLevel(ThreadPriorityLevel priority) { if (!Win32.SetThreadPriority(this, (int)priority)) Win32.ThrowLastError(); } /// /// Suspends the thread. /// public int Suspend() { NtStatus status; int suspendCount; if ((status = Win32.NtSuspendThread(this, out suspendCount)) >= NtStatus.Error) Win32.ThrowLastError(status); return suspendCount; } /// /// Resumes the thread. /// public int Resume() { NtStatus status; int suspendCount; if ((status = Win32.NtResumeThread(this, out suspendCount)) >= NtStatus.Error) Win32.ThrowLastError(status); return suspendCount; } /// /// Terminates the thread. /// public void Terminate() { this.Terminate(0); } /// /// Terminates the thread, specifying an exit code. /// /// The exit code. public void Terminate(int ExitCode) { if (KProcessHacker.Instance != null) { try { KProcessHacker.Instance.KphTerminateThread(this, ExitCode); return; } catch (WindowsException ex) { if (ex.ErrorCode != 0x32) // ERROR_NOT_SUPPORTED throw ex; } } if (!Win32.TerminateThread(this, ExitCode)) Win32.ThrowLastError(); } /// /// Opens and returns a handle to the thread's token. /// /// A handle to the thread's token. public TokenHandle GetToken() { return GetToken(TokenAccess.All); } /// /// Opens and returns a handle to the thread's token. /// /// The desired access to the token. /// A handle to the thread's token. public TokenHandle GetToken(TokenAccess access) { return new TokenHandle(this, access); } } }