Friday, December 14, 2012

[C#] Read and write process memory in Windows

Disclaimer: I take no moral standpoint or legal responsibility of what you do with this information, just be aware of the consequences of your actions before you commit to them. (Tip: read license agreements)

Here is the code for this post:

Today we will manipulate a simple application, Notepad, through memory reading, writing and C# code.

Process in the System.Diagnostics namespace holds the GetProcessByName method, which returns a list of all processes with that name.
Process[] p = Process.GetProcessesByName("notepad");
We must now define a method which interacts with the Windows native libraries to read data from a process. Available through the System.Runtime.InteropServices lies OpenProcess in Kernel32, which gives us a handle we need later to both read and write in the memory allocated to the process.
public static extern int OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
We must set our desired level of Access Rights for our handle in the dwDesiredAccess parameter. This is not entirely simple. In essence, we create a flag depending on what we want to do, combining all the parameters into one. The following will give us full access.
uint DELETE = 0x00010000;
uint READ_CONTROL = 0x00020000;
uint WRITE_DAC = 0x00040000;
uint WRITE_OWNER = 0x00080000;
uint SYNCHRONIZE = 0x00100000;
uint END = 0xFFF; //if you have Windows XP or Windows Server 2003 you must change this to 0xFFFF
Let's assume you only have one Notepad process for simplicity. Since we have the Notepad process, get can get it's ID or dwProcessId. You can now get the handle like this.
int processHandle = OpenProcess(PROCESS_ALL_ACCESS, false, p[0].Id); 
The handle is required to call the two methods we use for memory reading and writing within Kernel32. Define them as the following:
public static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] buffer, int size, int lpNumberOfBytesRead);

public static extern bool WriteProcessMemory(int hProcess, int lpBaseAddress, byte[] buffer, int size, int lpNumberOfBytesWritten);
To simplify things:
public byte[] ReadMemory(int adress, int processSize, int processHandle) {
    byte[] buffer = new byte[processSize];
    ReadProcessMemory(processHandle, adress, buffer, processSize, 0);
    return buffer;

public void WriteMemory(int adress, byte[] processBytes, int processHandle) {
    WriteProcessMemory(processHandle, adress, processBytes, processBytes.Length, 0);
Let's try reading first. We have the processHandle parameter, but not the adress or size of the allocated memory we are going to read from the adress position. You can get these by a number of simple or advanced methods depending on what process you want to look at. The simplest way is to use Cheat Engine.

Open notepad and find the memory location with Cheat Engine (be sure to set it to Unicode).

On my computer the string "Hello!" has been located on (0x)0026D3F0. But how big is it in memory? This is not simple in most cases. For this case, we can send the string "Hello!" to memory and read how big it is though with the following:
public int GetObjectSize(object TestObject) {
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();
    byte[] Array;
    bf.Serialize(ms, TestObject);
    Array = ms.ToArray();
    return Array.Length;

int processSize = GetObjectSize("Hello!");
Now we can finally read from the Notepad process!
Console.WriteLine((Encoding.Unicode.GetString(ReadMemory(0x0026D3F0, processSize, processHandle))));

Writing is easy too.
WriteMemory(0x0026D3F0, Encoding.Unicode.GetBytes("Wow!" + "\0"), processHandle);

The reason we get a trailing "!" is because it still lingers in memory. We did not overwrite the entire allocated memory for "Hello!" with our "Wow!" string.

No comments:

Post a Comment