Exetools  

Go Back   Exetools > General > General Discussion

Notices

Reply
 
Thread Tools Display Modes
  #1  
Old 01-05-2007, 08:02
Sarge
 
Posts: n/a
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
Reply With Quote
  #2  
Old 01-06-2007, 02:28
Mkz Mkz is offline
Friend
 
Join Date: Jan 2002
Posts: 98
Rept. Given: 0
Rept. Rcvd 2 Times in 2 Posts
Thanks Given: 5
Thanks Rcvd at 25 Times in 17 Posts
Mkz Reputation: 2
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
Reply With Quote
  #3  
Old 01-06-2007, 03:30
IWarez IWarez is offline
Friend
 
Join Date: Jul 2003
Posts: 41
Rept. Given: 7
Rept. Rcvd 6 Times in 2 Posts
Thanks Given: 1
Thanks Rcvd at 0 Times in 0 Posts
IWarez Reputation: 7
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.

Last edited by IWarez; 01-06-2007 at 03:34. Reason: Addition
Reply With Quote
  #4  
Old 01-06-2007, 03:47
JoeStewart
 
Posts: n/a
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.
Reply With Quote
  #5  
Old 01-06-2007, 08:21
Sarge
 
Posts: n/a
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
Reply With Quote
  #6  
Old 01-10-2007, 06:44
Sarge
 
Posts: n/a
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,
Reply With Quote
  #7  
Old 01-13-2007, 22:14
Sarge
 
Posts: n/a
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
Reply With Quote
  #8  
Old 01-26-2007, 13:05
Ghandi2006 Ghandi2006 is offline
VIP
 
Join Date: Jan 2006
Posts: 110
Rept. Given: 23
Rept. Rcvd 39 Times in 26 Posts
Thanks Given: 0
Thanks Rcvd at 28 Times in 23 Posts
Ghandi2006 Reputation: 39
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

Last edited by Ghandi2006; 01-26-2007 at 13:09.
Reply With Quote
  #9  
Old 01-27-2007, 01:44
deroko's Avatar
deroko deroko is offline
cr4zyserb
 
Join Date: Nov 2005
Posts: 217
Rept. Given: 13
Rept. Rcvd 30 Times in 14 Posts
Thanks Given: 7
Thanks Rcvd at 33 Times in 16 Posts
deroko Reputation: 30
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
__________________
http://accessroot.com
Reply With Quote
  #10  
Old 01-27-2007, 08:49
Maximus Maximus is offline
Friend
 
Join Date: Nov 2005
Posts: 39
Rept. Given: 0
Rept. Rcvd 0 Times in 0 Posts
Thanks Given: 0
Thanks Rcvd at 1 Time in 1 Post
Maximus Reputation: 0
Well, might also be a good idea to call it also for the RIP event, who knows...
Reply With Quote
  #11  
Old 01-27-2007, 22:35
deroko's Avatar
deroko deroko is offline
cr4zyserb
 
Join Date: Nov 2005
Posts: 217
Rept. Given: 13
Rept. Rcvd 30 Times in 14 Posts
Thanks Given: 7
Thanks Rcvd at 33 Times in 16 Posts
deroko Reputation: 30
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
__________________
http://accessroot.com
Reply With Quote
  #12  
Old 02-02-2007, 09:58
Sarge
 
Posts: n/a
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

Last edited by Sarge; 02-02-2007 at 10:02.
Reply With Quote
  #13  
Old 03-03-2007, 02:54
Ghandi2006 Ghandi2006 is offline
VIP
 
Join Date: Jan 2006
Posts: 110
Rept. Given: 23
Rept. Rcvd 39 Times in 26 Posts
Thanks Given: 0
Thanks Rcvd at 28 Times in 23 Posts
Ghandi2006 Reputation: 39
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
Reply With Quote
  #14  
Old 03-16-2007, 20:50
Sarge
 
Posts: n/a
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
Reply With Quote
  #15  
Old 03-17-2007, 02:57
evlncrn8 evlncrn8 is offline
VIP
 
Join Date: Sep 2005
Posts: 179
Rept. Given: 36
Rept. Rcvd 54 Times in 24 Posts
Thanks Given: 50
Thanks Rcvd at 118 Times in 70 Posts
evlncrn8 Reputation: 54
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
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



All times are GMT +8. The time now is 20:26.


Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX, chessgod101
( Since 1998 )