Reading and Writing into Process's Memory
Get the basic understanding on the remote process memory read and write all by windows 32 API and create your own game hacks.
Hello, world! Till now you might have seen programs are changing the bits and bytes in their own memory, by changing the state of variables either by user input or network requests or hardcoded in the software. But today, I will teach you one of the most interesting topics among the hackers - playing with the different process's memory.
Motivation
My curiosity on this topic started while playing the IGI game after starting a different application that was used to provide me unlimited health and open all the missions – The IGI game crack. Since then I always wondered how does a different application is working to change the state of another application so accurately that it knows where the variables are stored and etc.
Then I read more about reading and writing process memory and recently tried to create my own demonstration of that we will be discussing in the following sections. In this post, I will share that knowledge to get started. This knowledge is the building block of process injection that I will cover later with different techniques and creating your own game hacks.
Writing the Victim Process Code
In this section, I will guide you to create a demo victim process that will accept a string in the CLI argument and assign it to the buffer. Then it will show the address of the buffer and copied text and total length of the string.
For any of the attacker processes, it is required to open the process by its ID. So let's start off by printing the process id. Luckily there is a function from processthreadsapi.h
– GetCurrentProcessId()
which will return the process id of the calling process.
Next, you can create a buffer using VirtualAlloc
function and use the CopyMemory()
function which is just a macro for the RtlCopyMemory()
function from the wdm.h file.
EvenRtlCopyMemory
is a macro ofmemcpy
function (read this). Therefore, there is no difference if you use CopyMemory function, the compiler will automatically resolve it to memcpy.
Print the value of the address and the contents for both of the buffer
and argv[1]
variables.
Now it's time to wait for the process so that the attacker write process can perform tamper with the base address of the string. After this print the same details, if the value tampers, it would be different from the one that is previously printed
Writing the Attacker Process
Finally we come to the section where actual magic will happen! Before moving forward let's think of a question and try to understand it. Why does modern OS like windows allow different process to peek into the address space of another process even though it is like each process address space is isolated from another and so on? Well the answer is simple, when you do the development it is required to debug your process to find the issues and resolve them. Now it is required to peek into the memory of function frames to show developers the exact picture of what is going on and why. This approach then was used by offensive security researchers to perform read/write in the remote process.
The application will be accepting at least 3 required parameter and one optional parameter – Victim's process id (PID), Hexadecimal base address of the string variable and Text to overwrite. The optional parameter will basically used to read the amount of data, this is default to 1. A single page is about 4kb (4096 bytes).
To convert the hex address from string to number type, you can use StrToInt64ExA()
function for this. It provided an option STIF_SUPPORT_HEX
to allow parsing hexadecimal string into LONGLONG
type.
Open the process handle with access to PROCESS_VM_READ
, PROCESS_VM_WRITE
and PROCESS_VM_OPERATION
to allow the attacker process to read the remote process memory and write the contents into it. This can be done by passing the combination of access rights in the OpenProcess()
function. This function will return NULL
for either the invalid process id or protected processes.
Reading from Memory
The function ReadProcessMemory()
will mainly take the process's handle, base address from where to start reading the contents and a buffer to return the contents. When the operation is successful, it will return TRUE
, otherwise FALSE
.
This LONGLONG
to LPCVOID
is used to convert the numerical format of address into pointer form because the addresses are always stored in the pointer variables. The address of llBaseAddr (&llBaseAddr
) will give the address of the variable instead and it will produce different output.
Writing into Memory
This can be done by WriteProcessMemory()
function and the syntax is similar to the RPM method we have just looked. Instead of page size, that we have used earlier, in this case we have to use the length of the buffer we want to write, strlen(argv[3]) + 1
. This plus 1 is used to tell the function to include the NULL
character in the string.
Once this is successful, you can press any key on the victim process and see the difference. You can check the working of this code in the following video.
Code in Action 🚀
Once you have implemented the code as guided above, the exploit will work as shown below