In many projects, I needed to access another process’s memory but there are only a few examples that are not using C#’s full potential and some of them are very old. I came up with a simple way of reading and writing memory with very little code.
Keep in mind that this way only works for unmanaged data types like int
or byte
, to read strings from memory you need to do some more work, have a look into this class: https://github.com/Jnnshschl/AmeisenBotX/blob/master/AmeisenBotX.Memory/XMemory.cs
1
2
3
4
5
6
7
8
9
| Memory mem = new();
mem.Attach(__PROCESS__)
mem.Write(new IntPtr(0x400000), 4711);
if (mem.Read(new IntPtr(0x408192), out int i))
{
// ...
}
|
Needed imports and structures from ntdll.dll
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| [DllImport("ntdll", SetLastError = false)]
public static extern int NtClose(IntPtr hObject);
[DllImport("ntdll", SetLastError = true)]
public static extern bool NtOpenProcess(out IntPtr processHandle, ProcessAccessFlags accessMask, ref ObjectAttributes objectAttributes, ref ClientId clientId);
[DllImport("ntdll", SetLastError = true)]
public static extern bool NtReadVirtualMemory(IntPtr processHandle, IntPtr baseAddress, void* buffer, int size, out IntPtr numberOfBytesRead);
[DllImport("ntdll", SetLastError = true)]
public static extern bool NtWriteVirtualMemory(IntPtr processHandle, IntPtr baseAddress, void* buffer, int size, out IntPtr numberOfBytesWritten);
public struct ClientId
{
public IntPtr UniqueProcess;
public IntPtr UniqueThread;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct ObjectAttributes
{
public int Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
|
Memory class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| public unsafe class Memory
{
public IntPtr ProcessHandle { get; private set; }
public void Attach(Process process)
{
ObjectAttributes attr = new();
attr.Length = sizeof(ObjectAttributes);
ClientId clientID = new();
clientID.UniqueProcess = new(process.Id);
if (!NtOpenProcess(out IntPtr processHandle, ProcessAccessFlags.VirtualMemoryReadWrite, ref attr, ref clientID))
{
ProcessHandle = processHandle;
}
}
public void Detach()
{
if (ProcessHandle != IntPtr.Zero)
{
NtClose(ProcessHandle);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Read<T>(IntPtr address, out T value) where T : unmanaged
{
int size = sizeof(T);
fixed (byte* pBuffer = stackalloc byte[size])
{
if (!NtReadVirtualMemory(ProcessHandle, address, pBuffer, size, out _))
{
value = *(T*)pBuffer;
return true;
}
}
value = default;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Write<T>(IntPtr address, T value) where T : unmanaged
{
return !NtWriteVirtualMemory(ProcessHandle, address, &value, sizeof(T), out _);
}
}
|