pythonctypesreadprocessmemoryopenprocess

ReadProcessMemory returns nothing


I was following this Stackoverflow question here. I'm trying to read the data stored in notepad.exe memory space. But my get_data function seems to return nothing. I have some text stored in notepad, i would like to retrieve that text from RAM and store it in a variable in Python. This is the code:

import os
from ctypes import *
from ctypes.wintypes import *

def get_pid(exe_name):
    x = os.popen('tasklist /FI "ImageName eq '+process_name+'"').read()
    if not x.find("No tasks are running") >= 0:
        return int(list(filter(None, x[x.find(process_name):-1].split(" ")))[1])
    return -1

def get_data(PROCESS_ID, PROCESS_HEADER_ADDR, STRLEN=255, PROCESS_VM_READ=0x0010):
    k32 = WinDLL('kernel32')
    k32.OpenProcess.argtypes = DWORD,BOOL,DWORD
    k32.OpenProcess.restype = HANDLE
    k32.ReadProcessMemory.argtypes = HANDLE,LPVOID,LPVOID,c_size_t,POINTER(c_size_t)
    k32.ReadProcessMemory.restype = BOOL

    process = k32.OpenProcess(PROCESS_VM_READ, 0, PROCESS_ID)
    buf = create_string_buffer(STRLEN)
    s = c_size_t()
    if k32.ReadProcessMemory(process, PROCESS_HEADER_ADDR, buf, STRLEN, byref(s)):
        return (s.value,buf.raw)


process_name = "notepad.exe"
pid = get_pid(process_name)
process_header_addr = 0x7FF79A1E0000 # address from VMMap

data = get_data(pid, process_header_addr)

when i run this code, there is no data it just prints nothing:

>>> print(data)
None
>>> 

how can I retrieve the data?


Solution

  • [MS.Docs]: ReadProcessMemory function states:

    If the function fails, the return value is 0 (zero). To get extended error information, call GetLastError.

    Here's a small example.

    code00.py:

    #!/usr/bin/env python
    
    import sys
    import ctypes as ct
    from ctypes import wintypes as wt
    
    
    PROCESS_VM_READ = 0x0010
    
    
    def main(*argv):
        kernel32 = ct.WinDLL("kernel32")
    
        OpenProcess = kernel32.OpenProcess
        OpenProcess.argtypes = [wt.DWORD, wt.BOOL, wt.DWORD]
        OpenProcess.restype = wt.HANDLE
    
        ReadProcessMemory = kernel32.ReadProcessMemory
        ReadProcessMemory.argtypes = [wt.HANDLE, wt.LPCVOID, wt.LPVOID, ct.c_size_t, ct.POINTER(ct.c_size_t)]
        ReadProcessMemory.restype = wt.BOOL
    
        GetLastError = kernel32.GetLastError
        GetLastError.argtypes = []
        GetLastError.restype = wt.DWORD
    
        CloseHandle = kernel32.CloseHandle
        CloseHandle.argtypes = [wt.HANDLE]
        CloseHandle.restype = wt.BOOL
    
        np_pid = 34376  # Got it from a process monitoring tool
        np = OpenProcess(PROCESS_VM_READ, 0, np_pid)
        if not np:
            print("OpenProcess failed: {0:d}".format(GetLastError()))
            return
    
        buf_len = 0x0F # 0xFF  # Lower value for display purposes
        buf = ct.create_string_buffer(buf_len)
        read = ct.c_size_t()
        addr = 0x00001CF26F20000  # Got a readable address from VMMap as well, but I don't know the one where the actual text is stored
    
        res = ReadProcessMemory(np, addr, buf, buf_len, ct.byref(read))
        if res:
            print("Read ({0:d} bytes) from process ({1:d}) address 0x{2:016X}:".format(read.value, np_pid, addr))
            text = ""
            for i in range(read.value):
                text += " 0x{0:02X}".format(ord(buf[i]))
            print(text)
        else:
            print("ReadProcessMemory failed: {0:d}".format(GetLastError()))
    
        CloseHandle(np)
    
    
    if __name__ == "__main__":
        print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        main(*sys.argv[1:])
        print("\nDone.")
    

    Output:

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q063273381]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py
    Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    Read (15 bytes) from process (34376) address 0x000001CF26F20000:
     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xC2 0x3B 0x78 0x62 0xE6 0xFA 0x00
    
    Done.
    


    Update #0

    I don't know how Notepad organizes its memory internally. I can assume that the text is stored in a buffer (or maybe more, could be one per line, ...) which should reside in the heap area. But where exactly I can't say. You could inspect the process memory using a tool (I know that CheatEngine can do that) do a match between the memory contents and the text, and get that address, and paste it in the code, but I think that would:


    All in all, I don't think this is the way to go. You could search for alternatives, like using WinAPIs to send messages (maybe WM_GETTEXT) to the Notepad window to get the text. I don't know exactly how to do it, but I remember I was able to programmatically insert characters in Notepad using WM_CHAR.
    Or you could send a Ctrl + A, Ctrl + C, and then get the clipboard contents.