This library implements performant wrapper methods over, in game hacking, commonly used NtDll
and Kernel32
functions. The different classes allow you to use generic type parameters with ReadProcessMemory
and WriteProcessMemory
and call simpler functions like OpenProcess
, CreateRemoteThread
and more without any overhead.
While most of the methods are implemented using their NtDll
equivalent instead of Kernel32
some still require Kernel32
to work properly or are depricated in NtDll
.
All methods are tested and working under 32bit and 64bit windows and are well documented.
This library is available in the NuGet Gallery.
The ProcessMemoryUtilities.Native
namespace offers direct access to either Kernel32
or NtDll
methods without any overhead. Most of them not only offer the traditional function signature but also implement overloads with common default values set.
All the required enums and constants are available with their XML documentation.
The ProcessMemoryUtilities.Managed
namespace offers the NativeWrapper
class which is there to provide a single place to access all the implemented methods with a more user friendly and Kernel32
like interface. This also adds basic error handling over ReadProcessMemory
and WriteProcessMemory
and offers a Win32
error code when any function failed.
- CloseHandle
- CreateRemoteThread(Ex)
- Generic ReadProcessMemory
- Generic WriteProcessMemory
- OpenProcess
- VirtualAllocEx
- VirtualFreeEx
- VirtualProtectEx
- WaitForSingleObject
Every native method is implemented using the calli
IL instruction and bypasses type limitations introduced in C#. All native calls are done in a safe manner and use correct types and pinning for pointer variables.
Some important improvements are:
- Direct calls to WinAPI methods
- Using
NtDll
methods instead ofKernel32
whenever possible - No performance loss due to marshaling or delegates
- Optimized memory allocations
I copied some of the function signatures to give you a quick overview of what kind of methods you can expect from this library.
// CreateRemoteThreadEx with a reduced set of parameters for easier usage
IntPtr CreateRemoteThreadEx(IntPtr handle, IntPtr startAddress, IntPtr parameter);
// compared to this one which is also available
IntPtr CreateRemoteThreadEx(IntPtr handle, IntPtr threadAttributes, IntPtr stackSize, IntPtr startAddress, IntPtr parameter, ThreadCreationFlags creationFlags, IntPtr attributeList, out uint threadId);
// OpenProcess
IntPtr OpenProcess(ProcessAccessFlags desiredAccess, bool inheritHandle, int processId);
IntPtr OpenProcess(ProcessAccessFlags desiredAccess, int processId);
// WaitForSingleObject
WaitObjectResult WaitForSingleObject(IntPtr handle, uint timeout);
// ReadProcessMemory and WriteProcessMemory
uint NtReadVirtualMemory<T>(IntPtr handle, IntPtr baseAddress, ref T buffer, out IntPtr numberOfBytesRead);
bool WriteProcessMemoryArray<T>(IntPtr handle, IntPtr baseAddress, T[] buffer, int offset, out IntPtr numberOfBytesWritten);
// VirtualMemory functions
uint NtAllocateVirtualMemory(IntPtr handle, IntPtr size, AllocationType allocationType, MemoryProtectionFlags memoryProtection, out IntPtr address);
IntPtr VirtualAllocEx(IntPtr handle, IntPtr address, IntPtr size, AllocationType allocationType, MemoryProtectionFlags memoryProtection);
bool VirtualFreeEx(IntPtr handle, IntPtr address, IntPtr size, FreeType freeType);
bool VirtualProtectEx(IntPtr handle, IntPtr address, IntPtr size, MemoryProtectionFlags newProtect, out MemoryProtectionFlags oldProtect);
While this approach offers a wide range of benefits you may encounter a single drawback.
Because we use the IL instruction sizeof
instead of Marshal.SizeOf
the whole marshaling layer is skipped. This means that you can not use classes and the following keywords inside of structs
[MarshalAs]
string
Please use fixed
arrays instead of [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
// This does not work with my library because sizeof gives us a wrong size (4 instead of 16)
[StructLayout(LayoutKind.Explicit)]
private struct Wrong
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public int[] Numbers;
[FieldOffset(0)]
public int FirstNumber;
}
// [StructLayout(LayoutKind.Sequential)] is also a valid option
[StructLayout(LayoutKind.Explicit)]
private unsafe struct Correct
{
[FieldOffset(0)]
public fixed int Numbers[4];
[FieldOffset(0)]
public int FirstNumber;
}
The ProcessMemoryUtilities.NativeWrapper
class offers the CaptureErrors
property (which is set true by default) to emulate SetLastError
and GetLastError
.
The LastError
property converts the saved NtStatus
to a equivalent Win32
error code which you can use in your exceptions.
The project file was generated using Visual Studio 2019.
Clone or download the repository and restore the required NuGet packages.
You can help by reporting issues, adding new features, fixing bugs and by providing a better documentation.
Following dependencies are used to build the project but are NOT included in the NuGet package.
Fody
InlineIL.Fody
ILRepack.Lib.MSBuild.Task
Do you like this project and want to help me to keep working on it?
I appreciate any donation that helps me to continue working on OSS like this.
BTC bc1qp6zc73vy8pmr6lfe4cxa6eqzvkuer9hrjwpzza