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
The Implementation of Std::Forward
What Is Wrong with Using Inline Functions
Cleaning Up an Stl List/Vector of Pointers
Netbeans 7.2 Shows "Unable to Resolve Identifier" , Although Build Is Successful
Templated Class Specialization Where Template Argument Is a Template
Accessing Protected Members of Superclass in C++ with Templates
Calling the Base Class Constructor from the Derived Class Constructor
How to Alpha Blend Rgba Unsigned Byte Color Fast
How to Download the Visual C++ Command Line Compiler Without Visual Studio
Casting via Void* Instead of Using Reinterpret_Cast
C++ Socket Server - Unable to Saturate Cpu
C++ Vector, What Happens Whenever It Expands/Reallocate on Stack
Use Createprocess to Run a Batch File