public static class ThreadUtil { /// /// Yields back a series of int32 values pulled from [basePtr - (addressesToReadBefore * 4)] to [basePtr + (addressesToReadAfter * 4)] /// /// /// /// /// /// public static IEnumerable> DumpStack32(IntPtr hProc, UIntPtr basePtr, int addressesToReadBefore, int addressesToReadAfter) { var bp = basePtr.ToUInt32(); var bufferSizeForAddresses = Marshal.SizeOf(typeof(int)) * addressesToReadBefore; var startAddress = bp - bufferSizeForAddresses; var bufferSize = bufferSizeForAddresses + (addressesToReadAfter * Marshal.SizeOf(typeof(int))); var buffer = new byte[bufferSize]; int bytesRead; ReadProcessMemory(hProc, new IntPtr(startAddress), buffer, bufferSize, out bytesRead); using (var br = new BinaryReader(new MemoryStream(buffer))) { for (int i = 0; i < (addressesToReadBefore + addressesToReadAfter); i++) { var offset = (i * 4); var addr = br.ReadInt32(); yield return Tuple.Create(startAddress + offset, addr); } } } public static string WalkFrom(IntPtr hProc, string name, UIntPtr address, int goBack, int goFwd) { var sb = new StringBuilder(); var addressRaw = address.ToUInt32(); sb.AppendFormat("\t{0}:0x{1}", name, addressRaw.ToString("X")).AppendLine(); var retAddrs = DumpStack32(hProc, address, goBack, goFwd); foreach (var retAddr in retAddrs) { var memLoc = retAddr.Item1; var offset = memLoc - addressRaw; var addr = retAddr.Item2; sb.AppendFormat("\t\tAddr 0x{0}[{1}{2}0x{3}]:0x{4}", memLoc.ToString("X"), name, offset < 0 ? "-" : "+", Math.Abs(offset).ToString("X"), addr.ToString("X")).AppendLine(); } return sb.ToString(); } public static string DumpThreadInfo(IntPtr hProc, IntPtr hThread, int framesBefore, int framesAfter) { var sb = new StringBuilder(); var ctx = new CONTEXT { ContextFlags = (uint)CONTEXT_FLAGS.CONTEXT_ALL }; GetThreadContext(hThread, ref ctx); sb.AppendFormat("hProc:0x{0} hThread:0x{1}", hProc.ToInt32().ToString("X"), hThread.ToInt32().ToString("X")).AppendLine(); sb.AppendFormat("IP:0x{0}", ctx.Eip).AppendLine(); sb.AppendFormat(WalkFrom(hProc, "EBP", new UIntPtr(ctx.Ebp), framesBefore, framesAfter)).AppendLine(); sb.AppendFormat(WalkFrom(hProc, "ESP", new UIntPtr(ctx.Esp), framesBefore, framesAfter)).AppendLine(); return sb.ToString(); } #region P/Invoke Gobbledegook! #region enums [Flags] public enum ThreadAccess : int { TERMINATE = (0x0001), SUSPEND_RESUME = (0x0002), GET_CONTEXT = (0x0008), SET_CONTEXT = (0x0010), SET_INFORMATION = (0x0020), QUERY_INFORMATION = (0x0040), SET_THREAD_TOKEN = (0x0080), IMPERSONATE = (0x0100), DIRECT_IMPERSONATION = (0x0200) } public enum CONTEXT_FLAGS : uint { CONTEXT_i386 = 0x10000, CONTEXT_i486 = 0x10000, // same as i386 CONTEXT_CONTROL = CONTEXT_i386 | 0x01, // SS:SP, CS:IP, FLAGS, BP CONTEXT_INTEGER = CONTEXT_i386 | 0x02, // AX, BX, CX, DX, SI, DI CONTEXT_SEGMENTS = CONTEXT_i386 | 0x04, // DS, ES, FS, GS CONTEXT_FLOATING_POINT = CONTEXT_i386 | 0x08, // 387 state CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 | 0x10, // DB 0-3,6,7 CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x20, // cpu specific extensions CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS, CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS } public enum ProcessAccessFlags : int { All = 0x001F0FFF, Terminate = 0x00000001, CreateThread = 0x00000002, VMOperation = 0x00000008, VMRead = 0x00000010, VMWrite = 0x00000020, DupHandle = 0x00000040, SetInformation = 0x00000200, QueryInformation = 0x00000400, Synchronize = 0x00100000 } #endregion #region structs [StructLayout(LayoutKind.Sequential)] public struct FLOATING_SAVE_AREA { public uint ControlWord; public uint StatusWord; public uint TagWord; public uint ErrorOffset; public uint ErrorSelector; public uint DataOffset; public uint DataSelector; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)] public byte[] RegisterArea; public uint Cr0NpxState; } [StructLayout(LayoutKind.Sequential)] public struct CONTEXT { public uint ContextFlags; //set this to an appropriate value // Retrieved by CONTEXT_DEBUG_REGISTERS public uint Dr0; public uint Dr1; public uint Dr2; public uint Dr3; public uint Dr6; public uint Dr7; // Retrieved by CONTEXT_FLOATING_POINT public FLOATING_SAVE_AREA FloatSave; // Retrieved by CONTEXT_SEGMENTS public uint SegGs; public uint SegFs; public uint SegEs; public uint SegDs; // Retrieved by CONTEXT_INTEGER public uint Edi; public uint Esi; public uint Ebx; public uint Edx; public uint Ecx; public uint Eax; // Retrieved by CONTEXT_CONTROL public uint Ebp; public uint Eip; public uint SegCs; public uint EFlags; public uint Esp; public uint SegSs; // Retrieved by CONTEXT_EXTENDED_REGISTERS [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] public byte[] ExtendedRegisters; } public struct BYTES { public byte BaseMid; public byte Flags1; public byte Flags2; public byte BaseHi; } public struct BITS { int Value; /// /// Max set value is 255 (11111111b) /// public int BaseMid { get { return (Value & 0xFF); } set { Value = (Value & unchecked((int)0xFFFFFF00)) | (value & 0xFF); } } /// /// Max set value is 31 (11111b) /// public int Type { get { return (Value & 0x1F00) >> 8; } set { Value = (Value & unchecked((int)0xFFFFE0FF)) | ((value & 0x1F) << 8); } } /// /// Max set value is 3 (11b) /// public int Dpl { get { return (Value & 0x6000) >> 13; } set { Value = (Value & unchecked((int)0xFFFF9FFF)) | ((value & 0x3) << 13); } } /// /// Max set value is 1 (1b) /// public int Pres { get { return (Value & 0x4000) >> 15; } set { Value = (Value & unchecked((int)0xFFFFBFFF)) | ((value & 0x1) << 15); } } /// /// Max set value is 15 (1111b) /// public int LimitHi { get { return (Value & 0xF0000) >> 16; } set { Value = (Value & unchecked((int)0xFFF0FFFF)) | ((value & 0xF) << 16); } } /// /// Max set value is 1 (1b) /// public int Sys { get { return (Value & 0x100000) >> 20; } set { Value = (Value & unchecked((int)0xFFEFFFFF)) | ((value & 0x1) << 20); } } /// /// Max set value is 1 (1b) /// public int Reserved_0 { get { return (Value & 0x200000) >> 21; } set { Value = (Value & unchecked((int)0xFFDFFFFF)) | ((value & 0x1) << 21); } } /// /// Max set value is 1 (1b) /// public int Default_Big { get { return (Value & 0x400000) >> 22; } set { Value = (Value & unchecked((int)0xFFBFFFFF)) | ((value & 0x1) << 22); } } /// /// Max set value is 1 (1b) /// public int Granularity { get { return (Value & 0x800000) >> 23; } set { Value = (Value & unchecked((int)0xFF7FFFFF)) | ((value & 0x1) << 23); } } /// /// Max set value is 255 (11111111b) /// public int BaseHi { get { return (Value & unchecked((int)0xFF000000)) >> 24; } set { Value = (Value & unchecked((int)0xFFFFFF)) | ((value & 0xFF) << 24); } } } [StructLayout(LayoutKind.Explicit)] public struct HIGHWORD { [FieldOffset(0)] public BYTES Bytes; [FieldOffset(0)] public BITS Bits; } public struct LDT_ENTRY { public ushort LimitLow; public ushort BaseLow; public HIGHWORD HighWord; } [StructLayout(LayoutKind.Sequential)] public struct MEMORY_BASIC_INFORMATION { public IntPtr BaseAddress; public IntPtr AllocationBase; public uint AllocationProtect; public IntPtr RegionSize; public uint State; public uint Protect; public uint Type; } public enum AllocationProtect : uint { PAGE_EXECUTE = 0x00000010, PAGE_EXECUTE_READ = 0x00000020, PAGE_EXECUTE_READWRITE = 0x00000040, PAGE_EXECUTE_WRITECOPY = 0x00000080, PAGE_NOACCESS = 0x00000001, PAGE_READONLY = 0x00000002, PAGE_READWRITE = 0x00000004, PAGE_WRITECOPY = 0x00000008, PAGE_GUARD = 0x00000100, PAGE_NOCACHE = 0x00000200, PAGE_WRITECOMBINE = 0x00000400 } #endregion #region general calls [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CloseHandle(IntPtr hObject); #endregion #region window calls [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern int GetWindowTextLength(IntPtr hWnd); #endregion #region thread calls [DllImport("kernel32.dll")] public static extern bool TlsSetValue(uint dwTlsIndex, IntPtr lpTlsValue); [DllImport("kernel32.dll")] public static extern IntPtr TlsGetValue(uint dwTlsIndex); [DllImport("kernel32.dll")] public static extern uint SuspendThread(IntPtr hThread); [DllImport("kernel32.dll")] public static extern uint ResumeThread(IntPtr hThread); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool GetThreadSelectorEntry(IntPtr hThread, uint dwSelector, out LDT_ENTRY lpSelectorEntry); [DllImport("kernel32.dll")] public static extern bool GetThreadContext(IntPtr hThread, ref CONTEXT lpContext); [DllImport("kernel32.dll")] public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); [DllImport("kernel32.dll")] public static extern void ExitThread(uint dwExitCode); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool IsGUIThread([MarshalAs(UnmanagedType.Bool)] bool bConvert); [DllImport("kernel32.dll")] public static extern uint GetCurrentThreadId(); [DllImport("user32.dll")] public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam); public delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam); public static void EnumThreadWindows(uint processId, Func callback) { EnumThreadWindows(processId, new EnumThreadDelegate(callback), IntPtr.Zero); } #endregion #region process and memory calls [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint dwFreeType); [DllImport("kernel32.dll")] public static extern int VirtualQueryEx( IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, uint dwLength); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool ReadProcessMemory( IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool WriteProcessMemory( IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten); [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); #endregion #endregion }