Reconstructing a Binary (1)

It is possible to reconstruct the program binary of process from a memory dump. This enables you to scan a binary for viruses even if it has been deleted from the disk. This article outlines the process.

Once again the first image from the DFRWS Memory Analysis Challenge shall serve as an example. In addition I'll use the 010 Editor and the template to dump a page directory.

Also you should feel familiar with the PE header found in Windows executables. Microsoft provides a description.

During this tutorial we're going to reconstruct the program "dd.exe" whose PID is 284. PTfinder gives you an offset of 0x414dd60 for the _EPROCESS structure . From there the pointer to the Process Environment Blocks (PEB) can be found at offset 0x1b0. It points to the virtual address 0x7ffdf000.

In order to convert this into a physical address (and an offset into the memory dump) you also need the Page Directory Base Address. PTfinder displays it in a row labeled "PDB", it's 0x01d9e000. Now point the editor to this offset, load the template and start it. The editor will build the Page Directory Entries (PDE) and Page Table Entries (PTE), which may take a while.

The virtual address of 0x7ffdf000 leads us to PDE 511 and its PTE 991, which willl point you to an offset of 0x2c2d000. Now, this is the PEB. And this is what it looks like in Windows 2000:

kd> dt _PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 SpareBool        : UChar
   +0x004 Mutant           : Ptr32 Void
   +0x008 ImageBaseAddress : Ptr32 Void
   +0x00c Ldr              : Ptr32 _PEB_LDR_DATA
   +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
   +0x014 SubSystemData    : Ptr32 Void
   +0x018 ProcessHeap      : Ptr32 Void
   +0x01c FastPebLock      : Ptr32 Void
   +0x020 FastPebLockRoutine : Ptr32 Void
   +0x024 FastPebUnlockRoutine : Ptr32 Void
   +0x028 EnvironmentUpdateCount : Uint4B
   +0x02c KernelCallbackTable : Ptr32 Void
   +0x030 SystemReserved   : [2] Uint4B
   +0x038 FreeList         : Ptr32 _PEB_FREE_BLOCK
   +0x03c TlsExpansionCounter : Uint4B
   +0x040 TlsBitmap        : Ptr32 Void
   +0x044 TlsBitmapBits    : [2] Uint4B
   +0x04c ReadOnlySharedMemoryBase : Ptr32 Void
   +0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
   +0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
   +0x058 AnsiCodePageData : Ptr32 Void
   +0x05c OemCodePageData  : Ptr32 Void
   +0x060 UnicodeCaseTableData : Ptr32 Void
   +0x064 NumberOfProcessors : Uint4B
   +0x068 NtGlobalFlag     : Uint4B
   +0x070 CriticalSectionTimeout : _LARGE_INTEGER
   +0x078 HeapSegmentReserve : Uint4B
   +0x07c HeapSegmentCommit : Uint4B
   +0x080 HeapDeCommitTotalFreeThreshold : Uint4B
   +0x084 HeapDeCommitFreeBlockThreshold : Uint4B
   +0x088 NumberOfHeaps    : Uint4B
   +0x08c MaximumNumberOfHeaps : Uint4B
   +0x090 ProcessHeaps     : Ptr32 Ptr32 Void
   +0x094 GdiSharedHandleTable : Ptr32 Void
   +0x098 ProcessStarterHelper : Ptr32 Void
   +0x09c GdiDCAttributeList : Uint4B
   +0x0a0 LoaderLock       : Ptr32 Void
   +0x0a4 OSMajorVersion   : Uint4B
   +0x0a8 OSMinorVersion   : Uint4B
   +0x0ac OSBuildNumber    : Uint2B
   +0x0ae OSCSDVersion     : Uint2B
   +0x0b0 OSPlatformId     : Uint4B
   +0x0b4 ImageSubsystem   : Uint4B
   +0x0b8 ImageSubsystemMajorVersion : Uint4B
   +0x0bc ImageSubsystemMinorVersion : Uint4B
   +0x0c0 ImageProcessAffinityMask : Uint4B
   +0x0c4 GdiHandleBuffer  : [34] Uint4B
   +0x14c PostProcessInitRoutine : Ptr32    
   +0x150 TlsExpansionBitmap : Ptr32 Void
   +0x154 TlsExpansionBitmapBits : [32] Uint4B
   +0x1d4 SessionId        : Uint4B
   +0x1d8 AppCompatInfo    : Ptr32 Void
   +0x1dc CSDVersion       : _UNICODE_STRING

The ImageBaseAddress provides you with the virtual address of the program binary in memory. It reads 0x00400000. Convert it to a physical address and you should get 0x0fee000. At the beginning of this memory page you'll spot the letters "MZ", the magic signature of executables. Copy and paste the whole memory page (4096 bytes) into a blank file and save it as "reconstructed.exe".

You may inspect the header with PEriscope or FileAlyzer. Just ignore any error messages, a lot of important data is still missing from the file.

The header will be our construction plan. You'll find the beginning of a section by adding ImageBaseAddress to the section's virtual address VirtAddr as shown in the PE header. The physical address PhysAddr indicates the position where the recovered data will go in the reconstructed file. Finally PhysSize tells us the count of bytes to be copied.

Sections of the reconstructed binary.

Now let's start with ".text", the program code. Base address is 0x400000, we add 0x1000 as shown in the Section Table. According to PhysSize the section extends over 6 pages (6 * 0x1000 bytes). Therefore we'll have to calculate the physical addresses of the virtual address 0x401000 and the following 5 pages. For reference I'll provide the results in a table at the end of this article - but please try on your own first. Now copy and paste each page into the file.

The code section should be complete now. If you're curious about the binary you may load it into a disassembler now. Of course there might be some errors again, because the import and export tables are still missing. Beside this the code looks viable.

Our next section will be .rdata, starting at 0x407000 for 5 pages. After you've reconstructed this section you'll recognize the import and export tables:

Reconstructed import / export tables

We're almost done. Only .data (starting at 0x40c000) and .rsrc (starting at 0x40d000) are missing. Both sections consist of a single page only. As soon as you're done with it, the version resource appears:

Version resource

Congratulations, you've just reconstructed your first binary from a memory dump! Now you could proceed with your examination, scan for viruses or disassemble the binary.

SectionPagevirt. Addr.PDIPTIphys. Addr.
.text10x401000110x3357000
20x402000120x76b8000
30x403000130x0099000
40x404000140x33da000
50x405000150x6efb000
60x406000160x2b3c000
.rdata10x407000170x3c40000
20x408000180x7760000
30x409000190x3561000
40x40a0001100x16e2000
50x40b0001110x1aa3000
.data10x40c0001120x3c4b000
.rsrc10x40d0001130x0768000

04/17/2006: In the 2nd part you'll learn how to simplify the process with the help of memdump.pl

Archives

Imprint

This blog is a project of:
Andreas Schuster
Im Äuelchen 45
D-53177 Bonn
impressum@forensikblog.de

Copyright © 2005-2012 by
Andreas Schuster
All rights reserved.
Powered by Movable Type 5.12