1

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?

  • You check none of the return values (e.g. for *NULL*). Read each function's documentation and in case of error call *GetLastError* to find out more details. – CristiFati Aug 05 '20 at 21:12

1 Answers1

0

[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:

  • Beat the very purpose of the script (as scripts are used for automation, to do the work instead of the user)
  • Not be reliable. A buffer is allocated with a specific length. If the user keeps typing stuff in Notepad, eventually that buffer will get full and (behind the scenes) it will be relocated which will (most likely) change its address


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.

CristiFati
  • 38,250
  • 9
  • 50
  • 87
  • I copied and pasted your code exactly into a new python file, then changed the VMMap address. But the code returns `ReadProcessMemory failed: 299`. What address are you using in VMMap, is it the header address where all the purple text is? https://i.snipboard.io/3JtmVi.jpg –  Aug 05 '20 at 22:25
  • As I stated I don't know where the text is actually stored, but in any case it's not the header that you tried (that's part of the *notepad.exe* image). – CristiFati Aug 06 '20 at 04:59