Introduction
As with any internal cheat, you will need to inject a DLL into the target process. Since we cannot compile a Python program into a DLL, we will inject the interpreter directly into our victim app and later call its functions.
Thankfully, Pymem has everything we need.
Reading & modifying memory from the current process
The best way to achieve this is by using cast from ctypes. I've made a little example to show how it works:
from ctypes import c_int, addressof, cast, POINTER
# Create an integer variable x with the value 10
x = c_int(10)
# Get the memory address of the integer variable x
xAddr = addressof(x)
# Print the value and memory address of x
print(f"""
Value of x: {x.value}
Address of x: {hex(xAddr)}
""")
# Create a pointer that points to the memory address of x
pointer = cast(xAddr, POINTER(c_int))
# Update the value at the memory address pointed by the pointer to 99
pointer.contents.value = 99
# Print the new value of x, which should reflect the change made through the pointer
print(f"New value of x: {x.value}") # This will show 99
The same logic will be applied after we inject the interpreter.
Injecting the interpreter
As I previously said, Pymem has everything we need:
from pymem import Pymem
# Create a Pymem instance for a target process ('dummyapp_x64.exe')
pm = Pymem('dummyapp_x64.exe')
# Inject the Python interpreter into the target process
pm.inject_python_interpreter()
This will allow us to inject Python code into the victim app using pm.inject_python_shellcode
Putting everything together
from pymem import Pymem
pm = Pymem('dummyapp_x64.exe')
pm.inject_python_interpreter()
shellcode = """
from ctypes import c_int, cast, POINTER
xAddr = 0x14FED0
pointer = cast(xAddr, POINTER(c_int))
pointer.contents.value = 99
"""
pm.inject_python_shellcode(shellcode)
This code snippet should inject the Python DLL into the dummyapp_x64.exe process & execute our Python shellcode.
int xAddr = 0x14FED0;
int* pointer = reinterpret_cast<int*>(xAddr);
*pointer = 99;
Injecting that Python shellcode is like having this C++ code inside a DLL that's injected in the process.
... or this Rust code:
let xaddr: i32 = 0x14FED0;
let pointer: *mut i32 = xaddr as *mut i32;
unsafe {
*pointer = 99;
}
To prove its functionality, I've made and ran a simple app in C++ that outputs the value of an integer (X) followed by its address.
while (true){
cout << "Value of X: " << X << "\n";
cout << "Address of X: " << &X << "\n\n"; //0x14FED0
_getch();
}
After running the Python code, the value of X was changed to 99.
Therefore the PoC works.
Limitations
If your Python is x64, you will not be able to make an internal cheat for an x86 process, and vice-versa.
You will not be able to do all the things you could have done in a lower level programming language.
Lack of speed
As of 28.12.2023, this implementation does not seem to work on Python 3.12.x, due to the Pymem module not being updated.
That being said
"Python has a place and purpose and it is not used for internals due to the level of programming it is."
If you want a good internal cheat, I recommend learning C++, C or Rust.