Working with and Detecting Injected Processes

Occasionally it is nice to walk through code in a debugger. I love spending time in IDA but I have found that focusing on 100% static analysis slows down my learning process. If you read this blog you might have noticed that I spend a good amount of time reversing banking malware. Along with portable based document exploit analysis (not vulnerability) I find banking malware fascinating. The reason for this is because of all the different techniques and code that can reside in one executable. Take for example Cridex. It's code contains for XML parsing, form grabbing, monitoring registry traces, code injection and all sorts of interesting features. It's a lot of code to cover and it is easy to get lost. Debugging usually helps me to focus on one path of code execution. Sadly the authors of banking malware do not make debugging their software very easy, especially the injected process. Luckily there are tricks to help with debugging the injected process. Let's do a quick walk through of process injection. The malware will first find the process it wants to inject to, the malware will call OpenProcess, adjust it's token for the correct rights, call VirtualAlloc to allocate memory into the process, write into the process memory using WriteProcessMemory, call CreateRemoteThread and then we have code execution of the injected process. Finding the process to inject into can be done in a couple of ways. The malware can enumerate all processes then inject into selected processes or the malware can hook APIs related to the creation of new processes creation and then inject once those APIs get called. Cridex hooks NtCreateThread to ensure it injects into newly started processes.  A side effect of injecting into the creation of all new processes is the malware injects into all new processes. Let's use this side effect to find the entry point of the injected code of Cridex in Ollydbg. Firstly infect the machine with Cridex. Then Open up Ollydbg, Options > Debugging Options > Select System breakpoint. Then open up iexplorer.exe the folder path in Windows XP is (C:\Program Files\Internet Explorer\IEXPLORE.EXE). Then click View > Memory or ALT-M.

Memory of Injected Process
We will notice a block of memory that has Read, Write and Execute rights. If we set a breakpoint (F2) on this memory address Ollydbg will break on the entry point of the injected code.Remove breakpoint.
Start of Injected Code
In the image above we can see string references to LoadLibaryA and GetProcAddress which is used to rebuild the import table. At this point if we wanted to now where the Cridex executable and registry keys were stored, we could use Search for all referenced text strings in Ollydbg. This technique also works for Citadel but the memory won't be highlighted in red. If we search for a memory block with no owner and REW access we should be able to find it.

Disclaimer Time:
Horrible C code is ahead. I have not coded in C in years and even then it was not much.  I should probably remove that skill from my resume. I tried to include all links or references I used. The following code was for me to learn and get back into C. Nothing original. More of me just sketching out ideas. I would not apply these techniques for incident response. It would be much much better to use Volatility for something like this.

If we were to use a tool like Process Explorer we would not see the malware running on the machine. A lot of the time the watcher process is running in the memory space of explorer.exe. Usually the injected process is wanting to be injected into iexplorer.exe, firefox.exe or chorme.exe. If we wanted to detect the malware or dump the malware the best choice would be to help out the malware by starting a browser. Then enumerate all memory sections with a protection of RWE. If we find a block of memory with this protection we dump the block of memory to a file. After that we close down the browser and exit.

Let's walk through that process. We can get the address of iexplorer.exe on Windows XP and Windows 7 by reading the default value at the registry key HKEY_CLASSES_ROOT\applications\iexplore.exe\shell\open\command, once we have the file path we open iexplorer.exe in debug mode using CreateProcessA. This will ensure that iexplorer.exe doesn't start messing with the memory protections. At this point the malware will see the new process and inject into it. We can now use VirtualQueryEx to query the memory protection. If the protection is PAGE_EXECUTE_READWRITE we will use ReadProcessMemory to read the contents and finally dump the contects to a file with the name of the memory address. What would this look like on a machine infected with Citadel and Cridex?

Scan results on a machine infected with Cridex and Citadel
Cool. Now we have dumps of the files and we can do whatever we usually do with memory dumps. Below is the code. Warning It's only a POC. Which has only been tested on Windows XP. Which was compiled with Microsoft Visual C++ 2010. Which has dependencies. And it's a debug build. I know it just keeps getting worse.. Please do not download code from my blog. I don't update the code here, only on the repo. The repo contains an executable in the bin folder. The needed dependencies are also included in a zip.

Bibucket Repo - LINK


    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <tchar.h>
    /*
    Name:
        bkdump.exe
    Version:
        0.1
    Description:
        Starts iexplor.exe and then dumps any memory that has RWE rights. POC only tested on Win XP.
    Author:
        alexander<dot>hanel<at>gmail<dot>com
    License:
    bkdump is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with this program. If not, see
    <http://www.gnu.org/licenses/>.
    Notes:
        * Best technique for finding dynamically loaded dlls in a process space?
    To Do:
    Useful Links:
        http://www.youtube.com/watch?NR=1&v=lwFIC7It3Fc&feature=endscreen <- most of the code came from here. Awesome Video!
        http://www.catch22.net/tuts/undocumented-createprocess
        http://www.blizzhackers.cc/viewtopic.php?p=2483118
        http://cboard.cprogramming.com/windows-programming/102965-help-mbi-baseaddress-loop.html
    */
    typedef struct _MEMBLOCK
    {
        HANDLE hProc;
        unsigned char *addr;
        int size;
        unsigned char *buffer;
        struct _MEMBLOCK *next;
        
    } MEMBLOCK;
    MEMBLOCK* create_memblock (HANDLE hProc, MEMORY_BASIC_INFORMATION *meminfo)
    {    // used to create the membloc
        MEMBLOCK *mb = malloc(sizeof(MEMBLOCK));
        if (mb)
        {
            mb->hProc = hProc;
            mb->addr = meminfo->BaseAddress;
            mb->size = meminfo->RegionSize;
            mb->buffer = malloc(meminfo->RegionSize);
            mb->next = NULL;
        }
        return mb;
    }
    void free_memblock (MEMBLOCK *mb)
    {
        if (mb)
        {
            if (mb->buffer)
            {
                free (mb->buffer);
            }
            free (mb);
        }
    }
    unsigned int peek (HANDLE hProc, int data_size, unsigned int addr)
    {
    unsigned int val = 0;
    if (ReadProcessMemory (hProc, (void*)addr, &val, data_size, NULL) == 0)
    {
    printf ("peek failed\r\n");
    }
    return val;
    }
    char * getIePath()
    {
        // example of reading registry http://www.codersource.net/Win32/Win32Registry/RegistryOperationsusingWin32.aspx
        char lszValue[MAX_PATH];
        char **newValue = lszValue + 1;
        char *strkey;
        HKEY hKey;
        LONG returnStatus;
        DWORD dwType = REG_SZ;
        DWORD dwSize = MAX_PATH;
        returnStatus = RegOpenKeyEx(HKEY_CLASSES_ROOT,TEXT("applications\\iexplore.exe\\shell\\open\\command"), 0L, KEY_READ, &hKey);
        if (returnStatus == ERROR_SUCCESS)
        {
            returnStatus = RegQueryValueExA(hKey, NULL, NULL, &dwType, (LPBYTE)&lszValue, &dwSize);
            if(returnStatus == ERROR_SUCCESS)
            {
                RegCloseKey(hKey);
                if( ( strkey=strstr(lszValue, "%1" ) ) !=NULL)
                    *(strkey=strkey-2)='\0';
                printf("iexplorer.exe path is %s", newValue);
                // newValue was the easiest way I could find to remove the first char. I miss python
                return newValue;
            }
            else
            {
            printf("ERROR: Registry IE Path not Found");
            }
        }
        else
        {
            printf("ERROR: Registry IE Path not Found");
        }
        RegCloseKey(hKey);
        return NULL;
    }
    MEMBLOCK* create_scan ( unsigned int pid)
    {
        char path[MAX_PATH];
        MEMBLOCK *mb_list = NULL;
        MEMORY_BASIC_INFORMATION meminfo;
        unsigned char *addr = 0;
        STARTUPINFO si;
        PROCESS_INFORMATION pi;
        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(si);
        strcpy(path,getIePath());
        if(!CreateProcessA(path , NULL, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi))
            printf("\nSorry! Broke on CreateProcess()\n\n");
        else
        {
            printf("\nDummy Process Started");
        }
        if (pi.hProcess)
        {
            while (1)
            {
                if (VirtualQueryEx(pi.hProcess, addr, &meminfo, sizeof(meminfo)) == 0)
                { // query addresses, reads all meomory including non-commited
                    break;
                }
                if (meminfo.Protect & PAGE_EXECUTE_READWRITE)
                {
                    MEMBLOCK *mb = create_memblock (pi.hProcess, &meminfo);
                    if (mb)
                    {
                        mb->next = mb_list;
                        mb_list = mb;
                    }
                }
                addr = ( unsigned char*)meminfo.BaseAddress + meminfo.RegionSize;
            }
        }
        return mb_list;
    }
    void free_scan (MEMBLOCK *mb_list)
    {
        CloseHandle(mb_list->hProc);
        while ( mb_list)
        {
            MEMBLOCK *mb = mb_list;
            mb_list = mb_list->next;
            free_memblock (mb);
        }
    }
    void dump_scan_info ( MEMBLOCK *mb_list)
    {
        MEMBLOCK *mb = mb_list;
        char *buffer = (char*) malloc(mb->size);
        while (mb)
        {
            char *buffer = (char*) malloc(mb->size);
            FILE *fp;
            char filename[15];
            sprintf(filename, "0x%08x.bin", mb->addr);
            fp=fopen(filename, "wb");
            printf ("\nSuspicious Memory Block:\nAddr: 0x%08x Size:%d\r\n", mb->addr, mb->size);
            if (ReadProcessMemory(mb->hProc,(void*)mb->addr, buffer, mb->size, NULL) != 0)
            {
                printf ("Dumping Memory at 0x%08x", mb->addr);
                fwrite(buffer,1, mb->size, fp);
                fclose(fp);
            }
            else
                printf("Error Could Not Dump Memory");
            mb = mb->next;
        }
    }
    int main(int argc, char *argv[])
    {
        
        MEMBLOCK *scan = create_scan(0);
        if (scan)
        {
            dump_scan_info (scan);
            free_scan (scan);
        }
        /*
        
        */
        return 0;
    }
Source code highlighted with hilite.me

2 comments:

  1. I was going to use tamper data to see what I can see on browser input capture like cridex, have you tried that yet? Excellent writeup, thanks for sharing!

    ReplyDelete
    Replies
    1. Thank you. I have tried similar things using Python but not in C. The Gray Hat Python book has some good examples of hooking code if you want to check it out. Here's a link. http://nostarch.com/download/ghpython_src.zip

      Delete