My program, running elevated on Windows 10:
OpenProcess
ReadProcessMemory
import ctypes
from ctypes import wintypes
import win32process
import psutil
targetProcess = "notepad.exe"
PROCESS_ALL_ACCESS = 0x1F0FFF
BUFFER_SIZE = 200
def getpid():
for proc in psutil.process_iter():
if proc.name() == targetProcess:
return proc.pid
def main():
status = ctypes.windll.ntdll.RtlAdjustPrivilege(20, 1, 0, ctypes.byref(ctypes.c_bool()))
if(status == -1073741727):
print("STATUS_PRIVILEGE_NOT_HELD - A required privilege is not held by the client.")
hProcess = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, getpid()) # handle to process
lpBuffer = ctypes.create_string_buffer(BUFFER_SIZE) # Buffer we want to write results to
targetProcessBaseAddress = None # base address of the target processes entry module
modules = win32process.EnumProcessModules(hProcess) # Retreive modules of target process
for module in modules:
name = str(win32process.GetModuleFileNameEx(hProcess, module))
if targetProcess in name:
targetProcessBaseAddress = hex(module)
count = ctypes.c_ulong(0)
res = ctypes.windll.kernel32.ReadProcessMemory(hProcess, targetProcessBaseAddress, ctypes.byref(lpBuffer), BUFFER_SIZE, ctypes.byref(count))
if res == 0:
err = ctypes.windll.kernel32.GetLastError()
if (err == 299):
print("ERROR_PARTIAL_COPY - Only part of a ReadProcessMemory or WriteProcessMemory request was completed.")
else:
print(err)
else:
print(lpBuffer.raw)
if __name__ == '__main__':
main()
Above is done via python3.8 using the native ctypes
library.
I'm expecting to see a hexdump or any data other than 0x00,0x00
.. but it seems my error is somewhere in the arguments provided to ReadProcessMemory
, which is assumed due to error 299 returned from GetLastError()
, which indicates:
"ERROR_PARTIAL_COPY - Only part of a ReadProcessMemory or WriteProcessMemory request was completed."
Not sure where I'm messing up, would be very grateful for suggestions and assistance!
ReadProcessMemory
second argument is a LPCVOID
(long pointer to const void*) but you're passing the result of hex
which returns a string (which then would translate to a pointer to string in ctypes context).
Follow @CristiFati comment and use ctypes argtypes and restype which would have spotted the problem immediately.
Do not use directly GetLastError
from the win32 API. The interpreter is free to call any Windows API during its life, thus when you call this API you don't know if it's the result from your script or an API that was called by the interpreter for its own purpose. For this, ctypes proposes a specific variable which caches the result in the form of ctypes.get_last_error
.
The best way to do that is to start your script with something like that:
import ctypes
# obtain kernel32 WinDLL ensuring that we want to cache the last error for each API call.
kernel32 = ctypes.WinDLL("kernel32", use_last_error = True)
# start prototyping your APIs
OpenProcess = kernel32.OpenProcess
OpenProcess.argtypes = [ ... ]
OpenProcess.restype = ...
# then call the api
res = OpenProcess( ... )
#ensure you check the result by calling the cached last error
if not res:
err = ctypes.get_last_error()
# you might also raise
raise ctypes.WinError(err)