Extracting Objects from a Running Process

Few days ago two new 0-days have been spotted in the wild: CVE-2013-0633 and CVE-2013-0634, both of them involving a .swf file, possibly embedded inside a Word Document. It might be interesting to understand how to dump a similar resource while the attacked process is running,  after all the obfuscation layers are cleared. Clearly this same technique can be expanded to extract any type of object from any process, as an example whenever we need to simply dump a memory area, or when we need to extract (or find) an entire object. While memory dumpers are usually common tools, it might be interesting to understand how the dumping process works. So let’s dig a bit deeper.

Step 1: Understanding Windows Address Space

The virtual address space of a process is the set of virtual memory addresses that the process is allowed to use. In the default Virtual Address Space for 32-bit Windows processes, the lower 2GB: from 0x0 through 0x7FFFFFFF, are assigned to the running process while the other 2GB are normally used by the system.

More information: Virtual Address SpaceVirtual Address Space in 64-bit Windows.

Step 2: APIs to get system information and to access a process memory

2.1 Processes list

In order to read a given process memory, we need to acquire its handle and in turn, to do that we need to iterate the list of processes until we find the one we are looking for. Process enumeration is performed through the use of EnunProcesses() API, the code is quite simple:

if(!EnumProcesses(nProcess,  //processes list
                sizeof(nProcess), 
                &nByteOfProc))
            {return false;}

nOfProcesses =  nByteOfProc / sizeof(DWORD);

//for each process
for (i = 0; i < nOfProcesses; i++ )
{
    // Get a handle to the process.
    hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, nProcess[i] );

    // Get the process name.
    if ( hProcess != NULL)
    {
        if (EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) )
        {
            //get application filename
            GetModuleBaseName(hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR));

            //Has it found the right process?
            if(!_tcscmp(szProcessName, procName))
            {
                // Do whatever we need with the newly found process
                ...
            }
        }

        CloseHandle(hProcess);
    }

}

2.2 Memory sections

Once we have access to memory process throungh the handle, we need to get information on its sections in order to find in a signature (that is: a sequence of bytes).

That’s how we’ll proceed:

  1. Get the system minimum and maximum address of a process using GetSystemInfo
  2. Call VirtualQueryEx to retrieve information about sections within the virtual address space of a specified process, by section’s base address.
    1. System minimum address is the the first section base address.
    2. In the MEMORY_BASIC_INFORMATION structure there are info about memory section: size, base address and more;
    3. nextSectionBaseAddr = currentBaseAddress + currentSectionSize
  3. Copy in a locally allocated process memory the target process’ section (ReadProcessMemory) to find the desired  signature with a simple memcmp().

Here it is the code to find a sequence of bytes in memory: the SWF signature (the entire project can be downloaded at the end of the article).

//the signature to find
TCHAR buffer[] = _T("FWS"); //the signature can also be: CWS and ZWS, see SWF file format specification

GetSystemInfo(&sysInfo);
memBase = sysInfo.lpMinimumApplicationAddress;
memMax = sysInfo.lpMaximumApplicationAddress;

//until max memory address or until buffer found
while(memBase < memMax) {

    //get section info
        VirtualQueryEx(hProcess, memBase, &mbi, sizeof(MEMORY_BASIC_INFORMATION));

        //if section is not reserved
        if(mbi.State != MEM_RESERVE )
        {
            //I copy it: I alloc a memory buffer in local process ..
            allocatedMem = (BYTE *) VirtualAlloc(NULL, mbi.RegionSize, MEM_COMMIT, PAGE_READWRITE);
            if(allocatedMem != NULL) {

                //.. and copy bytes  for more scan velocity
                if(ReadProcessMemory(hProcess, memBase, allocatedMem, mbi.RegionSize, &jnk)) {
                    storedBaseAddr = baseAddr =  allocatedMem;
                    maxAddr = (BYTE*) ( ((SIZE_T)baseAddr + mbi.RegionSize) - lenBuffer);

                    //and find the buffer into section
                    while(baseAddr < maxAddr) {
                        if(memcmp((LPVOID)baseAddr, buffer, lenBuffer) == 0) {

                            //I found it!!
                            _tprintf(_T("\tBuffer found at found at %p\n"), ( (SIZE_T)memBase + (baseAddr-storedBaseAddr)) );  

                            //try to dump!
                            DWORD objSize = dumpObj(baseAddr);
                            //go ahead the object
                            baseAddr = (BYTE*) ((SIZE_T)baseAddr + objSize);

                        } else
                            baseAddr++; //next byte
                    }
                }

                //free memory
                VirtualFree(allocatedMem, 0, MEM_RELEASE);
            }
        }

        //next section base address
        memBase = (LPVOID) ((SIZE_T)memBase +  mbi.RegionSize);
}

2.3 Dump the object

Once we have found a signature in memory, we  can dump the object, provided we know its size and where to find this information: according to SWF specification the length field is an UInt32 (DWORD) at  start+4:

//addr of "FileLength" field:
stringBaseAddr += 4;

//get file size
DWORD size = *((DWORD*)stringBaseAddr);
_tprintf(_T("\tFile size: %u bytes\n"), size);

//go to object start addr
stringBaseAddr -= 4;

//create a new file
HANDLE hFile = CreateFile(outputFileName,GENERIC_WRITE, FILE_SHARE_WRITE, 0,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE) {
	_tprintf(_T("\tImpossible to create dump file\n")); 
        return;
    }

    //and write in the object
    if(WriteFile(hFile, stringBaseAddr, size, &jnk, NULL) == false) {
        CloseHandle(hFile);
        DeleteFile(outputFileName);
        _tprintf(_T("\tImpossible to dump the file\n\n")); 
    } else {
        CloseHandle(hFile);
        _tprintf(_T("\tFile dumped!!\n\n")); 
    }

That’s all!

Download project’s code.

Pn