1.     public static class ThreadUtil
  2.     {
  3.         /// <summary>
  4.         /// Yields back a series of int32 values pulled from [basePtr - (addressesToReadBefore * 4)] to [basePtr + (addressesToReadAfter * 4)]
  5.         /// </summary>
  6.         /// <param name="hProc"></param>
  7.         /// <param name="basePtr"></param>
  8.         /// <param name="addressesToReadBefore"></param>
  9.         /// <param name="addressesToReadAfter"></param>
  10.         /// <returns></returns>
  11.         public static IEnumerable<Tuple<long, int>> DumpStack32(IntPtr hProc, UIntPtr basePtr, int addressesToReadBefore, int addressesToReadAfter)
  12.         {
  13.             var bp = basePtr.ToUInt32();
  14.             var bufferSizeForAddresses = Marshal.SizeOf(typeof(int)) * addressesToReadBefore;
  15.             var startAddress = bp - bufferSizeForAddresses;
  16.             var bufferSize = bufferSizeForAddresses + (addressesToReadAfter * Marshal.SizeOf(typeof(int)));
  17.             var buffer = new byte[bufferSize];
  18.             int bytesRead;
  19.             ReadProcessMemory(hProc, new IntPtr(startAddress), buffer, bufferSize, out bytesRead);
  20.             using (var br = new BinaryReader(new MemoryStream(buffer)))
  21.             {
  22.                 for (int i = 0; i < (addressesToReadBefore + addressesToReadAfter); i++)
  23.                 {
  24.                     var offset = (i * 4);
  25.                     var addr = br.ReadInt32();
  26.                     yield return Tuple.Create(startAddress + offset, addr);
  27.                 }
  28.             }
  29.         }
  30.  
  31.         public static string WalkFrom(IntPtr hProc, string name, UIntPtr address, int goBack, int goFwd)
  32.         {
  33.             var sb = new StringBuilder();
  34.             var addressRaw = address.ToUInt32();
  35.             sb.AppendFormat("\t{0}:0x{1}", name, addressRaw.ToString("X")).AppendLine();
  36.             var retAddrs = DumpStack32(hProc, address, goBack, goFwd);
  37.             foreach (var retAddr in retAddrs)
  38.             {
  39.                 var memLoc = retAddr.Item1;
  40.                 var offset = memLoc - addressRaw;
  41.                 var addr = retAddr.Item2;
  42.                 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();
  43.             }
  44.             return sb.ToString();
  45.         }
  46.  
  47.         public static string DumpThreadInfo(IntPtr hProc, IntPtr hThread, int framesBefore, int framesAfter)
  48.         {
  49.             var sb = new StringBuilder();
  50.             var ctx = new CONTEXT { ContextFlags = (uint)CONTEXT_FLAGS.CONTEXT_ALL };
  51.             GetThreadContext(hThread, ref ctx);
  52.             sb.AppendFormat("hProc:0x{0} hThread:0x{1}", hProc.ToInt32().ToString("X"), hThread.ToInt32().ToString("X")).AppendLine();
  53.             sb.AppendFormat("IP:0x{0}", ctx.Eip).AppendLine();
  54.             sb.AppendFormat(WalkFrom(hProc, "EBP", new UIntPtr(ctx.Ebp), framesBefore, framesAfter)).AppendLine();
  55.             sb.AppendFormat(WalkFrom(hProc, "ESP", new UIntPtr(ctx.Esp), framesBefore, framesAfter)).AppendLine();
  56.             return sb.ToString();
  57.         }
  58.  
  59.         #region P/Invoke Gobbledegook!
  60.         #region enums
  61.         [Flags]
  62.         public enum ThreadAccess : int
  63.         {
  64.             TERMINATE = (0x0001),
  65.             SUSPEND_RESUME = (0x0002),
  66.             GET_CONTEXT = (0x0008),
  67.             SET_CONTEXT = (0x0010),
  68.             SET_INFORMATION = (0x0020),
  69.             QUERY_INFORMATION = (0x0040),
  70.             SET_THREAD_TOKEN = (0x0080),
  71.             IMPERSONATE = (0x0100),
  72.             DIRECT_IMPERSONATION = (0x0200)
  73.         }
  74.         public enum CONTEXT_FLAGS : uint
  75.         {
  76.             CONTEXT_i386 = 0x10000,
  77.             CONTEXT_i486 = 0x10000,   //  same as i386
  78.             CONTEXT_CONTROL = CONTEXT_i386 | 0x01, // SS:SP, CS:IP, FLAGS, BP
  79.             CONTEXT_INTEGER = CONTEXT_i386 | 0x02, // AX, BX, CX, DX, SI, DI
  80.             CONTEXT_SEGMENTS = CONTEXT_i386 | 0x04, // DS, ES, FS, GS
  81.             CONTEXT_FLOATING_POINT = CONTEXT_i386 | 0x08, // 387 state
  82.             CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 | 0x10, // DB 0-3,6,7
  83.             CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x20, // cpu specific extensions
  84.             CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS,
  85.             CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS
  86.         }
  87.  
  88.         public enum ProcessAccessFlags : int
  89.         {
  90.             All = 0x001F0FFF,
  91.             Terminate = 0x00000001,
  92.             CreateThread = 0x00000002,
  93.             VMOperation = 0x00000008,
  94.             VMRead = 0x00000010,
  95.             VMWrite = 0x00000020,
  96.             DupHandle = 0x00000040,
  97.             SetInformation = 0x00000200,
  98.             QueryInformation = 0x00000400,
  99.             Synchronize = 0x00100000
  100.         }
  101.         #endregion
  102.  
  103.         #region structs
  104.         [StructLayout(LayoutKind.Sequential)]
  105.         public struct FLOATING_SAVE_AREA
  106.         {
  107.             public uint ControlWord;
  108.             public uint StatusWord;
  109.             public uint TagWord;
  110.             public uint ErrorOffset;
  111.             public uint ErrorSelector;
  112.             public uint DataOffset;
  113.             public uint DataSelector;
  114.             [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)]
  115.             public byte[] RegisterArea;
  116.             public uint Cr0NpxState;
  117.         }
  118.  
  119.         [StructLayout(LayoutKind.Sequential)]
  120.         public struct CONTEXT
  121.         {
  122.             public uint ContextFlags; //set this to an appropriate value
  123.             // Retrieved by CONTEXT_DEBUG_REGISTERS
  124.             public uint Dr0;
  125.             public uint Dr1;
  126.             public uint Dr2;
  127.             public uint Dr3;
  128.             public uint Dr6;
  129.             public uint Dr7;
  130.             // Retrieved by CONTEXT_FLOATING_POINT
  131.             public FLOATING_SAVE_AREA FloatSave;
  132.             // Retrieved by CONTEXT_SEGMENTS
  133.             public uint SegGs;
  134.             public uint SegFs;
  135.             public uint SegEs;
  136.             public uint SegDs;
  137.             // Retrieved by CONTEXT_INTEGER
  138.             public uint Edi;
  139.             public uint Esi;
  140.             public uint Ebx;
  141.             public uint Edx;
  142.             public uint Ecx;
  143.             public uint Eax;
  144.             // Retrieved by CONTEXT_CONTROL
  145.             public uint Ebp;
  146.             public uint Eip;
  147.             public uint SegCs;
  148.             public uint EFlags;
  149.             public uint Esp;
  150.             public uint SegSs;
  151.             // Retrieved by CONTEXT_EXTENDED_REGISTERS
  152.             [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
  153.             public byte[] ExtendedRegisters;
  154.         }
  155.  
  156.         public struct BYTES
  157.         {
  158.             public byte BaseMid;
  159.             public byte Flags1;
  160.             public byte Flags2;
  161.             public byte BaseHi;
  162.         }
  163.  
  164.         public struct BITS
  165.         {
  166.             int Value;
  167.             /// <summary>
  168.             /// Max set value is 255 (11111111b)
  169.             /// </summary>
  170.             public int BaseMid
  171.             {
  172.                 get { return (Value & 0xFF); }
  173.                 set { Value = (Value & unchecked((int)0xFFFFFF00)) | (value & 0xFF); }
  174.             }
  175.             /// <summary>
  176.             /// Max set value is 31 (11111b)
  177.             /// </summary>
  178.             public int Type
  179.             {
  180.                 get { return (Value & 0x1F00) >> 8; }
  181.                 set { Value = (Value & unchecked((int)0xFFFFE0FF)) | ((value & 0x1F) << 8); }
  182.             }
  183.             /// <summary>
  184.             /// Max set value is 3 (11b)
  185.             /// </summary>
  186.             public int Dpl
  187.             {
  188.                 get { return (Value & 0x6000) >> 13; }
  189.                 set { Value = (Value & unchecked((int)0xFFFF9FFF)) | ((value & 0x3) << 13); }
  190.             }
  191.             /// <summary>
  192.             /// Max set value is 1 (1b)
  193.             /// </summary>
  194.             public int Pres
  195.             {
  196.                 get { return (Value & 0x4000) >> 15; }
  197.                 set { Value = (Value & unchecked((int)0xFFFFBFFF)) | ((value & 0x1) << 15); }
  198.             }
  199.             /// <summary>
  200.             /// Max set value is 15 (1111b)
  201.             /// </summary>
  202.             public int LimitHi
  203.             {
  204.                 get { return (Value & 0xF0000) >> 16; }
  205.                 set { Value = (Value & unchecked((int)0xFFF0FFFF)) | ((value & 0xF) << 16); }
  206.             }
  207.             /// <summary>
  208.             /// Max set value is 1 (1b)
  209.             /// </summary>
  210.             public int Sys
  211.             {
  212.                 get { return (Value & 0x100000) >> 20; }
  213.                 set { Value = (Value & unchecked((int)0xFFEFFFFF)) | ((value & 0x1) << 20); }
  214.             }
  215.             /// <summary>
  216.             /// Max set value is 1 (1b)
  217.             /// </summary>
  218.             public int Reserved_0
  219.             {
  220.                 get { return (Value & 0x200000) >> 21; }
  221.                 set { Value = (Value & unchecked((int)0xFFDFFFFF)) | ((value & 0x1) << 21); }
  222.             }
  223.             /// <summary>
  224.             /// Max set value is 1 (1b)
  225.             /// </summary>
  226.             public int Default_Big
  227.             {
  228.                 get { return (Value & 0x400000) >> 22; }
  229.                 set { Value = (Value & unchecked((int)0xFFBFFFFF)) | ((value & 0x1) << 22); }
  230.             }
  231.             /// <summary>
  232.             /// Max set value is 1 (1b)
  233.             /// </summary>
  234.             public int Granularity
  235.             {
  236.                 get { return (Value & 0x800000) >> 23; }
  237.                 set { Value = (Value & unchecked((int)0xFF7FFFFF)) | ((value & 0x1) << 23); }
  238.             }
  239.             /// <summary>
  240.             /// Max set value is 255 (11111111b)
  241.             /// </summary>
  242.             public int BaseHi
  243.             {
  244.                 get { return (Value & unchecked((int)0xFF000000)) >> 24; }
  245.                 set { Value = (Value & unchecked((int)0xFFFFFF)) | ((value & 0xFF) << 24); }
  246.             }
  247.         }
  248.  
  249.         [StructLayout(LayoutKind.Explicit)]
  250.         public struct HIGHWORD
  251.         {
  252.             [FieldOffset(0)]
  253.             public BYTES Bytes;
  254.             [FieldOffset(0)]
  255.             public BITS Bits;
  256.         }
  257.  
  258.         public struct LDT_ENTRY
  259.         {
  260.             public ushort LimitLow;
  261.             public ushort BaseLow;
  262.             public HIGHWORD HighWord;
  263.         }
  264.  
  265.         [StructLayout(LayoutKind.Sequential)]
  266.         public struct MEMORY_BASIC_INFORMATION
  267.         {
  268.             public IntPtr BaseAddress;
  269.             public IntPtr AllocationBase;
  270.             public uint AllocationProtect;
  271.             public IntPtr RegionSize;
  272.             public uint State;
  273.             public uint Protect;
  274.             public uint Type;
  275.         }
  276.  
  277.         public enum AllocationProtect : uint
  278.         {
  279.             PAGE_EXECUTE = 0x00000010,
  280.             PAGE_EXECUTE_READ = 0x00000020,
  281.             PAGE_EXECUTE_READWRITE = 0x00000040,
  282.             PAGE_EXECUTE_WRITECOPY = 0x00000080,
  283.             PAGE_NOACCESS = 0x00000001,
  284.             PAGE_READONLY = 0x00000002,
  285.             PAGE_READWRITE = 0x00000004,
  286.             PAGE_WRITECOPY = 0x00000008,
  287.             PAGE_GUARD = 0x00000100,
  288.             PAGE_NOCACHE = 0x00000200,
  289.             PAGE_WRITECOMBINE = 0x00000400
  290.         }
  291.         #endregion
  292.  
  293.         #region general calls
  294.         [DllImport("kernel32.dll", SetLastError = true)]
  295.         [return: MarshalAs(UnmanagedType.Bool)]
  296.         public static extern bool CloseHandle(IntPtr hObject);
  297.         #endregion
  298.  
  299.         #region window calls
  300.         [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  301.         public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
  302.  
  303.         [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  304.         public static extern int GetWindowTextLength(IntPtr hWnd);
  305.         #endregion
  306.  
  307.         #region thread calls
  308.         [DllImport("kernel32.dll")]
  309.         public static extern bool TlsSetValue(uint dwTlsIndex, IntPtr lpTlsValue);
  310.  
  311.         [DllImport("kernel32.dll")]
  312.         public static extern IntPtr TlsGetValue(uint dwTlsIndex);
  313.  
  314.         [DllImport("kernel32.dll")]
  315.         public static extern uint SuspendThread(IntPtr hThread);
  316.  
  317.         [DllImport("kernel32.dll")]
  318.         public static extern uint ResumeThread(IntPtr hThread);
  319.  
  320.         [DllImport("kernel32.dll", SetLastError = true)]
  321.         public static extern bool GetThreadSelectorEntry(IntPtr hThread, uint dwSelector, out LDT_ENTRY lpSelectorEntry);
  322.  
  323.         [DllImport("kernel32.dll")]
  324.         public static extern bool GetThreadContext(IntPtr hThread, ref CONTEXT lpContext);
  325.  
  326.         [DllImport("kernel32.dll")]
  327.         public static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
  328.  
  329.         [DllImport("kernel32.dll")]
  330.         public static extern void ExitThread(uint dwExitCode);
  331.  
  332.         [DllImport("user32.dll")]
  333.         [return: MarshalAs(UnmanagedType.Bool)]
  334.         public static extern bool IsGUIThread([MarshalAs(UnmanagedType.Bool)] bool bConvert);
  335.  
  336.         [DllImport("kernel32.dll")]
  337.         public static extern uint GetCurrentThreadId();
  338.  
  339.         [DllImport("user32.dll")]
  340.         public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
  341.  
  342.         [DllImport("user32.dll")]
  343.         [return: MarshalAs(UnmanagedType.Bool)]
  344.         public static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
  345.  
  346.         public delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
  347.         public static void EnumThreadWindows(uint processId, Func<IntPtr, IntPtr, bool> callback)
  348.         {
  349.             EnumThreadWindows(processId, new EnumThreadDelegate(callback), IntPtr.Zero);
  350.         }
  351.         #endregion
  352.  
  353.         #region process and memory calls
  354.         [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
  355.         public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
  356.  
  357.         [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
  358.         public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint dwFreeType);
  359.  
  360.         [DllImport("kernel32.dll")]
  361.         public static extern int VirtualQueryEx(
  362.               IntPtr hProcess,
  363.               IntPtr lpAddress,
  364.                out MEMORY_BASIC_INFORMATION lpBuffer,
  365.                uint dwLength);
  366.  
  367.         [DllImport("kernel32.dll", SetLastError = true)]
  368.         public static extern bool ReadProcessMemory(
  369.               IntPtr hProcess,
  370.               IntPtr lpBaseAddress,
  371.               [Out] byte[] lpBuffer,
  372.                int dwSize,
  373.                out int lpNumberOfBytesRead);
  374.  
  375.         [DllImport("kernel32.dll", SetLastError = true)]
  376.         public static extern bool WriteProcessMemory(
  377.               IntPtr hProcess,
  378.               IntPtr lpBaseAddress,
  379.                byte[] lpBuffer,
  380.                uint nSize,
  381.                out UIntPtr lpNumberOfBytesWritten);
  382.  
  383.         [DllImport("kernel32.dll")]
  384.         public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
  385.         #endregion
  386.  
  387.         #endregion
  388.     }