Main Code
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Runtime.InteropServices;
public class WinApi
{
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
#region "Windows(R) Functions"
public static extern void GetSystemInfo(ref SystemInfo lpSystemInfo);
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern IntPtr OpenProcess(UInt32 dwDesiredAcess, bool bInheritHandle, Int32 dwProcessId);
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 iSize, ref int lpNumberOfBytesRead);
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpbaseAddress, byte[] lpBuffer, Int32 nSize, ref Int32 dwNumberOfBytesWritten);
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, Int32 dwSize, UInt32 dwNewProtect, ref UInt32 dwOldProtect);
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, ref MemoryBasicInformation lpMemoryBasicInformation, Int32 dwLength);
#endregion
#region "Structs"
public struct SystemInfo
{
public Int16 wProcessorArchitecture;
public Int16 wReserved;
public Int32 dwPageSize;
public IntPtr lpMinimumApplicationAddress;
public IntPtr lpMaximumApplicationAddress;
public Int32 dwActiveProcessorMask;
public Int32 dwNumberOfProcessors;
public Int32 dwProcessorType;
public Int32 dwAllocationGranularity;
public Int16 wProcessorLevel;
public Int16 wProcessorRevision;
}
public struct MemoryBasicInformation
{
public IntPtr BaseAddress;
public IntPtr AllocationBase;
public UInt32 AllocationProtect;
public IntPtr RegionSize;
public UInt32 State;
public UInt32 Protect;
public UInt32 AllocationType;
}
#endregion
#region "Enums"
public enum MemoryAllocationProtection : UInt32
{
PAGE_NOACCESS = 0x1,
PAGE_READONLY = 0x2,
PAGE_READWRITE = 0x4,
PAGE_WRITECOPY = 0x8,
PAGE_EXECUTE = 0x10,
PAGE_EXECUTE_READ = 0x20,
PAGE_EXECUTE_READWRITE = 0x40,
PAGE_EXECUTE_WRITECOPY = 0x80,
PAGE_GUARD = 0x100,
PAGE_NOCACHE = 0x200,
PAGE_WRITECOMBINE = 0x400
}
public enum MemoryAllocationType : UInt32
{
MEM_IMAGE = 0x1000000,
MEM_MAPPED = 0x40000,
MEM_PRIVATE = 0x20000
}
public enum MemoryAllocationState : UInt32
{
COMMIT = 0x1000,
RESERVE = 0x2000,
DECOMMIT = 0x4000,
RELEASE = 0x8000,
RESET = 0x80000,
PHYSICAL = 0x400000,
TOP_DOWN = 0x100000,
WRITE_WATCH = 0x200000,
LARGE_PAGES = 0x20000000
}
public enum ProcessAccess : UInt32
{
//'Function descriptions taken from msdn.com
/// <summary>
/// Required to terminate a process using TerminateProcess.
/// </summary>
PROCESS_TERMINATE = 0x1,
/// <summary>
/// Required to create a thread.
/// </summary>
PROCESS_CREATE_THREAD = 0x2,
/// <summary>
/// Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
/// </summary>
PROCESS_VM_OPERATION = 0x8,
/// <summary>
/// Required to read memory in a process using ReadProcessMemory.
/// </summary>
PROCESS_VM_READ = 0x10,
/// <summary>
/// Required to write to memory in a process using WriteProcessMemory.
/// </summary>
PROCESS_VM_WRITE = 0x20,
/// <summary>
/// Required to duplicate a handle using DuplicateHandle.
/// </summary>
PROCESS_DUP_HANDLE = 0x40,
/// <summary>
/// Required to create a process.
/// </summary>
PROCESS_CREATE_PROCESS = 0x80,
/// <summary>
/// Required to set memory limits using SetProcessWorkingSetSize.
/// </summary>
PROCESS_SET_QUOTA = 0x100,
/// <summary>
/// Required to set certain information about a process, such as its priority class (see SetPriorityClass).
/// </summary>
PROCESS_SET_INFORMATION = 0x200,
/// <summary>
/// Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken).
/// </summary>
PROCESS_QUERY_INFORMATION = 0x400,
/// <summary>
/// Required to suspend or resume a process.
/// </summary>
PROCESS_SUSPEND_RESUME = 0x800,
/// <summary>
/// Required to retrieve certain information about a process (see GetExitCodeProcess, GetPriorityClass, IsProcessInJob, QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION. Windows Server 2003 and Windows XP: This access right is not supported.
/// </summary>
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000,
/// <summary>
/// Required to wait for the process to terminate using the wait functions.
/// </summary>
PROCESS_SYNCHRONIZE = 0x100000
}
#endregion
}
public class MemoryManager : IDisposable
{
#region "Private"
private bool _isAttached = false;
private Int32 _targetProcessId = 0;
//'
private IntPtr _targetProcessHandle = IntPtr.Zero;
private IntPtr _mainModuleBase = IntPtr.Zero;
private WinApi.SystemInfo _systemInfo;
private Int32 _mbiSize = 0;
public MemoryManager()
{
WinApi.GetSystemInfo(ref _systemInfo);
_mbiSize = System.Runtime.InteropServices.Marshal.SizeOf(new WinApi.MemoryBasicInformation());
}
#endregion
public bool AttachToProcess(Int32 processId)
{
if (_isAttached)
{
return false;
}
else
{
foreach (Process proc in Process.GetProcesses())
{
if (proc.Id == processId)
{
_targetProcessHandle = WinApi.OpenProcess(WinApi.ProcessAccess.PROCESS_VM_WRITE | WinApi.ProcessAccess.PROCESS_VM_READ | WinApi.ProcessAccess.PROCESS_VM_OPERATION | WinApi.ProcessAccess.PROCESS_QUERY_INFORMATION, false, processId);
if (_targetProcessHandle == IntPtr.Zero)
{
return false;
}
else
{
_isAttached = true;
_targetProcessId = processId;
_mainModuleBase = proc.MainModule.BaseAddress;
return true;
}
}
}
MessageBox.Show("MemoryManager::AttachToProcess() Process Id not found: " + processId.ToString());
return false;
}
}
public void DetachFromProcess()
{
if (_isAttached)
{
if (_targetProcessHandle != IntPtr.Zero)
{
WinApi.CloseHandle(_targetProcessHandle);
}
_isAttached = false;
_targetProcessId = 0;
_targetProcessHandle = IntPtr.Zero;
}
}
public bool IsAttached
{
get { return _isAttached; }
}
public IntPtr MainModuleBase
{
get { return _mainModuleBase; }
}
#region "Read"
public byte ReadByte(IntPtr addr)
{
byte[] _byte = new byte[1];
if (_isAttached)
WinApi.ReadProcessMemory(_targetProcessHandle, addr, _byte, 1, ref new Int32());
return _byte(0);
}
public Int16 ReadInt16(IntPtr addr)
{
byte[] _bytes = new byte[2];
if (_isAttached)
WinApi.ReadProcessMemory(_targetProcessHandle, addr, _bytes, 2, ref new Int32());
return BitConverter.ToInt16(_bytes, 0);
}
public Int32 ReadInt32(IntPtr addr)
{
byte[] _bytes = new byte[4];
if (_isAttached)
WinApi.ReadProcessMemory(_targetProcessHandle, addr, _bytes, 4, ref new Int32());
return BitConverter.ToInt32(_bytes, 0);
}
public Int64 ReadInt64(IntPtr addr)
{
byte[] _bytes = new byte[8];
if (_isAttached)
WinApi.ReadProcessMemory(_targetProcessHandle, addr, _bytes, 8, ref new Int32());
return BitConverter.ToInt64(_bytes, 0);
}
public UInt16 ReadUInt16(IntPtr addr)
{
byte[] _bytes = new byte[2];
if (_isAttached)
WinApi.ReadProcessMemory(_targetProcessHandle, addr, _bytes, 2, ref new Int32());
return BitConverter.ToUInt16(_bytes, 0);
}
public UInt32 ReadUInt32(IntPtr addr)
{
byte[] _bytes = new byte[4];
if (_isAttached)
WinApi.ReadProcessMemory(_targetProcessHandle, addr, _bytes, 4, ref new Int32());
return BitConverter.ToUInt32(_bytes, 0);
}
public UInt64 ReadUInt64(IntPtr addr)
{
byte[] _bytes = new byte[8];
if (_isAttached)
WinApi.ReadProcessMemory(_targetProcessHandle, addr, _bytes, 8, ref new Int32());
return BitConverter.ToUInt64(_bytes, 0);
}
public float ReadFloat(IntPtr addr)
{
byte[] _bytes = new byte[4];
if (_isAttached)
WinApi.ReadProcessMemory(_targetProcessHandle, addr, _bytes, 4, ref new Int32());
return BitConverter.ToSingle(_bytes, 0);
}
public double ReadDouble(IntPtr addr)
{
byte[] _bytes = new byte[8];
if (_isAttached)
WinApi.ReadProcessMemory(_targetProcessHandle, addr, _bytes, 8, ref new Int32());
return BitConverter.ToDouble(_bytes, 0);
}
/// <summary>
/// Reads an Ascii string from the target process's ram.
/// </summary>
/// <param name="addr">Beginning address of the string.</param>
/// <param name="maxLength">Max length of string if unknown. Must be > 0</param>
public string ReadAsciiString(IntPtr addr, Int32 maxLength)
{
if (!(_isAttached && (maxLength > 0)))
{
return string.Empty;
}
byte[] _bytes = new byte[maxLength];
Int32 _bytesRead = 0;
if (WinApi.ReadProcessMemory(_targetProcessHandle, addr, _bytes, maxLength, ref _bytesRead))
{
return System.Text.Encoding.ASCII.GetString(_bytes, 0, _bytesRead);
}
else
{
return string.Empty;
//'rpm failed
}
}
public byte[] ReadBytes(IntPtr address, int Length = 4)
{
byte[] lpBuffer = new byte[((Length - 1) + 1)];
WinApi.ReadProcessMemory(_targetProcessHandle, address, lpBuffer, Length, ref 0);
return lpBuffer;
}
/// <summary>
/// Read a Unicode string from the target process's ram.
/// </summary>
/// <param name="addr">Beginning address of the string.</param>
/// <param name="maxLength">Max length of string if unknown. Must be > 0</param>
public string ReadUnicodeString(IntPtr addr, Int32 maxLength)
{
if (!(_isAttached && (maxLength > 0)))
{
return string.Empty;
//' fail. not attached to any process (or maxLength = 0).
}
maxLength = maxLength * 2;
//' 2 bytes per character. TODO: "Unicode" ?
byte[] _bytes = new byte[maxLength];
Int32 _bytesRead = 0;
if (WinApi.ReadProcessMemory(_targetProcessHandle, addr, _bytes, _bytes.Length, ref _bytesRead))
{
return System.Text.Encoding.Unicode.GetString(_bytes, 0, _bytesRead);
}
else
{
return string.Empty;
//' rpm failed.
}
}
/// <summary>
/// Reads an array of bytes from the target process's ram into a pre-declared buffer.
/// </summary>
/// <param name="byteBuff">Must be large enough, will not be re-sized!</param>
/// <param name="size">Number of bytes to read.</param>
/// <param name="actualBytesRead">Byref. Returns the actual number of bytes read.</param>
public bool ReadBytes(IntPtr addr, ref byte[] byteBuff, Int32 size, ref Int32 actualBytesRead)
{
return WinApi.ReadProcessMemory(_targetProcessHandle, addr, byteBuff, size, ref actualBytesRead);
}
#endregion
#region "Write"
public bool WriteByte(IntPtr addr, byte aByte)
{
byte[] _bts = { aByte };
//' Awkward. Winapi function is declared as array() instead of as IntPtr
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, _bts, 1, ref new Int32());
}
public bool WriteInt16(IntPtr addr, Int16 data)
{
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, BitConverter.GetBytes(data), 2, ref new Int32());
}
public bool WriteUInt16(IntPtr addr, UInt16 data)
{
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, BitConverter.GetBytes(data), 2, ref new Int32());
}
public bool WriteInt32(IntPtr addr, Int32 data)
{
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, BitConverter.GetBytes(data), 4, ref new Int32());
}
public bool WriteInt64(IntPtr addr, Int64 data)
{
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, BitConverter.GetBytes(data), 8, ref new Int32());
}
public bool WriteUInt64(IntPtr addr, UInt64 data)
{
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, BitConverter.GetBytes(data), 8, ref new Int32());
}
public bool WriteFloat(IntPtr addr, float data)
{
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, BitConverter.GetBytes(data), 4, ref new Int32());
}
public bool WriteDouble(IntPtr addr, double data)
{
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, BitConverter.GetBytes(data), 8, ref new Int32());
}
public bool WriteAsciiString(IntPtr addr, string str, ref Int32 actualBytesWritten)
{
byte[] _bytes = System.Text.Encoding.ASCII.GetBytes(str);
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, _bytes, _bytes.Length, ref actualBytesWritten);
}
public bool WriteUnicodeString(IntPtr addr, string str, ref Int32 actualBytesWritten)
{
byte[] _bytes = System.Text.Encoding.Unicode.GetBytes(str);
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, _bytes, _bytes.Length, ref actualBytesWritten);
}
public bool WriteBytes(IntPtr addr, byte[] bytes)
{
dynamic byteCount = bytes.Count();
return WinApi.WriteProcessMemory(_targetProcessHandle, addr, bytes, bytes.Length, ref byteCount);
}
public void WriteNOP(IntPtr address, long Length = 0)
{
long num = (Length - 1);
long i = 0;
while ((i <= num))
{
WriteByte((address + Convert.ToInt32(i)), 0x90);
i = (i + 1);
}
}
#endregion
#region "Hack"
public WinApi.MemoryBasicInformation[] GetMemRegions()
{
List<WinApi.MemoryBasicInformation> _rtnBlocks = new List<WinApi.MemoryBasicInformation>();
Int64 _curBase = _systemInfo.lpMinimumApplicationAddress.ToInt64;
WinApi.MemoryBasicInformation _curMbi = default(WinApi.MemoryBasicInformation);
try
{
do
{
if (WinApi.VirtualQueryEx(_targetProcessHandle, new IntPtr(_curBase), ref _curMbi, _mbiSize) == 0)
{
break; // TODO: might not be correct. Was : Exit Do
}
if (_curMbi.State == WinApi.MemoryAllocationState.COMMIT)
{
_rtnBlocks.Add(_curMbi);
}
_curBase += _curMbi.RegionSize.ToInt64;
} while (_curBase < _systemInfo.lpMaximumApplicationAddress.ToInt64);
}
catch (Exception ex)
{
MessageBox.Show("MemoryManager::GetMemRegions() Threw an Exception. Please give this message to tech support -->" + Environment.NewLine + "Current memory region info:" + Environment.NewLine + ".baseAddress: 0x" + _curMbi.BaseAddress.ToString("X") + Environment.NewLine + ".size: " + _curMbi.RegionSize.ToString() + " bytes" + Environment.NewLine + ".protect: " + _curMbi.Protect + Environment.NewLine + ".type: " + _curMbi.AllocationType + Environment.NewLine + "state: " + _curMbi.State + Environment.NewLine + Environment.NewLine + "System error message (also important): " + Environment.NewLine + ex.Message + Environment.NewLine + "Thank you. ");
}
return _rtnBlocks.ToArray();
}
#endregion
#region "IDisposable Support"
private bool disposedValue;
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
DetachFromProcess();
}
}
this.disposedValue = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}