Skip to content

Instantly share code, notes, and snippets.

@Konctantin
Created March 28, 2014 16:21
Show Gist options
  • Save Konctantin/9836775 to your computer and use it in GitHub Desktop.
Save Konctantin/9836775 to your computer and use it in GitHub Desktop.
ProcessMemory
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