0

So I've been fighting through issue after issue with external process memory reading (reading the memory of processes I don't have access to.) My understanding of several things has changed, but there is one thing which I simply cannot get my head around.

The win32api function ReadProcessMemory() accepts several arguments, as seen here:

    public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
        [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);

I'm passing those arguments like this:

    public byte[] ReadBytes(IntPtr Handle, Int64 Address, uint BytesToRead)
    {
        IntPtr ptrBytesRead;
        byte[] buffer = new byte[BytesToRead];
        ReadProcessMemory(Handle, new IntPtr(Address), buffer, BytesToRead, out ptrBytesRead);
        return buffer;
    }

It was, until recently, my understanding that the Address listed here was the only thing really important to reading the memory, and that was what found me the correct value in memory. Unfortunately, that appears to be a load of tosh, and it seems that actually the handle controls which window I am interacting with. For example:

I am running 2 versions of the Process "Notepad.exe".

Each instance of the process has an integer in it, the first one contains the number 12345, the second contains 54321.

I'm looking to read that integer, and let's say (though I haven't confirmed this, so it may be untrue) memory address within that programs memory space is 0x1000.

If I run for example:

ReadProcessMemory(NP.Handle, NP.MainModule.BaseAddress + 0x1000, buffer, 32, bread);

That will read the process with that handle, at that base address added to that offset. However this code will read exactly the same value:

ReadProcessMemory(NP.Handle, NP2.MainModule.BaseAddress + 0x1000, buffer, 32, bread);

Note that NP2 is supposed to be the second notepad window, and NP is the first. On top of the above, this will read a different value (in spite of the address we're reading being the same as the first example):

ReadProcessMemory(NP2.Handle, NP.MainModule.BaseAddress + 0x1000, buffer, 32, bread);

Surely this means that the handle is controlling where the memory is read, and not the address, and that in fact the address is completely irrelevant to what I'm actually trying to do? Can anyone explain to me why that would be the case?

Apologies if this question is excessively specific, but this issue has been racking my brain for a long time now, and although I speak to numerous programmers on a daily basis, none of them have been able to (or perhaps it's more that they haven't been willing to) help me.

I am fully aware that this will only work for 2 running instances of the same exe, so it won't work if you're gonna read Firefox and Notepad (I think). I'm just wondering why it is the handle that makes this change, and not the address.

Thanks

XtrmJosh
  • 889
  • 2
  • 14
  • 33
  • 2
    As you're only reading 4k from the base address, I suspect you're reading the executable part of the memory which will be identical for both `notepad.exe` processes. Have you tried reading it for two different programs? It's perfectly feasible the value is the same at that location for both processes. – Steve Jun 05 '13 at 10:15
  • What do you mean with 'exactly the same value' ? Same answer when it shouldn't ? – H H Jun 05 '13 at 10:16
  • @Steve - I get what you're saying, I'm in fact not working with Notepad, but another program which I doubt anyone here will have, notepad was just used as an example. The offset I'm actually reading is part of an array of structs (in the instance where I noticed this "issue"), and the offset from the base is 0x553008). Thanks for the suggestion though. – XtrmJosh Jun 05 '13 at 10:21
  • @Henk Holterman - The values are the same. If you look at how I'm passing in the values for the base address, I wasn't aware how two processes could share a base address but have completely different values stored within their memory, that is probably the easier way to phrase the question. I would previously have expected that if I read the values in that way, the address to be read would control what value was returned, but actually the Handle controls which client I'm communicating with, and thus which value I get back... – XtrmJosh Jun 05 '13 at 10:22
  • 1
    The address is relative to the process, so you need both the process handle *and* the address to specify the exact memory location. The address is not an absolute offset into system memory. – Matthew Watson Jun 05 '13 at 10:24
  • @Matthew Watson - So what is the base address then? I always had it in my head that the base address is the place in memory where the program starts, offset from the start of the system memory (where 0x0 would be the very first byte of RAM, and if you had say 16GB of RAM 0x400000000 would be the last byte... – XtrmJosh Jun 05 '13 at 10:26
  • Have you considered the possibility, that BaseAdress is same, in both cases? I would guess that BaseAdress is actually offset from handle. That would explain shown behavior. – jnovacho Jun 05 '13 at 10:26
  • The BaseAddress is actually the same in both processes, that's exactly the issue. I don't get how that could be, with my understanding that the base address is an offset from the start of the system memory, and I'm reading an address which is a static offset from that address and getting two different values depending on the handle I'm passing. I strongly suspect Richards post below might contain the answer, so will read that now! – XtrmJosh Jun 05 '13 at 10:29
  • You are aware about _Virtual_ Memory, right? Each process has its own address space. – H H Jun 05 '13 at 10:29
  • I'm kinda of aware of virtual memory, this is the issue, what I'm trying to do isn't often done, thus is thinly documented. This is the only thing in programming I've ever been interested in enough to try to do anything with, so I didn't really start at the beginning, either! My understanding now (after the help of all of you guys and Richard, thanks for all of it by the way) is that the BaseAddress is the start of the dynamically allocated memory (allocated after startup), and the Handle is actually a pointer to the space which contains both the program itself and the programs virtual memory? – XtrmJosh Jun 05 '13 at 10:40
  • @XtrmJosh Have a read of this, it might be interesting: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366912%28v=vs.85%29.aspx and also here: http://www.tenouk.com/WinVirtualAddressSpace.html – Matthew Watson Jun 05 '13 at 10:43

1 Answers1

2

It was, until recently, my understanding that the Address listed here was the only thing really important to reading the memory, and that was what found me the correct value in memory. Unfortunately, that appears to be a load of tosh, and it seems that actually the handle controls which window I am interacting with.

In this paragraph there are multiple miss-understandings about what you are doing that I'm not really sure where to begin.

But here goes (assume this is incomplete):

reading the memory of processes I don't have access to

You can't.

If you do not have sufficient access to the target process Windows security will prevent you getting a handle to that process with the necessary rights. (If this were not the case there would be no security.)

However we'll assume the reading process is running with Debug or equivalent privilege that gives sufficient access (this is why, in part, such are known as "God Privileges" allowing holders to bypass security).

handle controls which window

You need a process handle, not a window handle: they're completely different things. Use OpenProcess to get a process handle from a process id, to read memory include PROCESS_VM_READ in the requested access rights.

They you need to make use of knowledge of the (virtual) memory layout of Windows processes to work out what addresses you'll need to read from. Remember that ASLR and 32 vs 64bit processes will change that memory layout. Additional the allocated address space is unlikely to be contiguous, so you cannot just read memory sequentially.

This is an advanced topic. And in the end there is almost always a better approaches (proper APIs, making use of SendMessage to request the content of controls, …) than directly reading process memory (remember a minor change to one DLL—eg. from a security patch—will shift things around).

Summary: find another, better, way.

EDIT: A few resources to understand memory in Windows:

  1. Mysteries of Memory Management Revealed,with Mark Russinovich (Part 1 of 2)
  2. Mysteries of Memory Management Revealed,with Mark Russinovich (Part 2 of 2)

    A two part talk (part 1: virtual memory, part 2: physical) that will also introduce some very useful tools for looking at memory and how it is organised.

  3. Read Windows Internals, Mark Russinovich et al. (not all will be relevant, but you will need to understand security in Win32 as well).

  4. Read Advanced Windows, Jeffrey Richter. I think this is out of print (my 3rd edition covers Win95/NT4) but is the only serious coverage of the information you will need to know to read process memory and interpret the results.

Richard
  • 106,783
  • 21
  • 203
  • 265
  • Highly appreciative of your response, but finding another, better way is not really feasible. As mentioned I don't have direct access to the program, and I don't know the developers well enough to request that they make any changes. The ReadProcessMemory function is almost perfect as I'm looking to interact with the program as little as possible, the main alternatives are probably injection or pixel interpretation, injection is a bit excessive for this purpose, and pixel reading would require some intense algorithms just to calculate words and what not... – XtrmJosh Jun 05 '13 at 10:37
  • @XtrmJosh: I've added some resources you can use as basic knowledge you need (one [SO] answer can only scratch the surface here). Also anything about writing Win32 debuggers is likely to be helpful (but start with the basics of memory management: eg. understanding why physical and process memory are almost completely unrelated). Getting to know the other developers *will be easier* (they may well be keen to help). – Richard Jun 05 '13 at 10:54