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
NtDllmethods instead ofKernel32whenever 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]
stringPlease 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
