public static class ThreadUtil
{
/// <summary>
/// Yields back a series of int32 values pulled from [basePtr - (addressesToReadBefore * 4)] to [basePtr + (addressesToReadAfter * 4)]
/// </summary>
/// <param name="hProc"></param>
/// <param name="basePtr"></param>
/// <param name="addressesToReadBefore"></param>
/// <param name="addressesToReadAfter"></param>
/// <returns></returns>
public static IEnumerable<Tuple<long, int>> 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;
/// <summary>
/// Max set value is 255 (11111111b)
/// </summary>
public int BaseMid
{
get { return (Value & 0xFF); }
set { Value = (Value & unchecked((int)0xFFFFFF00)) | (value & 0xFF); }
}
/// <summary>
/// Max set value is 31 (11111b)
/// </summary>
public int Type
{
get { return (Value & 0x1F00) >> 8; }
set { Value = (Value & unchecked((int)0xFFFFE0FF)) | ((value & 0x1F) << 8); }
}
/// <summary>
/// Max set value is 3 (11b)
/// </summary>
public int Dpl
{
get { return (Value & 0x6000) >> 13; }
set { Value = (Value & unchecked((int)0xFFFF9FFF)) | ((value & 0x3) << 13); }
}
/// <summary>
/// Max set value is 1 (1b)
/// </summary>
public int Pres
{
get { return (Value & 0x4000) >> 15; }
set { Value = (Value & unchecked((int)0xFFFFBFFF)) | ((value & 0x1) << 15); }
}
/// <summary>
/// Max set value is 15 (1111b)
/// </summary>
public int LimitHi
{
get { return (Value & 0xF0000) >> 16; }
set { Value = (Value & unchecked((int)0xFFF0FFFF)) | ((value & 0xF) << 16); }
}
/// <summary>
/// Max set value is 1 (1b)
/// </summary>
public int Sys
{
get { return (Value & 0x100000) >> 20; }
set { Value = (Value & unchecked((int)0xFFEFFFFF)) | ((value & 0x1) << 20); }
}
/// <summary>
/// Max set value is 1 (1b)
/// </summary>
public int Reserved_0
{
get { return (Value & 0x200000) >> 21; }
set { Value = (Value & unchecked((int)0xFFDFFFFF)) | ((value & 0x1) << 21); }
}
/// <summary>
/// Max set value is 1 (1b)
/// </summary>
public int Default_Big
{
get { return (Value & 0x400000) >> 22; }
set { Value = (Value & unchecked((int)0xFFBFFFFF)) | ((value & 0x1) << 22); }
}
/// <summary>
/// Max set value is 1 (1b)
/// </summary>
public int Granularity
{
get { return (Value & 0x800000) >> 23; }
set { Value = (Value & unchecked((int)0xFF7FFFFF)) | ((value & 0x1) << 23); }
}
/// <summary>
/// Max set value is 255 (11111111b)
/// </summary>
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<IntPtr, IntPtr, bool> 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
}