For an introduction, please refer to Part 1 of this analysis.
Still inside the same Lab11-02.dll as before, we can return back to DllMain and locate one more subroutine call, which takes us to the hook code, but this is incomplete knowledge since we haven’t yet seen how the hook even gets installed in the first place. We know from the previous post that there is one function export in Lab11-02.dll entitled “installer.” While we could go dynamic by running the DLL by opening up cmd and entering “rundll32 Lab11-02.dll installer”, why not just take a look at the installer export in IDA since we already have it up?
Doing so gives us the below code, which is frankly a lot more clear and concise than that decipher code was:
In the above figure, the installer routine is looking for the [HKEY_LOCAL_MACHINE]\SOFTWARE\MICROSOFT\Windows NT\CurrentVersion\Windows key. The reason being, this key has the AppInit_DLLs entry which it is about to modify:
This code is very straightforward. It’s taking the opened key from above and going to the AppInit_DLLs subkey and then adding spoolvxx32.dll to it. Since spoolvxx32.dll is placed into the system folder by the malware, it doesn’t need to use a fully qualified path here.
The next thing that happens is the malware takes the fully qualified module path that was given to it by GetModuleFileNameA from DLLMain in the Part 1 blog post, and it uses that to copy the file to the system directory and name it spoolvxx32.dll as a means to conceal itself. The interesting part of this code is that it tries to set the registry value before it actually copies the file. So if the registry value was set but the file was not copied successfully for some reason, there would be a dead registry value sitting in the registry… But I suppose that the malware author doesn’t care about that.
To recap, adding itself to the system folder, renaming itself, and adding itself to the AppInit_DLLs registry entry will cause this malicious DLL to be loaded into the processes of Microsoft Windows applications when they boot up, such as MS Word, Excel, Outlook, and any other program that calls User32.DLL. The final part of this analysis is to look at the actual hook code and see how it works. For this we go back to DllMain and scroll down to this neck of the woods:
The highlighted routine is at offset 16E2 but I renamed it to check_names . This is the code which calls the malicious inline hook. It’s important to note here that the reason this code is inside of DllMain is because the nature of this malware is for it to be automatically loaded by AppInit_DLLs into every victim process anyway, so it makes sense to just throw the malicious payload into DllMain like this, as opposed to for example, a malicious DLL which lies dormant and waits to be called by a malicious executable, which would likely have malicious export functions that were outside of DllMain. The lesson here is when dealing with a malicious DLL, always check DllMain and the function exports, not just one or the other.
Entering the hook function, we see (excuse the high-level view, the important parts are the filenames here):
OUTLOOK.EXE and MSIMN.EXE are Microsoft Outlook and Outlook Express files, and THEBAT.EXE is another mail client. Prior to the code checking for those names, the program runs GetModuleFileNameA to get the current process name to compare, converts it to uppercase, then compares it with those strings. If the process’ filename matches, then it runs the hook code. The hook code is to say the least, quite complicated and it took some serious reverse-engineering time, but I’m going to do my best to break it down and explain it. Let’s take a quick look at what I’ll call the hook stub:
This stub, with the labelings that I’ve provided, give us a high-level view of the hook.
STEP 1 – PAUSE ALL THREADS
The malware first pauses all threads in the process other than the current thread that it’s using. Why? Perhaps it’s going to modify code that could conflict if it is currently being run. To do this, it runs the routine that I’ve labeled, entitled “suspend_all_threads.” This routine calls GetCurrentThreadId, then CreateToolhelp32Snapshot, which as discussed before, takes a snapshot of all processes and threads. Next, it calls Thread32First, checks that first thread in the list to see if it’s the current thread. If it is, it moves to the next and suspends it, then moves to the next and suspends that, and it keeps walking the threads until they’re all suspended. Here’s a high-level view that you may need to zoom in to see, but I wanted to show it anyway:
One interesting little factoid is we see this at the beginning of the entire thread suspending routine:
What this does is get the address of OpenThread from kernel32.dll1 in memory so that the function can be ran later on. The reason the malware needs to use this is to give itself permission to suspend threads before it actually suspends the threads. It requests access level THREAD_SUSPEND_RESUME from OpenThread and passes the thread’s ID into OpenThread:
STEP 2 – Install Hook
The next thing the hook_stub does is run a function that I’ve labeled “install_hook” which starts the payload process. The key/tricky part of this attack is that it takes the address of the routine which holds the malicious code that I’ve called hook_code, and passes it into install_hook. In other words, it passes a function pointer of the malicious code into the install_hook function as an argument, along with the names “send” and “wsock32.dll”.
The first part of install_hook is pretty much a repeat of what we saw in an earlier blog post. When the program needs to access an API function, it can use, in this order: GetModuleHandleA, LoadLibraryA, and then GetProcAddress. This just opens up a dll file and gets a specific function address out of it, for future usage. The malware uses this to obtain the address of the send winsock function in wsock32.dll. This function, if you have not done any socket programming, is the function used to send data across a network or the internet.
The malware then takes this address and passes it into yet another subroutine inside of install_hook which I’ve named virtual_protect just because that’s what it is mainly doing. virtual_protect alters the memory protection on the Winsock send function above to EXECUTE_READ_WRITE so that it can modify the API’s code itself to install the hook, by using the VirtualProtect (which stands for Virtual Memory Protections) API Call. After doing so, it then can modify send, and does so with the following code. Note that the following code is very complicated and it’s easy to get lost. For this reason, I’m going to go above and beyond simply just explaining what it does and also talk about my steps at attacking something like this to figure it out in the first place. First, the code:
This is somewhat simplified because of the names and comments that I placed in there, but keep in mind, at first, the names are arbitrary, such as loc_0000125B. In this situation, rather than get overwhelmed by the complexity, I start with familiar ground. What I knew was familiar here was malloc and memcpy. I saw that malloc was being used to allocate 255 bytes and put a pointer to that new memory inside of a variable, so I renamed that variable to new_mem. I also knew from the previous routine that virtual_protect had received a pointer to the address of the winsock send function as an argument. I named this pointer “send_addr.” These two new names helped made it obvious that bytes were being copied and moved between the two address spaces, and the code was walking around inside of them. What we can derive from this, especially sandwiched between two calls to VirtualProtect, is that this code is modifying the send function and installing a hook.
Returning to the code after memcpy, we see that it moves 10 bytes into new_mem and places jmp shellcode (0xE9). It then moves the address of send into eax, then it subtracts the address of new_mem from send_addr, subtracts 10 more bytes and then eventually places a jmp to the malicious code. After mulling over this a bit, the giveaway was a memory location which was being moved into eax and then another mov of [eax+1] which I don’t see very often. I double-clicked the ebp+ memory address, and it turns out that it’s a call to the malicious code, so I renamed it jmp_dest:
The important part is that this code block installs a jmp instruction in send that redirects execution to the malicious code, then eventually jumps back and runs send normally before finally adjusting the memory permissions of send back to normal and exiting the block. This is called an inline hook because it modifies the actual send function code rather than simply modifying an import address table to redirect to a completely different DLL/code library file altogether.
The final piece of this hook code is the “malicious code” itself, which adds a recipient to a char* which contains email recipients, only if it is detected in the first place. The code adds
RCPT TO: <email@example.com>\r\n
To the recipients of the email.
Summary of Behavior:
- Once installed, the DLL copies itself into system32 as spoolxxv32.dll and looks for the file Lab11-02.ini inside of the same folder.
- It also installs a registry entry inside of [HKEY_LOCAL_MACHINE]\SOFTWARE\MICROSOFT\Windows NT\CurrentVersion\Windows in AppInit_DLLs which loads it into every process which uses Windows API library user32.dll.
- After this, when it is loaded into a process, it reads the ini file and decrypts the email address firstname.lastname@example.org, then uses it when the send socket function call is made to intercept references to “RCPT TO:” and add an additional email recipient to the billy email address without the user knowing. This only occurs when the “RCPT TO:” string is found and the program is Microsoft Outlook, Outlook Express, or The Bat! email clients.
- It accomplishes the above behavior by installing an “inline hook” – by modifying the code inside of wsock32.dll’s send function to redirect execution to malicious code before directing it back to the legitimate send function afterwards.
In this rather detailed analysis, we saw yet another way for malware to try to conceal itself from both the user and basic analysis via encryption and hiding malicious code inside of a legitimate API function call. Microsoft has since updated the AppInit_DLLs so that it has much less potential of enabling malware since most modern computers ship with Secure Boot enabled by the hardware manufacturer, which disables AppInit_DLLs. However, it can still be turned on and therefore could still be a potential threat to be aware of. We also covered some useful methods to break down and analyze complicated assembly routines without obvious Win API calls. Last but not least, this analysis demonstrates a continuing theme of using indirect approaches of loading code modules for concealment purposes.