Read Process Memory of a Process Does Not Return Everything

Read process memory of a process does not return everything

At least in my opinion, if you have an LPMODULEENTRY involved, you're probably starting in the wrong direction. I'd walk through the blocks of memory in the target process with VirtualQueryEx instead. This will give you a MEMORY_BASIC_INFORMATION about each block in that process. You can then use ReadProcessMemory and scan through the blocks to find what you're looking for.

Here's some old code I wrote to do roughly the same thing, but looking for a string rather than a pointer:

#include <iostream>
#include <vector>
#include <string>
#include <windows.h>
#include <algorithm>
#include <iterator>

template <class InIter1, class InIter2, class OutIter>
void find_all(unsigned char *base, InIter1 buf_start, InIter1 buf_end, InIter2 pat_start, InIter2 pat_end, OutIter res) {
for (InIter1 pos = buf_start;
buf_end!=(pos=std::search(pos, buf_end, pat_start, pat_end));
++pos)
{
*res++ = base+(pos-buf_start);
}
}

template <class outIter>
void find_locs(HANDLE process, std::string const &pattern, outIter output) {

unsigned char *p = NULL;
MEMORY_BASIC_INFORMATION info;

for ( p = NULL;
VirtualQueryEx(process, p, &info, sizeof(info)) == sizeof(info);
p += info.RegionSize )
{
std::vector<char> buffer;

if (info.State == MEM_COMMIT &&
(info.Type == MEM_MAPPED || info.Type == MEM_PRIVATE))
{
SIZE_T bytes_read;
buffer.resize(info.RegionSize);
ReadProcessMemory(process, p, &buffer[0], info.RegionSize, &bytes_read);
buffer.resize(bytes_read);
find_all(p, buffer.begin(), buffer.end(), pattern.begin(), pattern.end(), output);
}
}
}

int main(int argc, char **argv) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <process ID> <pattern>", argv[0]);
return 1;
}

int pid;
sscanf(argv[1], "%i", &pid);

std::string pattern(argv[2]);

HANDLE process = OpenProcess(
PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
false,
pid);

find_locs(process, pattern,
std::ostream_iterator<void *>(std::cout, "\n"));

return 0;
}

Process memory reading always returns 0

It seems like you are reading the value of an int from the process, but saving it as a float. Try to change it to sizeof(float). Also, make address LPVOID, instead of casting it to a void pointer.

If it still fails, there is one more thing. The address you seem to be reading seems too small. A common address in the process will be like 0x7ff6d91c0000.

Also, you don't need <Process.h>, and the last address of ReadProcessMemory should be nullptr instead of 0.

ReadProcessMemory returns nothing

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

Reading a process memory

You are attempting to read address 0 in the target process. That will always fail. You need to read from an address which is meaningful in the virtual address space of the target process.

Note that in order to call ReadProcessMemory you only need PROCESS_VM_READ. That's not the problem here, but I thought I would point it out for sake of completeness.

Reading living process memory without interrupting it

For process 1234 you can get its memory map by reading sequentially /proc/1234/maps (a textual pseudo-file) and read the virtual memory by e.g. read(2)-ing or mmap(2)-ing appropriate segments of the /proc/1234/mem sparse pseudo-file.

However, I believe you cannot avoid some kind of synchronization (perhaps with ptrace(2), as gdb does), since the process 1234 can (and does) alter its address space at any time (with mmap & related syscalls).

The situation is different if the monitored process 1234 is not arbitrary, but if you could improve it to communicate somehow with the monitoring process.

I'm not sure to understand why do you ask this. And gdb is able to watch some location without stopping the process.



Related Topics



Leave a reply



Submit