Exetools

Exetools (https://forum.exetools.com/index.php)
-   General Discussion (https://forum.exetools.com/forumdisplay.php?f=2)
-   -   ReadProcessMemory etc in VB6 (https://forum.exetools.com/showthread.php?t=10592)

Sarge 01-05-2007 08:02

ReadProcessMemory etc in VB6
 
Ok, gang, need some help here. I want to make ProgramA take a memory snapshot of ProgramB after it is loaded into memory,
and before it runs. I am doing this in VB6.

To do this, I have determined I should probably use CreateProcess(?). Two of the parameters for CreateProcess include
STARTUPINFO and PROCESSINFORMATION. I have them properly TYPEd and DIMmed; the CreateProcess call seems to work, as
the target program launches, and the STARTUPINFO and PROCESSINFORMATION structures have data in them.

I also think I need to use ReadProcessMemory(?). This function requires a handle to the target program and the starting
address to read from, among others. I believe one of the items in PROCESSINFORMATION, called HProcess, will do for the
handle(?). But I am not sure what starting address to use. Isn't it true that, while a program has a preferred load address,
it may be relocated by windows? So, I need to determine where the real starting address of the target program is. I don't
see any item in either STARTUPINFO or PROCESSINFORMATION that would seem to indicate that.

So, I think I may need to use VirtualQueryEx before ReadProcessMemory(?). VirtualQueryEx requires MEMORYBASICINFORMATION
as a paramter. I have that properly TYPEd and DIMmed. It requires a handle to the target program, and again I am using
HProcess. It also requires an address. But what address? This cannot be the load address of the target program, as I don't know it yet. The definition of this parameter is "A pointer to the base address of the region
of pages to be queried". I'm not sure this is what I want (Jeez, I'm not even sure what it means). But if it's not, what do I need to do to get the real starting address of my target program? Or is this the wrong path? (I know the function works using a dummy address, as the return value is
correct and the MEMORYBASICINFORMATION structure has data in it.)

I have Googled a lot (a lot!) of C and C++ code that seem to do this, but apparently I'm not knowledgeable enough to
translate it well. And, little of the sample code in MSDN is in VB. Even most of the posts on ReadProcessMemory here on
this board are C/C++ based.

I know that I have to close/terminate the target when I'm done.

So, to recap:
1. CreateProcess
2. VirtualQueryEx
3. ReadProcessMemory
4. Terminate.

Here's my code snippet. Assume that the declarations, structures, etc, are correct.

*Start the target program
y = CreateProcess(Nil, "C:\Documents and Settings\Owner\Desktop\Project1.exe", ByVal 0&, ByVal 0&, 1&, NORMAL_PRIORITY_CLASS, ByVal 0&, Nil, SUInfo, ProcInfo)

*Get the load address
y = VirtualQueryEx(ProcInfo.hProcess, &h0, MBI, Len(MBI))

*Make a buffer; MySize is known to be the correct length of the target program
buffer = String(MySize, 0)

*Read the target program memory
y = ReadProcessMemory(ProcInfo.hProcess, ByVal MBI.BaseAddress, buffer, MySize, count)

*End the target program
y = TerminateProcess(ProcInfo.hProcess, 0&)
y = CloseHandle(ProcInfo.hThread)
y = CloseHandle(ProcInfo.hProcess)

I think/hope I'm close, and am just missing something small.

Thanks all.

Sarge

Mkz 01-06-2007 02:28

Just off the top of my head, could it be that not all the memory from the target program is paged in?
IIRC, the ReadProcessMemory fails if even a small part of the memory you requested was not available. Also, if the memory hasn't been paged in yet (because the execution of the target program hasn't triggered the page in), the ReadProcessMemory won't force the page in, and will fail because the memory is not available.

So if you have for instance these memory areas for the target:
400000(+3FF) - Image signature
401000(+FFFF) - .text (code)

a) If you request from 400000 to the end of .text, it will always fail because there's a hole before the start of .text

b) Even if you only request the ".text" area, it might fail because likely there are areas not paged in yet.

I think that a) you must solve yourself (don't ask for a whole contiguous area but for individual blocks), and for b) one of the options is to inject code to run in the target address space, that will force the memory to page in (by reading one address in each page?), after that in can probably be ReadProcessMemory'ed

IWarez 01-06-2007 03:30

I don't know if you want the program to run, but you might want to create the process suspended. That way windows will create the process but won't run it yet. What exactly to you want to achieve? I've done some sick things with VB6 and might be able to help you but I need to know more of what you are trying to do.

Oh by the way: www.planetsourcecode.com/vb does have some code that might help you. There are a lot of samples in vb6 that show how to use readprocessmemory/openprocess/etc.

JoeStewart 01-06-2007 03:47

The loaded image's base address can be found in the PEB. You can get the PEB's base address by calling NtQueryInformationProcess with the PROCESS_BASIC_INFORMATION constant. The information you want is a 32-bit value stored at offset 8 from the PEB base address. There are plenty of examples of this in C, not too many in VB I can find for you. Have a look at:

http://www.vbstreets.ru/VB/Articles/66404.aspx

You'll want to follow the same technique, calling NtQueryInformationProcess to get the PEB base, then use ReadProcessMemory to read 4 bytes from PBI.PebBaseAddress + 8 and that's your base address to read the process image from virtual memory.

Sarge 01-06-2007 08:21

Thanks all. I have made progress, sort of. The problem was VB's good ol' "ByVal-vs-ByRef-Parameter-Passing-Trick".
So, I am now reading memory, sort of. But I still have found two problems remaining:

1. The ReadProcessMemory function seems to choke on a read of more than $4000 or so bytes. A small program works ok, but
I'll probably have to read a larger program in $4000 chunks.

2. I'm using CreateProcess to spawn the target. If I set the flags to NormalPriority, I can get a good memory dump,
but the target becomes visible. If I use CreateSuspended, the target does not become visible, but then I get a bad
dump. In this case, a good dump is qualified by having the target program load into memory with it's linker data
(pointers to function addresses, DLL addresses, etc) included and all the address relocations performed. The bad dump
is just a copy of the target program on disk. Any ideas on what flags to use that will guarantee fully linked
in-memory data while not showing the target program? Or is CreateProcess even the right function?

So, what do I want to do? In VB, ProgramA should invisibly load/link ProgramB, so that I can get back ProgramB's
in-memory data. This is, I think, called a ProcessDump, or sometimes a MemoryDump. There are a number of C/C++ based
utilities out there, and even some with source code, but most expect the target program to be already active; I am not
versed enough in C/C++ to make the translation to VB...never mind that I have to make the target program invisible.

I will check out the suggested link and the NT function.

Thanks
Sarge

Sarge 01-10-2007 06:44

Update
 
Ok, what's new?

I'm now spawning programs as processes using CreateProcess. I can run them in debug mode as the debugee. I can read the process memory and take the snapshot (but, so far, only of single thread models).

My current stumbling block is TerminateProcess. How do I do this reliably?
Must I terminate each thread that the debuggee spawns? Do I close the process handle before or after I terminate the process? etc, etc. So far, nothing I do will remove the process from the system (TerminateProcess has a note about this) unless I end my VB IDE session.

More work needed..any help appreciated.

Thanks
Sarge

PS: the link http://www.vbstreets.ru/VB/Articles/66404.aspx comes up in Russian and won't translate,

Sarge 01-13-2007 22:14

Success.

I found that you can only ReadProcessMemory in small enough chunks, depending upon the limitations of the function and the size of the OS's memory blocks.

I finally found that I can use TerminateProcess if I carefully control WHEN I do so. This appears to take care of multi-threads, although further testing is needed.

Thanks to all for help and suggestions. Now, what do I do with it....?

Sarge

Ghandi2006 01-26-2007 13:05

ReadProcessMemory
 
The ReadProcessMemory API isn't limited to small chunks of memory.

You just need to ensure that:

The buffer you are passing to the call is large enough to receive the data.
You are asking for it to read from a valid address.
The region(s) you are attempting to read from have the correct permissions.

To ensure the permissions are correct, use VirtualProtectEx (not available in Win 9x) and set them to PAGE_READWRITE before calling ReadProcessMemory, then restore the original permissions afterward.

Some easy ways to find the SizeOfImage (which is what you want for allocating the correct buffer size and also for reading the correct amount of bytes) are:

1. Using the DWORD value we can find in the PE Header, aptly named 'SizeOfImage'.


2. Using the CreateToolHelp32Snapshot API, then Module32First to get information about the process. We can also use the Module32Next to build a complete list of modules (dll's and such) loaded & use it for import rebuilding.


3. Use the DWORD value from the PE Header, called 'ImageBase' as a base address & use VirtualQueryEx (not available in Win 9x) to build a 'map' of the processes memory. (You'll need to handle the logic that decides where the process memory ends if this wont return the complete image size though.)

4. Use the VirtualAddress & VirtualSize values from the last section header found in the Section Table, add them together and there's your image size. If you want, you can pay attention to the alignment and round it up, but be careful that you don't overestimate, otherwise ReadProcessMemory may fail with an ERROR_PARTIAL_COPY result.



About TerminateProcess, what do you mean 'if i am careful'?

If you are referring to the way the process doesn't appear to die after calling TerminateProcess, are you detaching from the process at all?

I found that if i launched a process from within my own debug loaders, it wouldn't disappear from the running processes list until i had either exited the debugger also, or (after getting frustrated so many times & experimanting) calling DebugActiveProcessStop (not available on Win 9x) before calling TerminateProcess. Once i learned of this API, no more termination problems...

Ghandi

deroko 01-27-2007 01:44

Quote:

Originally Posted by Ghandi2006
I found that if i launched a process from within my own debug loaders, it wouldn't disappear from the running processes list until i had either exited the debugger also, or (after getting frustrated so many times & experimanting) calling DebugActiveProcessStop (not available on Win 9x) before calling TerminateProcess. Once i learned of this API, no more termination problems...

You have to call ContinueDebugEvent with DBG_CONTINUE one more time when EXIT_DEBUG_EVENT occurs, then process will exit w/o a problem, and no need to use TerminateProcess and DebugActiveProcessStop :)

Maximus 01-27-2007 08:49

Well, might also be a good idea to call it also for the RIP event, who knows...

deroko 01-27-2007 22:35

yep I think it should be called on each event :) but I have never seen RIP event, so I have no idea how and why it is generated :(

Sarge 02-02-2007 09:58

Two items of confusion:
1. Haven't explored this one: DebugActiveProcessStop. I'll look into it. But if I shouldn't use that, or the Terminate function, how will I stop the target program? And would I still need to use ExitDebugEvent? Or does " calling DebugActiveProcessStop (not available on Win 9x) before calling TerminateProcess" really, really work?

2. I use the image size value that is somewhere in the PE header; it probably is the SizeOfImage data. (But if it's more than 4k, I limit it to 4k.) But if I can read, for example, 20k in 4k chunks, doesn't that imply that I have permissions for all 20k? So why then can't I just read in one 20k chunk?

I will keep on this, and I want to do the due diligence that is expected on my part.
These hints are appreciated.
Thanks
Sarge

Ghandi2006 03-03-2007 02:54

More info?
 
Thanks for the heads up Deroko, i wondered if i was doing it incorrectly... Sorry for the long delay on the reply mate.

Quote:

You have to call ContinueDebugEvent with DBG_CONTINUE one more time when EXIT_DEBUG_EVENT occurs, then process will exit w/o a problem, and no need to use TerminateProcess and DebugActiveProcessStop
Sarge, im pretty sure Deroko is saying that DebugActiveProcessStop isn't required at all. The process is in a debug loop and part of your loop needs to address the debug message received when a process exits and ALLOW IT TO EXIT... What i get from this & what i have read concerning this is:

When the debuggee tries to exit, the debugger is notified and presented with the opportunity to handle events. Your program doesn't need to do this though, there is already a handler waiting to receive it's own notification the process wants to exit, it can be either one the system has installed or one the software author has installed for whatever reasons.

All that is needed then, is to tell the process it is okay to continue to this handler by setting the dwContinue flag to DBG_CONTINUE and calling ContinueDebugEvent. This send the process to the default handler.

i could be totally wrong too, thats just what i interpreted some information into...

Happy Reversing,
Ghandi

Sarge 03-16-2007 20:50

Ok, got that. But it appears that, that method expects the debuggee to be exited somehow (more specifically, to be told to exit) by the user. My need is to spawn the debugee, load it into memory, read the memory, then exit/end the debugee, all without the use knowning it has happened. This tells me I should use the "Debug...Stop".

I have had to put that project aside for a few weeks, but I'll be getting back to it soon. At that time, I'll finally look at using the Debug...Stop API.

Thanks, guys...this looks really promising.

Sarge

evlncrn8 03-17-2007 02:57

Quote:

Originally Posted by Mkz
So if you have for instance these memory areas for the target:
400000(+3FF) - Image signature
401000(+FFFF) - .text (code)

a) If you request from 400000 to the end of .text, it will always fail because there's a hole before the start of .text

erm, im not sure you're right about that actually, if the process
has started there wont be a hole, the hole is only 'apparent' in the exe file itself, not when its loaded into memory

as for the 400000 being available, it should be considering you've done a createprocess , so you can pretty much guarantee that area is 'available' if the createprocess was successful

Mkz 03-20-2007 02:10

Quote:

Originally Posted by evlncrn8
erm, im not sure you're right about that actually, if the process
has started there wont be a hole, the hole is only 'apparent' in the exe file itself, not when its loaded into memory

as for the 400000 being available, it should be considering you've done a createprocess , so you can pretty much guarantee that area is 'available' if the createprocess was successful

At 400500 there is indeed a hole in memory, it is not mapped to anything. If you request data from this address, you won't get nothing for sure.
The same happens if you request from 400000 to 40FFFF (for instance) in one go, since the memory is not accessible - not all of it anyway.
Even if you keep below the 4K limit that Sarge mentioned, say 400000-400FFF, i'd say you still get an error, since there is valid data only from 400000 to 4003FF. That's what I meant by "hole" - unmapped memory from 400400 to 400FFF.

I must state that I haven't used this API in a long long time, these are all suppositions on my part :)

evlncrn8 03-20-2007 23:26

total suppositions, its not a hole, the sections will be aligned by section alignment in memory
so 400000->401000 is DEFINATELY available
hole in memory in the exe maybe, but not when mapped into memory.. try it..

suppositions help noone reading the thread...

all the memory is available in the exe, typically from the image base->image base + size of image, the sections are all linear, and padded until section alignment, unless the sections have attributes like PAGE_GUARD etc, which can be set from virtualprotect..

however on a normal exe, you can do the straight dump no problems
4k limit is bullshit too.. 4k is just the size of 1 memory page (alignment) - 4096 bytes
readprocessmemory/writeprocessmemory are definately capable of writing more than 4k in 1 go...

JuneMouse 03-21-2007 16:36

1 Attachment(s)
i ve read this thread several times earlier and mostly refrained from posting anything because im not that much comfortable with the language visual basic

in genearal Readprocessmemeory does not have any limits on reading only 4k and increments

if you simply want to check how big readprocessmemory can go
load ollydbg on ollydbg set a conditional breakpoint on ReadProcessmemory and log all function arguments you can see it reading
Code:

004AF16E  CALL to ReadProcessMemory from OLLYDBG.0046142B
            hProcess = 00000084 (window)
            pBaseAddress = 401000
            Buffer = 02D60020
            BytesToRead = AF000 (716800.)
            pBytesRead = NULL

i have attached an excel sheet that contains the log of all readprocessmemory address
and you can see ollydbg accessing address at ox400200 with sizes as big as ollydbg .txt section etc etc

Code:

address        data        crap
        pBaseAddress = 400200       
        pBaseAddress = 400200       
        pBaseAddress = 400200       
        pBaseAddress = 400200       
        pBaseAddress = 400200       
        pBaseAddress = 400200       
        pBaseAddress = 400200       
        pBaseAddress = 400200       
        pBaseAddress = 400200       
        pBaseAddress = 400200       
        pBaseAddress = 400200       

address        data        crap
        BytesToRead = 10CB (4299.)


Mkz 03-21-2007 23:32

Quote:

Originally Posted by evlncrn8
total suppositions, its not a hole, the sections will be aligned by section alignment in memory
so 400000->401000 is DEFINATELY available
hole in memory in the exe maybe, but not when mapped into memory.. try it..

I checked the memory map in OllyDbg and you are right. Indeed the whole 0x1000 page of the header is available. Also, since the next section starts immediately after (at 401000) there is no hole contrary to what I assumed. :o
Maybe if the header was 500 long, and the .text started at 402000, there would still be a whole - not from 400500 to 401FFF as I thought, but from 401000 to 401FFF, since the header takes up an entire page and not just 500.

I took a look at a couple of windows executables and all of them have the sections contiguous to each other, so the memory from the start of the first section to the end of the last one should all be available - excluding any page in/out issues.

evlncrn8 03-22-2007 05:41

there wont be a hole, the executable format, is section based, of an entire chunk
if the data does not reach the boundary, the 'hole' is zero filled, this is handled by the pe loader, the entire executable image should be entirely accessible (section characteristics of course setting r/w/e flags) when loaded into memory..
the holes you're talking about are typically in the exe file image, thats totally different compared to the memory layout when the exe is actually loaded..
i've yet to see an exe with a 'hole' in the section range, it would probably be against the pe file specifications, as for the 'maybe if the header was 500 long', the data in the elfanew will contain the pointer to the pe portion, mz->pe portion will be mapped into memory for sure and pe->start of first section too, header size changing will not change the results in any way..

Mkz 03-22-2007 18:24

Quote:

Originally Posted by evlncrn8
i've yet to see an exe with a 'hole' in the section range, it would probably be against the pe file specifications

Once again you are correct. I tried editing the sections of notepad.exe so that the RVA of the last section did not start immediately after the end of the previous one, but one page ahead.
I also tried updating the size of data and checksum but the exe always came up invalid.

I guess I must learn to keep my mouth shut when I'm not sure of what I'm saying :o

evlncrn8 03-22-2007 20:43

well, we all live and learn eh :), i didnt really decide to 'attack' your posts, or teach you a lesson, im just trying to help things


All times are GMT +8. The time now is 16:43.

Powered by vBulletin® Version 3.8.8
Copyright ©2000 - 2026, vBulletin Solutions, Inc.
Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX