Unpacking MFC Compiled CryptoWall Malware
Introduction
First and foremost, this article does not intend to analyze what CryptoWall malwares are (since many malware researchers did that already) but instead this analysis is focused on unpacking MFC compiled cryptowall.
You can also download this MFC tutorial by Externalist on this site https://tuts4you.com/download.php?view.2509 for additional references.
I used IDA Pro and Ollydbg hand in hand when debugging to get a better overview of the malware when unpacking.
I used IDA Pro and Ollydbg hand in hand when debugging to get a better overview of the malware when unpacking.
What is MFC?
MFC or Microsoft Foundation Class is a collection of classes most commonly used in object oriented programming. Think of MFC as a wrapper for windows API that are written in C++.
So, why MFC?
Since MFC are wrapped windows API, the commonly known windows API to us malware researchers are now hidden inside a MFC library making static analysis on malwares such as cryptowall quite difficult.
Here is what the imports of a MFC compiled file looks like:
Imports of MFC42.DLL
Woah!! No string names at all? Only ordinals? Don't fret, we can still continue to analyze this file.
If you have IDA Pro installed, it will automatically load the corresponding library for you making the import names for MFC42.dll visible.
Let's begin..
First, lets take a look at the WINMAIN of our sample (9DB8BE981E9CDFCB583030E0057345AB)
Following the MFC call instruction leads us to a dead-end. So what now?
Based from experience, I know that packed malwares will attempt to reconstruct it's import API from kernel32.dll at some point. So from there, lets load it in OllyDbg and put a breakpoint in LoadLibraryA. Press F9 (run) a couple of times until a familiar "kernel32.dll" string with an address within the scope of our file is visible in stack.
Press Alt+F9 to return to user code, then trace from there and let's look for a decryption routine.
I found the decryption routine at
.text:00401370 push esi ;
.text:00401371 mov esi, ecx
.text:00401373 call READ_TO_MEM ;
.text:00401378 push 54638
.text:0040137D mov ecx, esi
.text:0040137F call DECRYPT_FUNC
where it will first read a portion of itself to higher memory (Function@00401373) then proceed to decrypt a size of 0x434Ah.
Take note of the memory location because execution will be transferred from there.
In this case, the memory location is 00980228 and lets call this UNPACKED01 Function.
Typical to packers is that they will always attempt to rebuild their Import Address Table by preparing the windows API strings to be used together with using LoadLibrary and GetProcAddress combo.
In this example, the packer prepares its strings directly in stack as a way of obfuscation making it not readily visible to the naked eye of the researcher.
debug029:0098047F mov byte ptr [ebp-20h], 'k'List of strings used is listed below.
debug029:00980483 mov byte ptr [ebp-1Fh], 'e'
debug029:00980487 mov byte ptr [ebp-1Eh], 'r'
debug029:0098048B mov byte ptr [ebp-1Dh], 'n'
debug029:0098048F mov byte ptr [ebp-1Ch], 'e'
debug029:00980493 mov byte ptr [ebp-1Bh], 'l'
debug029:00980497 mov byte ptr [ebp-1Ah], '3'
debug029:0098049B mov byte ptr [ebp-19h], '2'
debug029:0098049F mov byte ptr [ebp-18h], '.'
debug029:009804A3 mov byte ptr [ebp-17h], 'd'
debug029:009804A7 mov byte ptr [ebp-16h], 'l'
debug029:009804AB mov byte ptr [ebp-15h], 'l'
debug029:009804AF mov [ebp-14h], bl
debug029:009804B2 mov byte ptr [ebp-0A0h], 'G'
debug029:009804B9 mov byte ptr [ebp-9Fh], 'e'
debug029:009804C0 mov byte ptr [ebp-9Eh], 't'
debug029:009804C7 mov byte ptr [ebp-9Dh], 'M'
debug029:009804CE mov byte ptr [ebp-9Ch], 'o'
debug029:009804D5 mov byte ptr [ebp-9Bh], 'd'
debug029:009804DC mov byte ptr [ebp-9Ah], 'u'
debug029:009804E3 mov byte ptr [ebp-99h], 'l'
debug029:009804EA mov byte ptr [ebp-98h], 'e'
debug029:009804F1 mov byte ptr [ebp-97h], 'F'
debug029:009804F8 mov byte ptr [ebp-96h], 'i'
debug029:009804FF mov byte ptr [ebp-95h], 'l'
debug029:00980506 mov byte ptr [ebp-94h], 'e'
debug029:0098050D mov byte ptr [ebp-93h], 'N'
debug029:00980514 mov byte ptr [ebp-92h], 'a'
debug029:0098051B mov byte ptr [ebp-91h], 'm'
debug029:00980522 mov byte ptr [ebp-90h], 'e'
debug029:00980529 mov byte ptr [ebp-8Fh], 'W'
debug029:00980530 mov [ebp-8Eh], bl
- kernel32.dll
- GetModuleFileNameW
- CreateFileA
- VirtualAlloc
- GetFileSize
- ReadFile
- CloseHandle
- myapp.exe
- GetSystemDirectoryA
- explorer.exe
It then traverses the values of LoadLibraryA and GetProcAddress in kernel32.dll. An interesting note here is that you will never see a string "LoadLibraryA" or "GetProcAddress" assembled in stack or somewhere in memory. This is because, the packer used its own hashing algorithm where the string hashes of LoadLibraryA, GetProcAddress and kernel32.dll are already precomputed.
debug029:00980263 nopIt will then check the existence of the file "%systemdrive%:\myapp.exe" (C:\myapp.exe) or "%windir%\explorer.exe.\" (C:\Windows\explorer.exe.\). If either exists, the malware will go into an infinite loop and system infection will be bypassed. Also, you may see a 100% CPU utilization by the malware process. Malware code authors may have used this function in order to not get their own system infected when writing/testing codes.
debug029:00980264 push 0D5786h ; hash of "LoadLibraryA"
debug029:00980269 push 0D4E88h ; hash of "kernel32.dll"
debug029:0098026E call UNHASHER
debug029:00980273 mov [ebp-4], eax ; EAX = kernel32.dll:kernel32_LoadLibraryA
debug029:00980276 push 348BFAh ; hash of "GetProcAddress"
debug029:0098027B push 0D4E88h ; hash of "kernel32.dll"
debug029:00980280 call UNHASHER
debug029:00980285 mov [ebp-8], eax ; EAX = kernel32.dll:kernel32_GetProcAddress
This function may have been copied over from a known malware family called "Soraya" in which the malware terminates and discontinue system infection when the file C:\myapp.exe exists. But in this case instead of terminating, it goes into an infinite loop.
It will then allocate another memory location using VirtualAlloc, copies itself yet again to the allocated memory, and then looks for a dword marker in order to know which offset it will start decrypting.
The dword marker is known to be 0xA2658111 in this sample.
009809AA C685 34FFFFFF A2 MOV BYTE PTR SS:[EBP-CC],0A2
009809B1 C685 35FFFFFF 65 MOV BYTE PTR SS:[EBP-CB],65
009809B8 C685 36FFFFFF 81 MOV BYTE PTR SS:[EBP-CA],81
009809BF C685 37FFFFFF 11 MOV BYTE PTR SS:[EBP-C9],11
Once found, it will decrypt 0x3AACh bytes starting at +0x14h from the marker offset. The decrypted code will then be copied once more to a newly allocated memory space. Execution will also be transfered afterwards to the decrypted code.
From here, we have successfully unpacked the first layer of cryptowall.
But wait, there's more...
If you think, that we are done, then you are mistaken my friend. There is one more layer to unpack in order to get to the real Cryptowall code.
So let's get started :)
Here is a preview of what the next set of codes looks like:
CRYPTO_UNPACK2 Function has 8 options total depending on what is pushed in stack before it is called.
These options are:
CRYPTO_UNPACK2 Function has 8 options total depending on what is pushed in stack before it is called.
These options are:
- 0 = Exit process
- 1 = Unpacked compressed data to allocated memory
- 4 = Verify priviliges and create mutex "UACMut"
- 5 = Check for existence of sbiedll.dll module (Sandboxie) in running processes
- 6 = Check for existence of VBoxService.exe and vmtoolsd.exe in running processes
- 7 = Run unpacked cryptowall in memory.
- 8 = Copy itself as system.pif to %ALLUSERSPROFILE%\Start Menu\Programs\Startup and in %USERPROFILE%\Application Data with hidden and system attributes, create REGRUN entries, and can also disable firewall service
- 9 = Traverses registry to get default web browser application, also verifies iexplorer.exe if it is 32-bit, 64-bit version is skipped.
Note that there is no option 2 and 3.
To summarize it, the typical execution sequence for this malware is the following:
Push 1 - Call CRYPTO_UNPACK2
|- Push 6 - Call CRYPTO_UNPACK2
|- Push 5 - Call CRYPTO_UNPACK2
|- Push 4 - Call CRYPTO_UNPACK2
|- Push 8 - Call CRYPTO_UNPACK2
|- Push 7 - Call CRYPTO_UNPACK2 (Spawn unpacked cryptowall as process)
Push 0 - Call CRYPTO_UNPACK2 (Exit Process)
At start of CRYPTO_UNPACK2 Function, it will reconstruct its strings and needed windows API with the same method as done previously.
For reference, here is the list of strings that will be populated in stack.
- kernel32.dll
- shell32.dll
- advapi32.dll
- GetProcAddress CreateProcessA
- CreateProcessW
- CreateToolhelp32Snapshot
- Process32First
- Process32Next
- Module32First
- Module32Next
- CloseHandle
- GetCurrentProcess
- GlobalAlloc
- OpenProcessToken
- GetTokenInformation
- AllocateAndInitializeSid
- EqualSid
- LookupAccountSid
- OpenMutexA
- CreateMutexA
- CreateFileA
- CreateFileW
- GetFileSize
- ReadFile
- GetSystemDirectoryA
- GetSystemDirectoryW
- SetFileAttributesW
- SHGetSpecialFolderPathW
- RegOpenKeyExA
- RegOpenKeyExW
- RegSetValueExA
- RegSetValueExW
- RegQueryValueExA
- RegQueryValueExW
- RegCloseKey
- CreateDirectoryW
- ExitProcess
- Sleep
- GetFileTime
- SetFileTime
- CopyFileW
- VirtualAlloc
- GetTickCount
- IsWow64Process
- OpenProcess
- DuplicateHandle
- NtUnmapViewOfSection
- VirtualAllocEx
- WriteProcessMemory
- GetThreadContext
- SetThreadContext
- ResumeThread
- VirtualProtectEx
- TerminateProcess
- NTReadVirtualMemory
Looking back at the execution sequence posted above, Push 1 - Call CRYPTO_UNPACK2 will decrypt more of its encrypted data not surprisingly with the same procedure done on the first layer.
It will check (again) for a dword marker in its encrypted data in order to get the offset where to start the decryption routine. This time the dword marker is 0x34E812AEh.
debug031:013C2795 _looForMarker: ;When the marker is found, it will call its decryption routine to decrpyt another chunk of data.
debug031:013C2795 cmp byte ptr [ecx+edi], 34h ; Compare Two Operands
debug031:013C2799 jnz short _notMarker ; Jump if Not Zero (ZF=0)
debug031:013C279B cmp byte ptr [ecx+edi+1], 0E8h ; Compare Two Operands
debug031:013C27A0 jnz short _notMarker ; Jump if Not Zero (ZF=0)
debug031:013C27A2 cmp byte ptr [ecx+edi+2], 12h ; Compare Two Operands
debug031:013C27A7 jnz short _notMarker ; Jump if Not Zero (ZF=0)
debug031:013C27A9 cmp byte ptr [ecx+edi+3], 0AEh ; Compare Two Operands
debug031:013C27AE jnz short _notMarker ; Jump if Not Zero (ZF=0)
debug031:013C27B0 cmp [ebp+arg_0], ebx ; Compare Two Operands
debug031:013C27B3 jnz short loc_13C27BD ; Jump if Not Zero (ZF=0)
debug031:013C27B5 lea edx, [ecx+4] ;
debug031:013C27BB jmp short _notMarker ;
VoilĂ !! Its another MZ-PE file (why am I not surprised?). This new win32 file is actually the REAL Cryptowall malware. You can dump this in olly if you want to have a local copy of it.
It will then check if it is being run under a specific environment (sandboxie, vboxservice and vmware) and when found true will force exit its execution. (Push 6 - Call CRYPTO_UNPACK2, Push 5 - Call CRYPTO_UNPACK2)
It will then verify if its running with administrative privileges and create a mutex name "UACMut" when successful. (Push 4 - Call CRYPTO_UNPACK2)
Next, it will create a copy of itself located in %ALLUSERSPROFILE%\Start Menu\Programs\Startup\system.pif and %USERPROFILE%\Application Data with hidden and system attributes. It will also create regrun entries pointing to its copy to ensure automatic execution at windows startup.
Firewall service may also be disabled from using the command line "net stop Mpssvc" (Push 8 - Call CRYPTO_UNPACK2)
The newly unpacked MZ-PE file (unpacked cryptowall) will then be executed in memory by spawning a suspended process of itself but replacing all of its contents to that of the newly unpacked cryptowall using WriteProcessMemory API before finally calling ResumeThread. (Push 7 - Call CRYPTO_UNPACK2)
This is where unpacking ends and the real cryptowall malware starts. You can continue analysis of the unpacked cryptowall malware if you want, but my job here is done :)
Firewall service may also be disabled from using the command line "net stop Mpssvc" (Push 8 - Call CRYPTO_UNPACK2)
The newly unpacked MZ-PE file (unpacked cryptowall) will then be executed in memory by spawning a suspended process of itself but replacing all of its contents to that of the newly unpacked cryptowall using WriteProcessMemory API before finally calling ResumeThread. (Push 7 - Call CRYPTO_UNPACK2)
This is where unpacking ends and the real cryptowall malware starts. You can continue analysis of the unpacked cryptowall malware if you want, but my job here is done :)
Christopher D. Del Fierro