/* * 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.Collections.Generic; using System.Diagnostics; using ProcessHacker.Common; using ProcessHacker.Native.Api; using ProcessHacker.Native.Security; namespace ProcessHacker.Native.Objects { /// /// Represents a handle to a Windows thread. /// public sealed class ThreadHandle : NativeHandle, IWithToken { public delegate bool WalkStackDelegate(ThreadStackFrame stackFrame); private static readonly ThreadHandle _current = new ThreadHandle(new IntPtr(-2), false); /// /// Gets a handle to the current thread. /// public static ThreadHandle Current { get { return _current; } } 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 ) { ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); IntPtr handle; try { Win32.NtCreateThread( out handle, access, ref oa, processHandle, out clientId, ref threadContext, ref initialTeb, createSuspended ).ThrowIf(); } finally { oa.Dispose(); } return new ThreadHandle(handle, true); } public static ThreadHandle CreateUserThread(ProcessHandle processHandle, IntPtr startAddress, IntPtr parameter) { return CreateUserThread(processHandle, false, startAddress, parameter); } public static ThreadHandle CreateUserThread( ProcessHandle processHandle, bool createSuspended, IntPtr startAddress, IntPtr parameter ) { ClientId clientId; return CreateUserThread(processHandle, createSuspended, 0, 0, startAddress, parameter, out clientId); } public static ThreadHandle CreateUserThread( ProcessHandle processHandle, bool createSuspended, int maximumStackSize, int initialStackSize, IntPtr startAddress, IntPtr parameter, out ClientId clientId ) { IntPtr threadHandle; Win32.RtlCreateUserThread( processHandle, IntPtr.Zero, createSuspended, 0, maximumStackSize.ToIntPtr(), initialStackSize.ToIntPtr(), startAddress, parameter, out threadHandle, out clientId ).ThrowIf(); return new ThreadHandle(threadHandle, 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 Current; } /// /// Gets the client ID of the current thread. /// /// A client ID. public static ClientId GetCurrentCid() { return new ClientId(ProcessHandle.CurrentId, GetCurrentId()); } /// /// Gets the ID of the current thread. /// /// A thread ID. public static int GetCurrentId() { return Win32.GetCurrentThreadId(); } /// /// Gets a pointer to the current thread's environment block. /// /// A pointer to the current TEB. public unsafe static Teb* GetCurrentTeb() { return (Teb*)Win32.NtCurrentTeb(); } /// /// Opens the current thread. /// /// The desired access to the thread. /// A handle to the current thread. public static ThreadHandle OpenCurrent(ThreadAccess access) { return new ThreadHandle(GetCurrentId(), access); } public static ThreadHandle OpenWithAnyAccess(int tid) { try { return new ThreadHandle(tid, OSVersion.MinThreadQueryInfoAccess); } catch { try { return new ThreadHandle(tid, (ThreadAccess)StandardRights.Synchronize); } catch { try { return new ThreadHandle(tid, (ThreadAccess)StandardRights.ReadControl); } catch { try { return new ThreadHandle(tid, (ThreadAccess)StandardRights.WriteDac); } catch { return new ThreadHandle(tid, (ThreadAccess)StandardRights.WriteOwner); } } } } } /// /// Registers a port which will be notified when the current thread terminates. /// /// A handle to a port. public static void RegisterTerminationPort(PortHandle portHandle) { Win32.NtRegisterThreadTerminatePort(portHandle).ThrowIf(); } /// /// Sleeps the current thread. /// /// The timeout, in 100ns units. /// Whether the timeout value is relative. /// A NT status value. public static NtStatus Sleep(long timeout, bool relative) { return Sleep(false, timeout, relative); } /// /// Sleeps the current thread. /// /// /// Whether user-mode APCs can be delivered during the wait. /// /// The timeout, in 100ns units. /// Whether the timeout value is relative. /// A NT status value. public static NtStatus Sleep(bool alertable, long timeout, bool relative) { if (timeout == 0) { Yield(); return NtStatus.Success; } long realTime = relative ? -timeout : timeout; return Win32.NtDelayExecution(alertable, ref realTime); } /// /// Checks whether the current thread is in an alerted state and /// executes any pending user-mode APCs. /// /// /// NtStatus.Alerted if the current thread was in an alerted state, /// otherwise NtStatus.Success. /// public static NtStatus TestAlert() { return Win32.NtTestAlert(); } /// /// Switches to another thread. /// public static void Yield() { Win32.NtYieldExecution(); } internal ThreadHandle(IntPtr handle, bool owned) : base(handle, owned) { } /// /// Opens a thread. /// /// The ID of the thread to open. public ThreadHandle(int tid) : this(tid, ThreadAccess.All) { } /// /// Opens a thread. /// /// 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) { this.MarkAsInvalid(); Win32.Throw(); } } public ThreadHandle( string name, ObjectFlags objectFlags, DirectoryHandle rootDirectory, ClientId clientId, ThreadAccess access ) { ObjectAttributes oa = new ObjectAttributes(name, objectFlags, rootDirectory); IntPtr handle; try { if (clientId.ProcessId == 0 && clientId.ThreadId == 0) { Win32.NtOpenThread( out handle, access, ref oa, IntPtr.Zero ).ThrowIf(); } else { Win32.NtOpenThread( out handle, access, ref oa, ref clientId ).ThrowIf(); } } 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() { Win32.NtAlertThread(this).ThrowIf(); } /// /// Resumes the thread in an alerted state. /// public int AlertResume() { int suspendCount; Win32.NtAlertResumeThread(this, out suspendCount).ThrowIf(); return suspendCount; } /// /// Captures a kernel-mode stack trace for the thread. /// /// An array of function addresses. public IntPtr[] CaptureKernelStack() { return this.CaptureKernelStack(0); } /// /// Captures a kernel-mode stack trace for the thread. /// /// The number of frames to skip. /// An array of function addresses. public IntPtr[] CaptureKernelStack(int skipCount) { //IntPtr[] stack = new IntPtr[62 - skipCount]; // 62 limit for XP and Server 2003 //int hash; // Capture a kernel-mode stack trace. //int captured = KProcessHacker.Instance.KphCaptureStackBackTraceThread( // this, // skipCount, // stack.Length, // stack, // out hash // ); // Create a new array with only the frames we captured. //IntPtr[] newStack = new IntPtr[captured]; //Array.Copy(stack, 0, newStack, 0, captured); return null;// newStack; } /// /// Captures a user-mode stack trace for the thread. /// /// An array of stack frames. public ThreadStackFrame[] CaptureUserStack() { return this.CaptureUserStack(0); } /// /// Captures a user-mode stack trace for the thread. /// /// The number of frames to skip. /// An array of stack frames. public ThreadStackFrame[] CaptureUserStack(int skipCount) { List frames = new List(); // Walk the stack. this.WalkStack(frame => { frames.Add(frame); return true; }); // If we want to skip frames than we have, just return an empty array. if (frames.Count <= skipCount) return new ThreadStackFrame[0]; // Otherwise, create a new array with the frames, minus what we skipped. ThreadStackFrame[] newFrames = new ThreadStackFrame[frames.Count - skipCount]; Array.Copy(frames.ToArray(), skipCount, newFrames, 0, newFrames.Length); return newFrames; } /// /// Attempts to terminate the thread using a dangerous method. This /// operation may cause the system to crash. /// /// The exit status. public void DangerousTerminate(NtStatus exitStatus) { //KProcessHacker.Instance.KphDangerousTerminateThread(this, exitStatus); } /// /// Gets the thread's base priority. /// public int GetBasePriority() { return this.GetInformationInt32(ThreadInformationClass.ThreadBasePriority); } /// /// Gets the thread's base priority. /// /// A ThreadPriorityLevel enum. public ThreadPriorityLevel GetBasePriorityWin32() { int priority = Win32.GetThreadPriority(this); if (priority == 0x7fffffff) Win32.Throw(); return (ThreadPriorityLevel)priority; } /// /// Gets the thread's basic information. /// /// A THREAD_BASIC_INFORMATION structure. public ThreadBasicInformation GetBasicInformation() { ThreadBasicInformation basicInfo = new ThreadBasicInformation(); int retLen; Win32.NtQueryInformationThread( this, ThreadInformationClass.ThreadBasicInformation, ref basicInfo, ThreadBasicInformation.SizeOf, out retLen ).ThrowIf(); return basicInfo; } /// /// Gets the thread's context. /// /// A CONTEXT struct. public Context GetContext(ContextFlags flags) { Context context = new 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) { Win32.NtGetContextThread(this, ref context).ThrowIf(); } /// /// Gets the thread's context. /// /// A CONTEXT struct. public ContextAmd64 GetContext(ContextFlagsAmd64 flags) { ContextAmd64 context = new ContextAmd64 { ContextFlags = flags }; this.GetContext(ref context); return context; } /// /// Gets the thread's context. /// /// A Context structure. The ContextFlags must be set appropriately. public void GetContext(ref ContextAmd64 context) { // HACK: To avoid a datatype misalignment error, allocate some // aligned memory. using (AlignedMemoryAlloc data = new AlignedMemoryAlloc(Utils.SizeOf(16, ContextAmd64.SizeOf), 16)) { data.WriteStruct(context); Win32.NtGetContextThread(this, data).ThrowIf(); context = data.ReadStruct(); } } /// /// Gets the thread's x86 context. The thread's process must be running /// under WOW64. /// /// A Context structure. The ContextFlags must be set appropriately. public void GetContextWow64(ref Context context) { Win32.RtlWow64GetThreadContext(this, ref context).ThrowIf(); } /// /// Gets the number of processor cycles consumed by the thread. /// public ulong GetCycleTime() { ulong cycles; if (!Win32.QueryThreadCycleTime(this, out cycles)) Win32.Throw(); return cycles; } /// /// Gets the thread's exit code. /// /// A number. public int GetExitCode() { int exitCode; if (!Win32.GetExitCodeThread(this, out exitCode)) Win32.Throw(); return exitCode; } /// /// Gets the thread's exit status. /// /// A NT status value. public NtStatus GetExitStatus() { return this.GetBasicInformation().ExitStatus; } private int GetInformationInt32(ThreadInformationClass infoClass) { int value; int retLength; Win32.NtQueryInformationThread( this, infoClass, out value, sizeof(int), out retLength ).ThrowIf(); return value; } private IntPtr GetInformationIntPtr(ThreadInformationClass infoClass) { IntPtr value; int retLength; Win32.NtQueryInformationThread( this, infoClass, out value, IntPtr.Size, out retLength ).ThrowIf(); 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) { int* data = stackalloc int[2]; int retLength; Win32.NtQueryInformationThread( this, ThreadInformationClass.ThreadLastSystemCall, data, sizeof(int) * 2, out retLength ).ThrowIf(); 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); } /// /// Opens the thread's process. /// /// A process handle. public ProcessHandle GetProcess(ProcessAccess access) { return new ProcessHandle(this, access); } /// /// Gets the thread's parent process' unique identifier. /// /// A process ID. public int GetProcessId() { return this.GetBasicInformation().ClientId.ProcessId; } /// /// Gets the thread's unique identifier. /// /// A thread ID. public int GetThreadId() { return this.GetBasicInformation().ClientId.ThreadId; } /// /// 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); } /// /// Gets the thread's Win32 start address. /// public IntPtr GetWin32StartAddress() { return this.GetInformationIntPtr(ThreadInformationClass.ThreadQuerySetWin32StartAddress); } /// /// Causes the thread to impersonate a client thread. /// /// A handle to a client thread. /// The impersonation level to request. public void Impersonate(ThreadHandle clientThreadHandle, SecurityImpersonationLevel impersonationLevel) { SecurityQualityOfService securityQos = new SecurityQualityOfService(impersonationLevel, false, false); Win32.NtImpersonateThread( this, clientThreadHandle, ref securityQos ).ThrowIf(); } /// /// Causes the thread to impersonate the anonymous account. /// public void ImpersonateAnonymous() { Win32.NtImpersonateAnonymousToken(this).ThrowIf(); } /// /// 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 THREAD_SET_CONTEXT access. /// /// 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.Throw(); } /// /// Adds an user-mode asynchronous procedure call (APC) to the thread's APC queue. /// This requires THREAD_SET_CONTEXT access. /// /// The delegate to execute.. /// The parameter to pass to the procedure. public void QueueApc(ApcRoutine action, IntPtr parameter) { if (!Win32.QueueUserAPC(action, this, parameter)) Win32.Throw(); } /// /// Queues a user-mode asynchronous procedure call (APC) to the thread. /// /// The address of the function to execute. /// The first parameter to pass to the function. /// The second parameter to pass to the function. /// The third parameter to pass to the function. public void QueueApc(IntPtr address, IntPtr param1, IntPtr param2, IntPtr param3) { Win32.NtQueueApcThread( this, address, param1, param2, param3 ).ThrowIf(); } public void RemoteCall(IntPtr address, IntPtr[] arguments) { this.RemoteCall(address, arguments, false); } public void RemoteCall(IntPtr address, IntPtr[] arguments, bool alreadySuspended) { ProcessHandle processHandle = new ProcessHandle(this.GetProcessId(), ProcessAccess.VmWrite); using (processHandle) this.RemoteCall(processHandle, address, arguments, alreadySuspended); } public void RemoteCall(ProcessHandle processHandle, IntPtr address, IntPtr[] arguments, bool alreadySuspended) { Win32.RtlRemoteCall( processHandle, this, address, arguments.Length, arguments, false, alreadySuspended ).ThrowIf(); } /// /// Resumes the thread. /// public int Resume() { int suspendCount; Win32.NtResumeThread(this, out suspendCount).ThrowIf(); return suspendCount; } /// /// Sets the thread's base priority. /// /// The thread's base priority. public void SetBasePriority(int basePriority) { this.SetInformationInt32(ThreadInformationClass.ThreadBasePriority, basePriority); } /// /// Sets the thread's base priority. /// /// The base priority of the thread. public void SetBasePriorityWin32(ThreadPriorityLevel basePriority) { if (!Win32.SetThreadPriority(this, (int)basePriority)) Win32.Throw(); } /// /// Sets the thread's context. /// /// A CONTEXT struct. public void SetContext(Context context) { Win32.NtSetContextThread(this, ref context).ThrowIf(); } /// /// Sets the thread's context. /// /// A CONTEXT struct. public void SetContext(ContextAmd64 context) { // HACK: To avoid a datatype misalignment error, allocate // some aligned memory. using (AlignedMemoryAlloc data = new AlignedMemoryAlloc(Utils.SizeOf(16, ContextAmd64.SizeOf), 16)) { data.WriteStruct(context); Win32.NtSetContextThread(this, data).ThrowIf(); } } /// /// Sets the thread's x86 context. The thread's process must /// be running under WOW64. /// /// A CONTEXT struct. public void SetContextWow64(Context context) { Win32.RtlWow64SetThreadContext(this, ref context).ThrowIf(); } /// /// Sets whether the thread is critical. /// /// Whether the thread should be critical. public void SetCritical(bool critical) { this.SetInformationInt32(ThreadInformationClass.ThreadBreakOnTermination, critical ? 1 : 0); } private void SetInformationInt32(ThreadInformationClass infoClass, int value) { Win32.NtSetInformationThread( this, infoClass, ref value, sizeof(int) ).ThrowIf(); } private void SetInformationIntPtr(ThreadInformationClass infoClass, IntPtr value) { Win32.NtSetInformationThread( this, infoClass, ref value, sizeof(int) ).ThrowIf(); } public void SetIoPriority(int ioPriority) { this.SetInformationInt32(ThreadInformationClass.ThreadIoPriority, ioPriority); } public void SetPagePriority(int pagePriority) { this.SetInformationInt32(ThreadInformationClass.ThreadPagePriority, pagePriority); } /// /// Sets the thread's priority. /// /// The thread's priority. public void SetPriority(int priority) { this.SetInformationInt32(ThreadInformationClass.ThreadPriority, priority); } /// /// Sets the thread's priority boost. /// /// Whether priority boost will be enabled. public void SetPriorityBoost(bool enabled) { this.SetInformationInt32(ThreadInformationClass.ThreadPriorityBoost, enabled ? 0 : 1); } /// /// Sets the thread's impersonation token. /// /// /// A handle to a token. Specify null to cause the thread to stop /// impersonating. /// public void SetToken(TokenHandle tokenHandle) { this.SetInformationIntPtr(ThreadInformationClass.ThreadImpersonationToken, tokenHandle ?? IntPtr.Zero); } /// /// Suspends the thread. /// public int Suspend() { int suspendCount; Win32.NtSuspendThread(this, out suspendCount).ThrowIf(); return suspendCount; } /// /// Terminates the thread. /// public void Terminate() { this.Terminate(NtStatus.Success); } /// /// Terminates the thread. /// /// The exit status. public void Terminate(NtStatus exitStatus) { Win32.NtTerminateThread(this, exitStatus).ThrowIf(); } /// /// Walks the call stack for the thread. /// /// A callback to execute. public void WalkStack(WalkStackDelegate walkStackCallback) { this.WalkStack(walkStackCallback, OSVersion.Architecture); } /// /// Walks the call stack for the thread. /// /// A callback to execute. /// /// The type of stack walk. On 32-bit systems, this value is ignored. /// On 64-bit systems, this value can be set to I386 to walk the /// 32-bit stack. /// public void WalkStack(WalkStackDelegate walkStackCallback, OSArch architecture) { // We need to duplicate the handle to get QueryInformation access. using (NativeHandle dupThreadHandle = this.Duplicate(OSVersion.MinThreadQueryInfoAccess)) using (ProcessHandle phandle = new ProcessHandle( FromHandle(dupThreadHandle).GetBasicInformation().ClientId.ProcessId, ProcessAccess.QueryInformation | ProcessAccess.VmRead )) { this.WalkStack(phandle, walkStackCallback, architecture); } } /// /// Walks the call stack for the thread. /// /// A handle to the thread's parent process. /// A callback to execute. public void WalkStack(ProcessHandle parentProcess, WalkStackDelegate walkStackCallback) { this.WalkStack(parentProcess, walkStackCallback, OSVersion.Architecture); } /// /// Walks the call stack for the thread. /// /// A handle to the thread's parent process. /// A callback to execute. /// /// The type of stack walk. On 32-bit systems, this value is ignored. /// On 64-bit systems, this value can be set to I386 to walk the /// 32-bit stack. /// public void WalkStack(ProcessHandle parentProcess, WalkStackDelegate walkStackCallback, OSArch architecture) { bool suspended; // Suspend the thread to avoid inaccurate thread stacks. try { this.Suspend(); suspended = true; } catch (WindowsException) { suspended = false; } // Use KPH for reading memory if we can. ReadProcessMemoryProc64 readMemoryProc = null; //if (KProcessHacker.Instance != null) //{ // readMemoryProc = // (IntPtr processHandle, ulong baseAddress, IntPtr buffer, int size, out int bytesRead) // => KProcessHacker.Instance.KphReadVirtualMemorySafe( // ProcessHandle.FromHandle(processHandle), (int)baseAddress, buffer, size, out bytesRead // ).IsSuccess(); //} try { // x86/WOW64 stack walk. if (OSVersion.Architecture == OSArch.I386 || (OSVersion.Architecture == OSArch.Amd64 && architecture == OSArch.I386)) { Context context = new Context { ContextFlags = ContextFlags.All }; if (OSVersion.Architecture == OSArch.I386) { // Get the context. this.GetContext(ref context); } else { // Get the WOW64 x86 context. this.GetContextWow64(ref context); } // Set up the initial stack frame structure. StackFrame64 stackFrame = new StackFrame64 { AddrPC = { Mode = AddressMode.AddrModeFlat, Offset = (ulong)context.Eip }, AddrStack = { Mode = AddressMode.AddrModeFlat, Offset = (ulong)context.Esp }, AddrFrame = { Mode = AddressMode.AddrModeFlat, Offset = (ulong)context.Ebp } }; while (true) { using (Win32.DbgHelpLock.AcquireContext()) { if (!Win32.StackWalk64( MachineType.I386, parentProcess, this, ref stackFrame, ref context, readMemoryProc, Win32.SymFunctionTableAccess64, Win32.SymGetModuleBase64, IntPtr.Zero )) break; } // If we got an invalid eip, break. if (stackFrame.AddrPC.Offset == 0) break; // Execute the callback. if (!walkStackCallback(new ThreadStackFrame(ref stackFrame))) break; } } // x64 stack walk. else if (OSVersion.Architecture == OSArch.Amd64) { ContextAmd64 context = new ContextAmd64 { ContextFlags = ContextFlagsAmd64.All }; // Get the context. this.GetContext(ref context); // Set up the initial stack frame structure. StackFrame64 stackFrame = new StackFrame64 { AddrPC = { Mode = AddressMode.AddrModeFlat, Offset = (ulong)context.Rip }, AddrStack = { Mode = AddressMode.AddrModeFlat, Offset = (ulong)context.Rsp }, AddrFrame = { Mode = AddressMode.AddrModeFlat, Offset = (ulong)context.Rbp } }; while (true) { using (Win32.DbgHelpLock.AcquireContext()) { if (!Win32.StackWalk64( MachineType.Amd64, parentProcess, this, ref stackFrame, ref context, readMemoryProc, Win32.SymFunctionTableAccess64, Win32.SymGetModuleBase64, IntPtr.Zero )) break; } // If we got an invalid rip, break. if (stackFrame.AddrPC.Offset == 0) break; // Execute the callback. if (!walkStackCallback(new ThreadStackFrame(ref stackFrame))) break; } } } finally { // If we suspended the thread before, resume it. if (suspended) { try { this.Resume(); } catch (WindowsException) { } } } } } public class ThreadStackFrame { private readonly IntPtr _pcAddress; private readonly IntPtr _returnAddress; private readonly IntPtr _frameAddress; private readonly IntPtr _stackAddress; private readonly IntPtr _bStoreAddress; private readonly IntPtr[] _params; internal ThreadStackFrame(ref StackFrame64 stackFrame) { _pcAddress = new IntPtr((long)stackFrame.AddrPC.Offset); _returnAddress = new IntPtr((long)stackFrame.AddrReturn.Offset); _frameAddress = new IntPtr((long)stackFrame.AddrFrame.Offset); _stackAddress = new IntPtr((long)stackFrame.AddrStack.Offset); _bStoreAddress = new IntPtr((long)stackFrame.AddrBStore.Offset); _params = new IntPtr[4]; for (int i = 0; i < 4; i++) _params[i] = new IntPtr(stackFrame.Params[i]); } public IntPtr PcAddress { get { return _pcAddress; } } public IntPtr ReturnAddress { get { return _returnAddress; } } public IntPtr FrameAddress { get { return _frameAddress; } } public IntPtr StackAddress { get { return _stackAddress; } } public IntPtr BStoreAddress { get { return _bStoreAddress; } } public IntPtr[] Params { get { return _params; } } } }