using Microsoft.Win32.SafeHandles; using System; using System.ComponentModel; /// /// Static core class providing tools for manipulating threads. /// public static class ThreadCore { #region GetThreadContext /// /// Retrieves the context of the specified thread. /// /// The type of the context to dump. /// The type must be unmanaged, so it can be fixed while the native call is done. /// The performance is increased if the structure is blittable, which is the case for the structures /// provided with the library. /// A handle to the thread whose context is to be retrieved. /// An instance of the structure where the context is loaded into. /// The context cannot be retrieved from the thread. public static unsafe void GetThreadContext(SafeMemoryHandle threadHandle, ref TContext context) where TContext : unmanaged { // Check if the handle is valid HandleManipulator.ValidateAsArgument(threadHandle, "threadHandle"); // Get the pointer of the structure and pin it, so the GC does not move it fixed (void* contextPtr = &context) { // Get the thread context if (NativeMethods.GetThreadContext(threadHandle, contextPtr) == (void*)0) { throw new Win32Exception("The context cannot be retrieved from the thread."); } } } #endregion #region NtQueryInformationThread /// /// Retrieves information about the specified thread. /// /// A handle to the thread to query. /// A structure containing thread information. public static unsafe ThreadBasicInformation NtQueryInformationThread(SafeMemoryHandle threadHandle) { // Check if the handle is valid HandleManipulator.ValidateAsArgument(threadHandle, "threadHandle"); // Create a structure to store thread info var info = new ThreadBasicInformation(); // Get the thread info void* infoPtr = &info; // info is already fixed var ret = NativeMethods.NtQueryInformationThread(threadHandle, ThreadInformationClass.ThreadBasicInformation, infoPtr, MarshalType.SizeAsPointer, out var returnLength); // If the function succeeded if (ret == 0) { return info; } // Else, couldn't get the thread info, throws an exception throw new ApplicationException($"The thread information cannot be queried; error code '{ret}'."); } /// /// Retrieves information about the specified thread. /// /// A handle to the thread to query. /// The class of the thread to retrieve. /// The requested data as an unsigned integer. public static unsafe ulong NtQueryInformationThread(SafeMemoryHandle threadHandle, ThreadInformationClass threadInformationClass) { // Check if the handle is valid HandleManipulator.ValidateAsArgument(threadHandle, "threadHandle"); // Get the thread info ulong info = 0; var ret = NativeMethods.NtQueryInformationThread(threadHandle, ThreadInformationClass.ThreadBasicInformation, &info, new IntPtr(sizeof(ulong)), out var returnLength); // If the function succeeded if (ret == 0) { return info; } // Else, couldn't get the thread info, throws an exception throw new ApplicationException($"The thread information cannot be queried; error code '{ret}'."); } #endregion #region ResumeThread /// /// Decrements a thread's suspend count. When the suspend count is decremented to zero, the execution of the thread is resumed. /// /// A handle to the thread to be restarted. /// The thread's previous suspend count. public static uint ResumeThread(SafeMemoryHandle threadHandle) { // Check if the handle is valid HandleManipulator.ValidateAsArgument(threadHandle, "threadHandle"); // Resume the thread var ret = NativeMethods.ResumeThread(threadHandle); // If the function failed if (ret == uint.MaxValue) throw new Win32Exception("Couldn't resume the thread."); return ret; } #endregion #region SetThreadContext /// /// Sets the context for the specified thread. /// /// The type of the context to set. /// The type must be unmanaged, so it can be fixed while the native call is done. /// The performance is increased if the structure is blittable, which is the case for the structures /// provided with the library. /// A handle to the thread whose context is to be set. /// A pointer to a structure that contains the context to be set in the specified thread. /// The context cannot be set to the thread. public static unsafe void SetThreadContext(SafeMemoryHandle threadHandle, ref TContext context) where TContext : unmanaged { // Check if the handle is valid HandleManipulator.ValidateAsArgument(threadHandle, "threadHandle"); // Get the pointer of the structure and pin it, so the GC does not move it fixed (void* contextPtr = &context) { // Set the thread context if (NativeMethods.SetThreadContext(threadHandle, contextPtr) == 0) { throw new Win32Exception("The context cannot be set to the thread."); } } } #endregion #region SuspendThread /// /// Suspends the specified thread. /// /// A handle to the thread that is to be suspended. /// The thread's previous suspend count. public static uint SuspendThread(SafeMemoryHandle threadHandle) { // Check if the handle is valid HandleManipulator.ValidateAsArgument(threadHandle, "threadHandle"); // Suspend the thread var ret = NativeMethods.SuspendThread(threadHandle); // If the function failed if (ret == uint.MaxValue) throw new Win32Exception("Couldn't suspend the thread."); return ret; } #endregion #region TerminateThread /// /// Terminates a thread. /// /// A handle to the thread to be terminated. /// The exit code for the thread. public static void TerminateThread(SafeMemoryHandle threadHandle, int exitCode) { // Check if the handle is valid HandleManipulator.ValidateAsArgument(threadHandle, "threadHandle"); // Terminate the thread var ret = NativeMethods.TerminateThread(threadHandle, exitCode); // If the function failed if(!ret) throw new Win32Exception("Couldn't terminate the thread."); } #endregion #region ThreadContextFlags /// /// Determines which registers are returned or set when using or . /// [Flags] public enum ThreadContextFlags : uint { /// /// The Intel 80386 microprocessor, also known as the i386. /// Intel386 = 0x10000, /// /// The Intel 80486 microprocessor, also known as the i486. /// Intel486 = 0x10000, /// /// SS:SP, CS:IP, FLAGS, BP /// Control = Intel386 | 0x01, /// /// AX, BX, CX, DX, SI, DI /// Integer = Intel386 | 0x02, /// /// DS, ES, FS, GS /// Segments = Intel386 | 0x04, /// /// 387 state /// FloatingPoint = Intel386 | 0x08, /// /// DB 0-3,6,7 /// DebugRegisters = Intel386 | 0x10, /// /// CPU specific extensions /// ExtendedRegisters = Intel386 | 0x20, /// /// All flags excepted FloatingPoint, DebugRegisters and ExtendedRegisters. /// Full = Control | Integer | Segments, /// /// All flags. /// All = Control | Integer | Segments | FloatingPoint | DebugRegisters | ExtendedRegisters } #endregion #region ThreadInformationClass /// ///The numeration that corresponds to the classes of thread information. /// public enum ThreadInformationClass : uint { ThreadBasicInformation = 0, ThreadTimes = 1, ThreadPriority = 2, ThreadBasePriority = 3, ThreadAffinityMask = 4, ThreadImpersonationToken = 5, ThreadDescriptorTableEntry = 6, ThreadEnableAlignmentFaultFixup = 7, ThreadEventPair_Reusable = 8, ThreadQuerySetWin32StartAddress = 9, ThreadZeroTlsCell = 10, ThreadPerformanceCount = 11, ThreadAmILastThread = 12, ThreadIdealProcessor = 13, ThreadPriorityBoost = 14, ThreadSetTlsArrayAddress = 15, ThreadIsIoPending = 16, ThreadHideFromDebugger = 17, ThreadBreakOnTermination = 18, ThreadSwitchLegacyState = 19, ThreadIsTerminated = 20, ThreadLastSystemCall = 21, ThreadIoPriority = 22, ThreadCycleTime = 23, ThreadPagePriority = 24, ThreadActualBasePriority = 25, ThreadTebInformation = 26, ThreadCSwitchMon = 27, ThreadCSwitchPmu = 28, ThreadWow64Context = 29, ThreadGroupInformation = 30, ThreadUmsInformation = 31, ThreadCounterProfiling = 32, ThreadIdealProcessorEx = 33, ThreadCpuAccountingInformation = 34, ThreadSuspendCount = 35, ThreadHeterogeneousCpuPolicy = 36, ThreadContainerId = 37, ThreadNameInformation = 38, ThreadSelectedCpuSets = 39, ThreadSystemThreadInformation = 40, ThreadActualGroupAffinity = 41, ThreadDynamicCodePolicyInfo = 42, ThreadExplicitCaseSensitivity = 43, ThreadWorkOnBehalfTicket = 44, ThreadSubsystemInformation = 45, ThreadDbgkWerReportActive = 46, ThreadAttachContainer = 47, ThreadManageWritesToExecutableMemory = 48, ThreadPowerThrottlingState = 49, ThreadWorkloadClass = 50, } #endregion #region ProcessBasicInformation /// /// Structure containing basic information about a process. /// [StructLayout(LayoutKind.Sequential)] public struct ProcessBasicInformation { /// /// The exit status. /// public IntPtr ExitStatus; /// /// The base address of Process Environment Block. /// public IntPtr PebBaseAddress; /// /// The affinity mask. /// public IntPtr AffinityMask; /// /// The base priority. /// public IntPtr BasePriority; /// /// The process id. /// public IntPtr ProcessId; /// /// The process id of the parent process. /// public IntPtr InheritedFromUniqueProcessId; } #endregion ProcessBasicInformation #region ThreadBasicInformation /// /// Structure containing basic information about a thread. /// [StructLayout(LayoutKind.Sequential)] public struct ThreadBasicInformation { /// /// the exit status (NTSTATUS). /// public uint ExitStatus; /// /// The base address of Thread Environment Block. /// public IntPtr TebBaseAddress; /// /// The process and thread identifiers. /// public ClientIdStruct ClientIdStruct; /// /// The affinity mask. /// public IntPtr AffinityMask; /// /// The priority. /// public int Priority; /// /// The base priority. /// public int BasePriority; } #endregion ThreadBasicInformation #region ThreadContext32 /// /// Represents a thread context for 32-bit processes on Windows. Create a new instance using the constructor with the /// flags. /// /// /// This structure is blittable and therefore, does not require any marshaling from Platform Invoke. /// Referenced on https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_wow64_context. /// Refer to the header file WinNT.h for definitions of this structure for each processor architecture. /// [StructLayout(LayoutKind.Explicit, Size = ThreadContext32Metadata.TotalSize)] public unsafe struct ThreadContext32 { /// /// Initializes a new instance of the struct. /// /// Determines which registers are returned or set during the context query. public ThreadContext32(ThreadContextFlags flags) : this() { ContextFlags = flags; } /// /// Determines which registers are returned or set when using or /// . /// If the context record is used as an INPUT parameter, then for each portion of the context record controlled by a flag /// whose value is set, it is assumed that portion of the /// context record contains valid context. If the context record is being used to modify a threads context, then only that /// portion of the threads context will be modified. /// If the context record is used as an INPUT/OUTPUT parameter to capture the context of a thread, then only those portions /// of the thread's context corresponding to set flags will be returned. /// The context record is never used as an OUTPUT only parameter. /// [FieldOffset(ThreadContext32Metadata.Offsets.ContextFlags)] public ThreadContextFlags ContextFlags; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Dr0)] public uint Dr0; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Dr1)] public uint Dr1; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Dr2)] public uint Dr2; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Dr3)] public uint Dr3; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Dr6)] public uint Dr6; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Dr7)] public uint Dr7; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.ControlWord)] public uint ControlWord; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.StatusWord)] public uint StatusWord; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.TagWord)] public uint TagWord; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.ErrorOffset)] public uint ErrorOffset; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.ErrorSelector)] public uint ErrorSelector; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.DataOffset)] public uint DataOffset; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.DataSelector)] public uint DataSelector; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.RegisterArea)] public fixed byte RegisterArea[ThreadContext32Metadata.Sizes.RegisterAreaSize]; /// /// This is specified/returned if contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Cr0NpxState)] public uint Cr0NpxState; /// /// This is specified/returned if contains the flag . /// [FieldOffset(ThreadContext32Metadata.Offsets.SegGs)] public uint SegGs; /// /// This is specified/returned if contains the flag . /// [FieldOffset(ThreadContext32Metadata.Offsets.SegFs)] public uint SegFs; /// /// This is specified/returned if contains the flag . /// [FieldOffset(ThreadContext32Metadata.Offsets.SegEs)] public uint SegEs; /// /// This is specified/returned if contains the flag . /// [FieldOffset(ThreadContext32Metadata.Offsets.SegDs)] public uint SegDs; /// /// This register is specified/returned if the ContextFlags word contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Edi)] public uint Edi; /// /// This register is specified/returned if the ContextFlags word contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Esi)] public uint Esi; /// /// This register is specified/returned if the ContextFlags word contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Ebx)] public uint Ebx; /// /// This register is specified/returned if the ContextFlags word contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Edx)] public uint Edx; /// /// This register is specified/returned if the ContextFlags word contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Ecx)] public uint Ecx; /// /// This register is specified/returned if the ContextFlags word contains the flag /// . /// [FieldOffset(ThreadContext32Metadata.Offsets.Eax)] public uint Eax; /// /// This is specified/returned if contains the flag . /// [FieldOffset(ThreadContext32Metadata.Offsets.Ebp)] public uint Ebp; /// /// This is specified/returned if contains the flag . /// [FieldOffset(ThreadContext32Metadata.Offsets.Eip)] public uint Eip; /// /// This is specified/returned if contains the flag . /// [FieldOffset(ThreadContext32Metadata.Offsets.SegCs)] public uint SegCs; /// /// This is specified/returned if contains the flag . /// [FieldOffset(ThreadContext32Metadata.Offsets.EFlags)] public uint EFlags; /// /// This is specified/returned if contains the flag . /// [FieldOffset(ThreadContext32Metadata.Offsets.Esp)] public uint Esp; /// /// This is specified/returned if contains the flag . /// [FieldOffset(ThreadContext32Metadata.Offsets.SegSs)] public uint SegSs; /// /// This is specified/returned if contains the flag /// . /// The format and contexts are processor specific. /// [FieldOffset(ThreadContext32Metadata.Offsets.ExtendedRegisters)] public fixed byte ExtendedRegisters[ThreadContext32Metadata.Sizes.ExtendedRegistersSize]; } #endregion ThreadContext32 #region ThreadContext64 /// /// Represents a thread context for 64-bit processes on Windows. Create a new instance using the constructor with the /// flags. /// /// /// This structure is blittable and therefore, does not require any marshaling from Platform Invoke. /// Referenced on https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_context. /// Refer to the header file WinNT.h for definitions of this structure for each processor architecture. /// [StructLayout(LayoutKind.Explicit, Size = ThreadContext64Metadata.TotalSize)] public unsafe struct ThreadContext64 { /// /// Initializes a new instance of the struct. /// /// Determines which registers are returned or set during the context query. public ThreadContext64(ThreadContextFlags flags) : this() { ContextFlags = flags; } [FieldOffset(ThreadContext64Metadata.Offsets.P1Home)] public ulong P1Home; [FieldOffset(ThreadContext64Metadata.Offsets.P2Home)] public ulong P2Home; [FieldOffset(ThreadContext64Metadata.Offsets.P3Home)] public ulong P3Home; [FieldOffset(ThreadContext64Metadata.Offsets.P4Home)] public ulong P4Home; [FieldOffset(ThreadContext64Metadata.Offsets.P5Home)] public ulong P5Home; [FieldOffset(ThreadContext64Metadata.Offsets.P6Home)] public ulong P6Home; /// /// Determines which registers are returned or set when using or /// . /// If the context record is used as an INPUT parameter, then for each portion of the context record controlled by a flag /// whose value is set, it is assumed that portion of the /// context record contains valid context. If the context record is being used to modify a threads context, then only that /// portion of the threads context will be modified. /// If the context record is used as an INPUT/OUTPUT parameter to capture the context of a thread, then only those portions /// of the thread's context corresponding to set flags will be returned. /// The context record is never used as an OUTPUT only parameter. /// [FieldOffset(ThreadContext64Metadata.Offsets.ContextFlags)] public ThreadContextFlags ContextFlags; [FieldOffset(ThreadContext64Metadata.Offsets.MxCsr)] public uint MxCsr; [FieldOffset(ThreadContext64Metadata.Offsets.SegCs)] public ushort SegCs; [FieldOffset(ThreadContext64Metadata.Offsets.SegDs)] public ushort SegDs; [FieldOffset(ThreadContext64Metadata.Offsets.SegEs)] public ushort SegEs; [FieldOffset(ThreadContext64Metadata.Offsets.SegFs)] public ushort SegFs; [FieldOffset(ThreadContext64Metadata.Offsets.SegGs)] public ushort SegGs; [FieldOffset(ThreadContext64Metadata.Offsets.SegSs)] public ushort SegSs; [FieldOffset(ThreadContext64Metadata.Offsets.EFlags)] public uint EFlags; [FieldOffset(ThreadContext64Metadata.Offsets.Dr0)] public ulong Dr0; [FieldOffset(ThreadContext64Metadata.Offsets.Dr1)] public ulong Dr1; [FieldOffset(ThreadContext64Metadata.Offsets.Dr2)] public ulong Dr2; [FieldOffset(ThreadContext64Metadata.Offsets.Dr3)] public ulong Dr3; [FieldOffset(ThreadContext64Metadata.Offsets.Dr6)] public ulong Dr6; [FieldOffset(ThreadContext64Metadata.Offsets.Dr7)] public ulong Dr7; [FieldOffset(ThreadContext64Metadata.Offsets.Rax)] public ulong Rax; [FieldOffset(ThreadContext64Metadata.Offsets.Rcx)] public ulong Rcx; [FieldOffset(ThreadContext64Metadata.Offsets.Rdx)] public ulong Rdx; [FieldOffset(ThreadContext64Metadata.Offsets.Rbx)] public ulong Rbx; [FieldOffset(ThreadContext64Metadata.Offsets.Rsp)] public ulong Rsp; [FieldOffset(ThreadContext64Metadata.Offsets.Rbp)] public ulong Rbp; [FieldOffset(ThreadContext64Metadata.Offsets.Rsi)] public ulong Rsi; [FieldOffset(ThreadContext64Metadata.Offsets.Rdi)] public ulong Rdi; [FieldOffset(ThreadContext64Metadata.Offsets.R8)] public ulong R8; [FieldOffset(ThreadContext64Metadata.Offsets.R9)] public ulong R9; [FieldOffset(ThreadContext64Metadata.Offsets.R10)] public ulong R10; [FieldOffset(ThreadContext64Metadata.Offsets.R11)] public ulong R11; [FieldOffset(ThreadContext64Metadata.Offsets.R12)] public ulong R12; [FieldOffset(ThreadContext64Metadata.Offsets.R13)] public ulong R13; [FieldOffset(ThreadContext64Metadata.Offsets.R14)] public ulong R14; [FieldOffset(ThreadContext64Metadata.Offsets.R15)] public ulong R15; [FieldOffset(ThreadContext64Metadata.Offsets.Rip)] public ulong Rip; [FieldOffset(ThreadContext64Metadata.Offsets.Header)] public fixed ulong Header[ThreadContext64Metadata.Sizes.Header]; [FieldOffset(ThreadContext64Metadata.Offsets.Legacy)] public fixed ulong Legacy[ThreadContext64Metadata.Sizes.Legacy]; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm0)] public M128A Xmm0; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm1)] public M128A Xmm1; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm2)] public M128A Xmm2; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm3)] public M128A Xmm3; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm4)] public M128A Xmm4; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm5)] public M128A Xmm5; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm6)] public M128A Xmm6; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm7)] public M128A Xmm7; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm8)] public M128A Xmm8; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm9)] public M128A Xmm9; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm10)] public M128A Xmm10; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm11)] public M128A Xmm11; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm12)] public M128A Xmm12; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm13)] public M128A Xmm13; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm14)] public M128A Xmm14; [FieldOffset(ThreadContext64Metadata.Offsets.Xmm15)] public M128A Xmm15; [FieldOffset(ThreadContext64Metadata.Offsets.VectorRegister)] public fixed ulong VectorRegister[ThreadContext64Metadata.Sizes.VectorRegister]; [FieldOffset(ThreadContext64Metadata.Offsets.VectorControl)] public ulong VectorControl; [FieldOffset(ThreadContext64Metadata.Offsets.DebugControl)] public ulong DebugControl; [FieldOffset(ThreadContext64Metadata.Offsets.LastBranchToRip)] public ulong LastBranchToRip; [FieldOffset(ThreadContext64Metadata.Offsets.LastBranchFromRip)] public ulong LastBranchFromRip; [FieldOffset(ThreadContext64Metadata.Offsets.LastExceptionToRip)] public ulong LastExceptionToRip; [FieldOffset(ThreadContext64Metadata.Offsets.LastExceptionFromRip)] public ulong LastExceptionFromRip; } #endregion ThreadContext64 } /// /// Represents a Win32 handle safely managed. /// [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)] public sealed class SafeMemoryHandle : SafeHandleZeroOrMinusOneIsInvalid { /// /// Parameterless constructor for handles built by the system (like ). /// public SafeMemoryHandle() : base(true) { } /// /// Initializes a new instance of the class, specifying the handle to keep in safe. /// /// The handle to keep in safe. public SafeMemoryHandle(IntPtr handle) : base(true) { SetHandle(handle); } /// /// Executes the code required to free the handle. /// /// True if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it generates a releaseHandleFailed MDA Managed Debugging Assistant. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] protected override bool ReleaseHandle() { // Check whether the handle is set AND whether the handle has been successfully closed return handle != IntPtr.Zero && NativeMethods.CloseHandle(handle); } }