This code (the archive contains the Perl script and a Windows EXE "compiled" using Perl2Exe...be sure to keep the included DLL with the EXE at all times) makes use of some of the RAM dump file parsing code from tools like lsproc and lspd, as well as the code from my File::ReadPE module. Like the other tools, this one relies on the output of lsproc for it's input...specifically, the offset to the process in question. It uses pretty much the same method as Andreas mentioned in his first blog entry on reassembling binaries, for no other reason than it just makes sense. After all, why not use the "map" provided to you in the PE headers to reassemble the executable image file.
Let's take a look at an example, focusing on the same process that Andreas looked at...dd.exe with a PID of 284, from the first memory dump from the DFRWS 2005 Memory Challenge. The output of lsproc gives us:
Proc 1112 284 dd.exe 0x0414dd60 Sun Jun 5 14:53:42 2005
Very cool. So, using the offset to the process within the dump file, we can then launch lspi with the following command line:
C:\Perl>lspi.pl d:\hacking\dfrws-mem1.dmp 0x0414dd60
What we get as output is:
lspi - list Windows 2000 process image (v.0.4 - 20060721)
Process Name : dd.exe
PID : 284
DTB : 0x01d9e000
PEB : 0x7ffdf000 (0x02c2d000)
ImgBaseAddr : 0x00400000 (0x00fee000)
e_lfanew = 0xe8
NT Header = 0x4550
Reading the Image File Header
Sections = 4
Opt Header Size = 0x000000e0 (224 bytes)
Machine = IMAGE_FILE_MACHINE_I860
Reading the Image Optional Header
Opt Header Magic = 0x10b
Subsystem : IMAGE_SUBSYSTEM_WINDOWS_CUI
Entry Pt Addr : 0x00006bda
Image Base : 0x00400000
File Align : 0x00001000
Reading the Image Data Directory information
Data Directory RVA Size
-------------- --- ----
ResourceTable 0x0000d000 0x00000430
DebugTable 0x00000000 0x00000000
BaseRelocTable 0x00000000 0x00000000
DelayImportDesc 0x0000af7c 0x000000a0
TLSTable 0x00000000 0x00000000
GlobalPtrReg 0x00000000 0x00000000
ArchSpecific 0x00000000 0x00000000
CLIHeader 0x00000000 0x00000000
LoadConfigTable 0x00000000 0x00000000
ExceptionTable 0x00000000 0x00000000
ImportTable 0x0000b25c 0x000000a0
unused 0x00000000 0x00000000
BoundImportTable 0x00000000 0x00000000
ExportTable 0x00000000 0x00000000
CertificateTable 0x00000000 0x00000000
IAT 0x00007000 0x00000210
Reading Image Section Header information
Name Virt Sz Virt Addr rData Ofs rData Sz Char
---- ------- --------- --------- -------- ----
.text 0x00005ee0 0x00001000 0x00001000 0x00006000 0x60000020
.data 0x000002fc 0x0000c000 0x0000c000 0x00001000 0xc0000040
.rsrc 0x00000430 0x0000d000 0x0000d000 0x00001000 0x40000040
.rdata 0x00004cfa 0x00007000 0x00007000 0x00005000 0x40000040
Reassembling image file into dd.exe.img
Bytes written = 57344
New file size = 57344
Most of this is the PE header being parsed and displayed.
Now, a couple of caveats...first, this code ONLY works for RAM dumps from Windows 2000 systems (yes, I have been getting a lot of questions about that). Second, this does not take PAE into account. Nor does it take special cases into account, such as UPX compressed executables.
However, the code does check for things like pages that are paged out to the pagefile. Sounds kind of circular, I know, but basically, the pages in RAM that aren't being used can be paged out to pagefile.sys...since we're just dealing with a memory dump, we're assuming at this point that the pages in the pagefile aren't available.
Also, you will notice that the resulting executable image, once renamed to an .exe, does not run the way the original does. This is due to the fact that once the various sections are loaded into memory, some of the values in the different sections will change as the code is being run.
So, how is something like this useful? Well, if you're into malware analysis, or if you're performing incident response against an as-yet-unknown bit of code, this would be helpful.
Of course, there are things that need to be added to this code, as it performs only parsing. For example, some actual analysis would be in order...like analyzing the code at the entry point address, performing some analysis of the sections, extracting the import address (or name) tables, etc.
As always, comments are welcome...