Extracting Malicious Codes from the Process Memory: ZeuS Case
In my last article, Analyzing Malwares Using Microsoft Tools, we collected a process dump image with an infected ZeuS variant inside it. In this article, we will go through the procedure for separating the ZeuS part from the other parts. With the extracted binary data, we can apply a disassembling process using IDA. You may wonder if it's possible to disassemble the image taken out from the process dump. In this case, the ZeuS variant was injecting a valid DLL file into the process, and somehow managed to hide the existence of the DLL so that it would not appear in the loaded modules list. We can locate that image and can take it out using some tricks.
In the previous article, we found that the following APIs were hooked:
ws2_32.dll:
send, WSASend, closesocket
wininet.dll:
InternetCloseHandle, HttpSendRequestA, HttpQueryInfoA, InternetReadFile, InternetQueryDataAvailable, HttpSendRequestExW, InternetReadFileExA, HttpSendRequestW, HttpSendRequestExA
crypt32.dll:
PFXImportCertStore
user32.dll:
TranslateMessage, DefWindowProcW, NtUserBeginPaint, NtUserEndPaint, DefWindowProcA, GetClipboardData
ntdll.dll:
ZwCreateThread, NtQueryDirectoryFile
With the list of hooked APIs in mind, open the process dump file using Windbg. Then use the "u"(disassemble) command to check the first instructions they have. Below are some of the examples:
0:000> u ws2_32!send L1
ws2_32!send:
71ab428a e911990c8f jmp 00b7dba0
0:000> u ws2_32!WSASend L1
ws2_32!WSASend:
71ab6233 e985790c8f jmp 00b7dbbd
…
0:000> u crypt32!PFXImportCertStore L1
crypt32!PFXImportCertStore:
77aef748 e9f7e50889 jmp 00b7dd44
…
0:000> u ntdll!ZwCreateThread L1
ntdll!ZwCreateThread:
7c90d7d2 e955962784 jmp 00b86e2c
0:000> u ntdll!NtQueryDirectoryFile L1
ntdll!NtQueryDirectoryFile:
7c90df5e e927902784 jmp 00b86f8a
From the installed inline hooks, we can get the memory region where the hooking function is installed. Here is one of the "!address" results from the hooking function's addresses:
0:000> !address 00b7dd44
00b70000 : 00b70000 - 00026000
Type 00020000 MEM_PRIVATE
Protect 00000040 PAGE_EXECUTE_READWRITE
State 00001000 MEM_COMMIT
Usage RegionUsageIsVAD
The memory region starts from 0xb70000 and the size is 0x26000 bytes. Let's just dump the start of the memory region using the "db" command, which dumps memory by bytes.
Yes, we got the start of the PE file. We can see the 'MZ' signature and some part of DOS header of the PE file. What we have to do now is simply dump the memory region to a file. Windbg provides a command called ".writemem" to write memory regions to a file.
The following command dumps the region of memory to the file " C:\Malwares\00b70000.bin".
0:000> .writemem C:\Malwares\00b70000.bin b70000 L26000
Writing 26000 bytes............................................................................
We open the file using IDA. It seems to be successful, until we find that there is something wrong with the disassembly listing.
First we get some error message dialog boxes:
Figure 1: Virtual Address Translation Error
We see that the imports table is empty:
Figure 2: Empty Imports Table
Even call instructions are referencing some invalid addresses:
Figure 3: Call Referencing Invalid Region
We notice broken data referencing:
This is happening because the base address for image loading is different from what is set in the PE header. We can check the value of the image base defined in the PE header by looking at the top of the IDA disassembly listing. In this case, the image base is set as 0x400000 as you can see from the following picture, but the image base when we dumped the image was actually 0xb70000.
Figure 4: Image Base is 0x400000
Will only fixing the image base solve the issues? No. We need to take care of the section relocations. When the PE file is loaded into the process address space, it is not just copied exactly. The sections inside are located according to their virtual address. Each section has their position and size in the physical file, and also has a virtual address region to be mapped. All the information is inside the PE file header.
We used the pefile Python module from Ero Carrera to achieve the PE file manipulation. Here's the source code for the script that we used:
import pefile
import sys
filename = sys.argv[1]
out_filename = sys.argv[2]
rebase_address = int(sys.argv[3],16)
pe = pefile.PE(filename)
print "Rebasing from ",hex(pe.OPTIONAL_HEADER.ImageBase),"to",hex(rebase_address)
pe.OPTIONAL_HEADER.ImageBase = rebase_address
for section in pe.sections:
section.PointerToRawData = section.VirtualAddress
pe.write(out_filename)
Save the python script as "Rebase.py" file. Here's how you can use the script:
c:\python26\python Rebase.py 00b70000.bin 00b70000_rebased.bin 0xb70000
This command will re-base the image base to 0xb70000 and will also correct section location information by setting PointerToRawData to be same as the VirtualAddress value. PointerToRawData is the offset in the file where the section starts. We dumped it from the memory and it should be same as VirtualAddress.
After running the script, open up the re-based image "00b70000_rebased.bin" using IDA.
Now we have a valid and good imports table:
Figure 5: Valid Imports Table
Call instructions are referencing valid APIs:
Figure 6: Call Instruction Referencing Valid APIs
Also, the string referencing is corrected and shows good values:
Figure 7: Valid String Data
Conclusion
Retrieving injected modules and making it valid for disassembling is possible with a few Windbg tricks and python scripts. Tracing malicious code inside a debugger doesn't compare to having it inside a full-blown disassembler. The script presented in this article can be applied to any injected modules in the Windows environment.
Next time we are going to talk about automated scripts that will do all the jobs that we have done with a single command.
Thanks and have a great reversing!