Created
March 28, 2014 16:21
-
-
Save Konctantin/9836775 to your computer and use it in GitHub Desktop.
ProcessMemory
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Diagnostics; | |
using System.Linq; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
using System.Threading; | |
namespace NetCallerFunc | |
{ | |
public delegate void HWReakPointHandler(ProcessMemory memory, CONTEXT context); | |
public class ProcessMemory : IDisposable | |
{ | |
#region API | |
[DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)] | |
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, AllocationType flAllocationType, MemoryProtection flProtect); | |
[DllImport("kernel32", EntryPoint = "OpenThread", SetLastError = true)] | |
public static extern IntPtr OpenThread(ThreadAccess DesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwThreadId); | |
[DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)] | |
static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, FreeType dwFreeType); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, IntPtr nSize, IntPtr lpNumberOfBytesWritten); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead); | |
[DllImport("kernel32.dll")] | |
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); | |
[DllImport("kernel32", SetLastError = true)] | |
public static extern bool GetExitCodeThread(IntPtr thandle, out uint dwExitCode); | |
[DllImport("kernel32", SetLastError = true)] | |
public static extern uint SuspendThread(IntPtr thandle); | |
[DllImport("kernel32", SetLastError = true)] | |
public static extern uint ResumeThread(IntPtr thandle); | |
[DllImport("kernel32", SetLastError = true)] | |
public static extern uint TerminateThread(IntPtr thandle, uint dwExitCode); | |
[DllImport("kernel32", SetLastError=true)] | |
public static extern bool GetThreadContext(IntPtr thandle, ref CONTEXT context); | |
[DllImport("kernel32", SetLastError = true)] | |
public static extern bool SetThreadContext(IntPtr thandle, ref CONTEXT context); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
static extern bool DebugActiveProcess(int dwProcessId); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
static extern bool DebugActiveProcessStop(int dwProcessId); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
static extern bool DebugSetProcessKillOnExit(bool KillOnExit); | |
[DllImport("kernel32.dll", EntryPoint = "WaitForDebugEvent")] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
public static extern bool WaitForDebugEvent([In] ref DEBUG_EVENT lpDebugEvent, uint dwMilliseconds); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
static extern bool ContinueDebugEvent(int dwProcessId, int dwThreadId, uint dwContinueStatus); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
static extern bool CloseHandle(IntPtr hObject); | |
#endregion | |
public Process Process { get; private set; } | |
public ProcessMemory(Process process) | |
{ | |
this.Process = process; | |
} | |
public IntPtr Alloc(int size) | |
{ | |
if (size <= 0) | |
throw new ArgumentNullException("size"); | |
if (this.Process == null) | |
throw new Exception("Process exists"); | |
var address = VirtualAllocEx(this.Process.Handle, IntPtr.Zero, size, AllocationType.Commit, MemoryProtection.ExecuteReadWrite); | |
if (address == IntPtr.Zero) | |
throw new Win32Exception(); | |
return address; | |
} | |
public void Free(IntPtr address) | |
{ | |
if (address == IntPtr.Zero) | |
throw new ArgumentNullException("address"); | |
if (this.Process == null) | |
throw new Exception("Process exists"); | |
if (!VirtualFreeEx(this.Process.Handle, address, 0, FreeType.Release)) | |
throw new Win32Exception(); | |
} | |
public T Read<T>(IntPtr address) where T : struct | |
{ | |
var result = new byte[Marshal.SizeOf(typeof(T))]; | |
ReadProcessMemory(this.Process.Handle, address, result, new IntPtr(result.Length), IntPtr.Zero); | |
var handle = GCHandle.Alloc(result, GCHandleType.Pinned); | |
T returnObject = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); | |
handle.Free(); | |
return returnObject; | |
} | |
public string ReadString(IntPtr addess, int length = 100) | |
{ | |
var result = new byte[length]; | |
ReadProcessMemory(this.Process.Handle, addess, result, new IntPtr(length), IntPtr.Zero); | |
return Encoding.UTF8.GetString(result.TakeWhile(ret => ret != 0).ToArray()); | |
} | |
public IntPtr Write<T>(T value) where T : struct | |
{ | |
var buffer = new byte[Marshal.SizeOf(value)]; | |
var hObj = Marshal.AllocHGlobal(buffer.Length); | |
var address = Alloc(buffer.Length); | |
if (address == IntPtr.Zero) | |
throw new Win32Exception(); | |
try | |
{ | |
Marshal.StructureToPtr(value, hObj, false); | |
Marshal.Copy(hObj, buffer, 0, buffer.Length); | |
if (!WriteProcessMemory(this.Process.Handle, address, buffer, new IntPtr(buffer.Length), IntPtr.Zero)) | |
throw new Win32Exception(); | |
} | |
catch | |
{ | |
Free(address); | |
} | |
finally | |
{ | |
Marshal.FreeHGlobal(hObj); | |
} | |
return address; | |
} | |
public void Write<T>(IntPtr address, T value) where T : struct | |
{ | |
var buffer = new byte[Marshal.SizeOf(value)]; | |
var hObj = Marshal.AllocHGlobal(buffer.Length); | |
try | |
{ | |
Marshal.StructureToPtr(value, hObj, false); | |
Marshal.Copy(hObj, buffer, 0, buffer.Length); | |
if (!WriteProcessMemory(this.Process.Handle, address, buffer, new IntPtr(buffer.Length), IntPtr.Zero)) | |
throw new Win32Exception(); | |
} | |
finally | |
{ | |
Marshal.FreeHGlobal(hObj); | |
} | |
} | |
public IntPtr Write(byte[] buffer) | |
{ | |
var addr = this.Alloc(buffer.Length); | |
if (addr == IntPtr.Zero) | |
throw new Win32Exception(); | |
this.Write(addr, buffer); | |
return addr; | |
} | |
public void Write(IntPtr address, byte[] buffer) | |
{ | |
WriteProcessMemory(this.Process.Handle, address, buffer, new IntPtr(buffer.Length), IntPtr.Zero); | |
} | |
public void WriteCString(IntPtr address, string str) | |
{ | |
var buffer = Encoding.UTF8.GetBytes(str + '\0'); | |
WriteProcessMemory(this.Process.Handle, address, buffer, new IntPtr(buffer.Length), IntPtr.Zero); | |
} | |
public IntPtr WriteCString(string str) | |
{ | |
var buffer = Encoding.UTF8.GetBytes(str + '\0'); | |
var address = Alloc(buffer.Length); | |
WriteProcessMemory(this.Process.Handle, address, buffer, new IntPtr(buffer.Length), IntPtr.Zero); | |
return address; | |
} | |
public void Call(IntPtr address, params int[] funcArgs) | |
{ | |
var retaddr = Write<uint>(0xDEAD); | |
var tHandle = OpenThread(ThreadAccess.All, false, this.Process.Threads[0].Id); | |
if (SuspendThread(tHandle) == 0xFFFFFFFF) | |
throw new Win32Exception(); | |
var context = new CONTEXT { ContextFlags = ContextFlags.Control }; | |
if (!GetThreadContext(tHandle, ref context)) | |
throw new Win32Exception(); | |
var bytes = new List<byte>(); | |
// push eip (stored refernse to next inctruction) | |
bytes.Add(0x68); | |
bytes.AddRange(BitConverter.GetBytes(context.Eip)); | |
// pushad (stored general registers) | |
bytes.Add(0x60); | |
// pushed to the stack function arguments | |
for (int i = funcArgs.Length - 1; i >= 0; --i) | |
{ | |
if (funcArgs[i] == 0) | |
{ | |
// push 0 | |
bytes.Add(0x6A); | |
bytes.Add(0x00); | |
} | |
else | |
{ | |
// push address | |
bytes.Add(0x68); | |
bytes.AddRange(BitConverter.GetBytes(funcArgs[i])); | |
} | |
} | |
// mov eax, address | |
var addr = this.Process.MainModule.BaseAddress.ToInt32() + address.ToInt32(); | |
bytes.Add(0xB8); | |
bytes.AddRange(BitConverter.GetBytes(addr)); | |
// call eax | |
bytes.Add(0xFF); | |
bytes.Add(0xD0); | |
// add esp, arg_count * pointersize (__cdecl correct stack) | |
bytes.Add(0x83); | |
bytes.Add(0xC4); | |
bytes.Add((byte)(funcArgs.Length * IntPtr.Size)); | |
// mov [retaddr], eax | |
bytes.Add(0xA3); | |
bytes.AddRange(BitConverter.GetBytes(retaddr.ToInt32())); | |
// popad (restore general registers) | |
bytes.Add(0x61); | |
// retn | |
bytes.Add(0xC3); | |
var code = this.Write(bytes.ToArray()); | |
context.Eip = (uint)code.ToInt32(); | |
context.ContextFlags = ContextFlags.Control; | |
if (!SetThreadContext(tHandle, ref context) || ResumeThread(tHandle) == 0xFFFFFFFF) | |
throw new Win32Exception(); | |
for (int i = 0; i < 0x100; ++i) | |
{ | |
System.Threading.Thread.Sleep(15); | |
if (this.Read<uint>(retaddr) != 0xDEAD) | |
break; | |
} | |
Free(retaddr); | |
Free(code); | |
} | |
public void ExecLua(IntPtr pointer, string source) | |
{ | |
var code = WriteCString(source); | |
var path = WriteCString("Teldrassil.lua"); | |
Call(pointer, code.ToInt32(), source.Length, path.ToInt32(), 0, 0, 0); | |
Free(code); | |
Free(path); | |
} | |
public IntPtr Rebase(IntPtr offset) | |
{ | |
return new IntPtr(offset.ToInt64() + this.Process.MainModule.BaseAddress.ToInt64()); | |
} | |
#region Debugger | |
private Thread debugThread; | |
private bool isRuning = false; | |
const int HWBreakCount = 4; | |
private IntPtr[] HWBreaks = new IntPtr[HWBreakCount]; | |
public event HWReakPointHandler OnHWBreakPoint1; | |
public event HWReakPointHandler OnHWBreakPoint2; | |
public event HWReakPointHandler OnHWBreakPoint3; | |
public event HWReakPointHandler OnHWBreakPoint4; | |
public void SetHWBreakPoint(int index, int offset = 0) | |
{ | |
if (index > 3 || index < 0) | |
throw new ArgumentOutOfRangeException("index", "must be beetwen 0-3"); | |
foreach (ProcessThread thread in this.Process.Threads) | |
{ | |
var thread_context = new CONTEXT { ContextFlags = ContextFlags.All }; | |
var hThread = ProcessMemory.OpenThread(ThreadAccess.All, false, thread.Id); | |
ProcessMemory.SuspendThread(hThread); | |
ProcessMemory.GetThreadContext(hThread, ref thread_context); | |
// Dr0-Dr3 - HW breakpoint Address | |
HWBreaks[index] = this.Process.MainModule.BaseAddress + offset; | |
thread_context.Dr0 = (uint)HWBreaks[index].ToInt32(); | |
// L0-L3 - Flag activate Dr0-Dr3 (4 bit) | |
if (offset == 0) | |
thread_context.Dr7 &= ~(1u << index); | |
else | |
thread_context.Dr7 |= 1u << index; | |
if (hThread != IntPtr.Zero) | |
{ | |
if (!ProcessMemory.SetThreadContext(hThread, ref thread_context)) | |
throw new Win32Exception(); | |
ProcessMemory.ResumeThread(hThread); | |
ProcessMemory.CloseHandle(hThread); | |
} | |
} | |
if (HWBreaks.Any(n => n != IntPtr.Zero)) | |
{ | |
isRuning = true; | |
if (debugThread == null) | |
{ | |
debugThread = new Thread(DooDebug); | |
debugThread.Start(); | |
} | |
} | |
else | |
{ | |
isRuning = false; | |
if (debugThread != null) | |
{ | |
debugThread.Abort(); | |
debugThread = null; | |
} | |
} | |
} | |
public void DooDebug() | |
{ | |
if (!DebugActiveProcess(this.Process.Id)) | |
throw new Win32Exception(); | |
DebugSetProcessKillOnExit(false); | |
DEBUG_EVENT DebugEvent = new DEBUG_EVENT(); | |
while (isRuning) | |
{ | |
if (!WaitForDebugEvent(ref DebugEvent, 0xFFFFFFFF)) | |
throw new Exception(); | |
if (DebugEvent.ExceptionCode == 0x80000004 && ( //EXCEPTION_SINGLE_STEP | |
(HWBreaks[0] != IntPtr.Zero && DebugEvent.ExceptionAddress == HWBreaks[0]) || | |
(HWBreaks[1] != IntPtr.Zero && DebugEvent.ExceptionAddress == HWBreaks[1]) || | |
(HWBreaks[2] != IntPtr.Zero && DebugEvent.ExceptionAddress == HWBreaks[2]) || | |
(HWBreaks[3] != IntPtr.Zero && DebugEvent.ExceptionAddress == HWBreaks[3]))) | |
{ | |
var hThread = ProcessMemory.OpenThread(ThreadAccess.All, false, DebugEvent.dwThreadId); | |
var thread_context = new CONTEXT() { ContextFlags = ContextFlags.Full }; | |
ProcessMemory.GetThreadContext(hThread, ref thread_context); | |
// logic | |
if (OnHWBreakPoint1 != null && HWBreaks[0] != IntPtr.Zero | |
&& DebugEvent.ExceptionAddress == HWBreaks[0]) | |
OnHWBreakPoint1(this, thread_context); | |
if (OnHWBreakPoint2 != null && HWBreaks[1] != IntPtr.Zero | |
&& DebugEvent.ExceptionAddress == HWBreaks[1]) | |
OnHWBreakPoint2(this, thread_context); | |
if (OnHWBreakPoint3 != null && HWBreaks[2] != IntPtr.Zero | |
&& DebugEvent.ExceptionAddress == HWBreaks[2]) | |
OnHWBreakPoint3(this, thread_context); | |
if (OnHWBreakPoint4 != null && HWBreaks[3] != IntPtr.Zero | |
&& DebugEvent.ExceptionAddress == HWBreaks[3]) | |
OnHWBreakPoint4(this, thread_context); | |
thread_context.EFlags |= 0x00010000; // CONTEXT_i386 | |
ProcessMemory.SetThreadContext(hThread, ref thread_context); | |
ProcessMemory.CloseHandle(hThread); | |
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, 0x00010002);// DBG_CONTINUE); | |
} | |
else | |
{ | |
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, 0x80010001 /*DBG_EXCEPTION_NOT_HANDLED*/); | |
} | |
} | |
if (!DebugActiveProcessStop(this.Process.Id)) | |
throw new Win32Exception(); | |
} | |
#endregion | |
public void Dispose() | |
{ | |
for (int i = 0; i < HWBreakCount; ++i) | |
SetHWBreakPoint(i); | |
isRuning = false; | |
if (debugThread != null) | |
{ | |
debugThread.Abort(); | |
debugThread = null; | |
} | |
} | |
} | |
#region Enums | |
[Flags] | |
public enum AllocationType : uint | |
{ | |
Commit = 0x00001000, | |
Reserve = 0x00002000, | |
Decommit = 0x00004000, | |
Release = 0x00008000, | |
Reset = 0x00080000, | |
TopDown = 0x00100000, | |
WriteWatch = 0x00200000, | |
Physical = 0x00400000, | |
LargePages = 0x20000000, | |
} | |
[Flags] | |
public enum MemoryProtection : uint | |
{ | |
NoAccess = 0x001, | |
ReadOnly = 0x002, | |
ReadWrite = 0x004, | |
WriteCopy = 0x008, | |
Execute = 0x010, | |
ExecuteRead = 0x020, | |
ExecuteReadWrite = 0x040, | |
ExecuteWriteCopy = 0x080, | |
GuardModifierflag = 0x100, | |
NoCacheModifierflag = 0x200, | |
WriteCombineModifierflag = 0x400, | |
} | |
[Flags] | |
public enum FreeType : uint | |
{ | |
Decommit = 0x4000, | |
Release = 0x8000, | |
} | |
[Flags] | |
public enum ThreadAccess : uint | |
{ | |
Terminate = 0x00001, | |
SuspendResume = 0x00002, | |
GetContext = 0x00008, | |
SetContext = 0x00010, | |
SetInformation = 0x00020, | |
QueryInformation = 0x00040, | |
SetThreadToken = 0x00080, | |
Impersonate = 0x00100, | |
DirectImpersonation = 0x00200, | |
All = 0x1F03FF | |
} | |
[Flags] | |
public enum ContextFlags : uint | |
{ | |
i386 = 0x10000, | |
i486 = 0x10000, // same as i386 | |
Control = i386 | 0x01, // SS:SP, CS:IP, FLAGS, BP | |
Integer = i386 | 0x02, // AX, BX, CX, DX, SI, DI | |
Segments = i386 | 0x04, // DS, ES, FS, GS | |
FloatingPoint = i386 | 0x08, // 387 state | |
DebugRegisters = i386 | 0x10, // DB 0-3,6,7 | |
ExtendedRegisters = i386 | 0x20, // cpu specific extensions | |
Full = Control | Integer | Segments, | |
All = Control | Integer | Segments | FloatingPoint | DebugRegisters | ExtendedRegisters | |
} | |
[StructLayout(LayoutKind.Sequential, Size = 0x60)] | |
public struct DEBUG_EVENT | |
{ | |
public uint dwDebugEventCode; | |
public int dwProcessId; | |
public int dwThreadId; | |
public uint ExceptionCode; | |
public uint ExceptionFlags; | |
public IntPtr ExceptionRecord; | |
public IntPtr ExceptionAddress; | |
public uint NumberParameters; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct CONTEXT | |
{ | |
public ContextFlags 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 | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst=112)] | |
public byte[] 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; | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment