Practical Malware Analysis, Lab 11-2

Twitter Google+ Facebook LinkedIn

This is a walkthrough of the Lab 11-2 from the book Practical Malware Analysis. The sample under analysis, Lab11-02.dll, is a user-mode rootkit that performs inline hooking. The analysis of hooking mechanism is very interesting.

The samples for this lab can be downloaded from here.

Let’s start!

Static analysis

I’m going to perform some basic static analysis first.

Hashes

Lab11-02.dll

MD5 be4f4b9e88f2e1b1c38e0a0858eb3dd9
SHA1 79787427773dcce211e8e65e1156bd60535494ec
SHA256 df899256c4a9fc0e550c62b84ab9cb8acd8d18683f0a41c98ba83f0487d4766e

Imports

Some interesting imports that the program uses are:

The functions RegOpenKeyEx and RegSetValueEx suggest that this malware manipulates registry keys.

The function CopyFile may indicate that the malware will make a copy of some file (maybe itself?).

The function CreateToolhelp32Snapshot returns a snapshot of current processes, heaps, threads and modules; malware often uses this function to iterate through running processes or threads. The functions OpenThread, ResumeThread, SuspendThread suggest that indeed the malware is manipulating threads. The function VirtualProtect is used to modify the protection settings of a memory region; if this is done on a running thread, then the thread shall be suspended first in order to avoid malfunctions.

The functions LoadLibrary, GetProcAddress are an indication that the malware is using other functions from other DLLs, loading them at runtime.

Exports

This DLL exports a function named installer. This may be some installation routing. I can install the malware executing:

rundll32.exe Lab11-02.dll,installer

Strings

These are some interesting strings:

THEBAT.EXE
OUTLOOK.EXE
MSIMN.EXE
RCPT TO: <
send
wsock32.dll
SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
AppInit_DLLs
\spoolvxx32.dll
\Lab11-02.ini

The string AppInit_DLLs may indicate that this malware achieve persistence by using AppInit_DLLs: the DLLs listed in the value AppInit_DLLs found in this registry key SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows, are loaded into every process that uses User32.dll. (See Microsoft documentation on AppInit_DLLs for more information.)

The strings wsock32.dll and send suggest that this malware may perform network activity. Since there are no imports of networking functions, the malware would load networking DLLs via LoadLibrary and GetProcAddress (which indeed are present in the imports).

The string RCPT TO: < may indicate that the malware uses the SMTP protocol somehow. It’s also worth noting that there are strings referring to the names of some email clients: The Bat, Outlook and Outlook Express.

We also see the name of a DLL spoolvxx32.dll (maybe the malware hides itself under this name?) and a .ini file (maybe a configuration file?).

Code Analysis with IDA Pro and OllyDbg

Let’s dig in the code with IDA Pro and OllyDbg.

installer exported function

This function is used to install the malware and make it persistent. Persistence is achieved by using AppInit_DLLs. The program opens the registry key SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows and set the value AppInit_DLLs to %SystemRoot%\System32\spoolvxx32.dll.

Then the malicious DLL copies itself to %SystemRoot%\System32\spoolvxx32.dll. The name of the source file to copy is stored in the global variable ExistingFileName which was set by DLLMain to the full path of the current module.

DLLMain function

The DLLMain function reads from a file named Lab11-02.ini located in the SystemDirectory (%SystemRoot%\System32\Lab11-02.ini). The content of this file seems to be encrypted.

sub_100010B3 function (Sub_Decoder)

Since the bytes read from this file are passed to the function sub_100010B3 (at 0x100016CA), I expect that this function contains some decryption routine - indeed the function contains a loop and bytes-manipulating instructions (like XOR). Let’s analyze this function’s behavior in OllyDbg.

I will place a breakpoint at 0x100016CA, when the function is called. After step-following the execution in the debugger I obtain the decrypted value read from the .ini file: billy@malwareanalysisbook.com. This value is placed in byte_100034A0, so I will rename this location to Email_Address. Also I will rename the function sub_100010B3 to Sub_Decoder.

sub_100014B6 function (Sub_InstallHook)

After the .ini file is decrypted, the DLLMain calls the function sub_100014B6 (at 0x100016E2) before terminating. This function first calls a subroutine to get the full path of the current module (I renamed this subroutine to Sub_GetModuleFileNameWrapper); then (at 0x100014D6) it calls a subroutine to extract the executable name from the full path; next it places that name in var_4 and converts it to uppercase (at 0x100014F0).

A series of string comparisions follows: the malware is comparing the name of the current executable (that it just obtained from the previous instructions) to those of three email clients: THEBAT.EXE, OUTLOOK.EXE and MSIMN.EXE (the last one is Outlook Express).

Let’s keep in mind that this malicious DLL is loaded via AppInit_DLLs: that means it is loaded into every process that loads User32.dll. The Bat, Outlook and Outlook Express will surely do, but they are not the only processes that loads User32.dll: the malware is checking the name of the executable it was loaded into to decide what to do next.

So when the malicious DLL is loaded by one of those email clients (and only by those), it will continue on with its malicious behavior.

Note: if I try to execute the DLL within OllyDbg I will most probably see LOADDLL.EXE as the executable name: this is used by OllyDbg to load and execute a DLL.

If the malware was loaded by any of those email clients, the program jumps to to 0x10001561:

10001561 loc_10001561:
10001561 call    sub_100013BD
10001566 push    offset dword_10003484 ; int
1000156B push    offset sub_1000113D ; int
10001570 push    offset aSend    ; "send"
10001575 push    offset aWsock32_dll ; "wsock32.dll"
1000157A call    sub_100012A3
1000157F add     esp, 10h
10001582 call    sub_10001499

This code block contains four functions to analyze!

sub_100013BD function (Sub_SuspendThreads)

The function sub_100013BD gets the CurrentProcessId and passes it as a parameter to the function sub_100012FE.

The function sub_100012FE starts by getting the address of the OpenThread function in kernel32.dll, and saves it in var_4 (renamed to Address_OpenThread). The subroutine sub_10001000 is a wrapper to LoadLibrary and GetProcAddress, so I will rename it to Sub_GetProcAddressWrapper.

Next the program gets the CurrentThreadId, saving it in var_28 (renamed to Var_CurrentThreadId), and then calls CreateToolhelp32Snapshot (at 0x10001333) to get a snapshot of the running threads. It then cycles through the running threads: if the thread Id is not that of the current thread (comparision is made on the CurrentThreadId at 0x10001360) then it accesses the thread by calling OpenThread at 0x10001375, and suspends it by calling SuspendThread at 0x10001389.

So the function sub_100012FE suspends all but the current thread. I will rename it to Sub_SuspendThreads_Inner, and rename sub_100013BD to Sub_SuspendThreads.

sub_10001499 function (Sub_ResumeThreads)

The function sub_10001499 at the end of the code block, and its inner function sub_100013DA, are the same as sub_100013BD and its inner sub_100012FE, but with one difference: their purpose is to resume the threads that were previously suspended. So I will rename them to (guess?) Sub_ResumeThreads and Sub_ResumeThreads_Inner.

So the malware is suspending the running threads and calling the function sub_100012A3.

sub_100012A3 function (Sub_PlaceHook_Outer)

This function takes four arguments (those pushed on the stack in the code block above):

sub_100012A3("wsock32.dll", "send", sub_1000113D, dword_10003484)

The program here gets the address of the send function in the wsock32.dll module (using LoadLibrary and GetProcAddress); then at 0x100012F2 it calls sub_10001203 passing in three parameters:

sub_10001203( <Address of "send">, sub_1000113D, dword_10003484)

Let’s follow sub_10001203 down the rabbit hole: as we will see this function performs inline hooking of the send function - so I will rename it to Sub_PlaceHook. And I will rename the function sub_100012A3 to Sub_PlaceHook_Outer.

sub_10001203 function (Sub_PlaceHook)

After the prologue, this function calculates the relative offset between the sub_1000113D function, ie the “hooking” function performing the malicious behavior (as we’ll see shortly) - and thus renamed to Sub_Hooking, and the function that gets hooked, ie send; then it further subtracts 5 bytes, and places the result into var_4.

10001209 mov     eax, [ebp+Address_Sub_Hooking]
1000120C sub     eax, [ebp+Address_send]
1000120F sub     eax, 5
10001212 mov     [ebp+var_4], eax

What’s happening here?

In order to hook the send function, the malware needs to place a JMP instruction at the beginning of the send function to jump to the hooking function that performs the malicious behavior. To place this JMP, it needs to calculate the relative offset, or displacement, for the jump.

When the CPU prepares to execute a relative JMP instruction, the new Instruction Pointer is calculated as follows:

IP(new) = IP(current) + 5 + displacement

Why 5? It is the length of the unconditional JMP instruction: the E9 opcode (1 byte) plus the 32-bit address (4 bytes displacement).

Since we are jumping from send to Sub_Hooking, the displacement of the relative jump is:

displacement = Address_Sub_Hooking - Address_send - 5

So now var_4 contains the displacement of the JMP instruction to jump from the send to the hooking function. I will rename var_4 to Var_DisplaceToHooking.

Let’s move on:

In order to place a hook a the beginning of the send function, the malware first needs to change the protection settings of that memory region - (it also needs to suspend the running threads before modifying the memory!). The hook is 5 bytes long, so the malware needs to change the protection settings of the first 5 bytes starting from the address of send.

To do this the malware calls VirtualProtect:

10001215 lea     ecx, [ebp+flOldProtect]
10001218 push    ecx             ; lpflOldProtect
10001219 push    PAGE_EXECUTE_READWRITE ; flNewProtect
1000121B push    5               ; dwSize
1000121D mov     edx, [ebp+Address_send]
10001220 push    edx             ; lpAddress
10001221 call    ds:VirtualProtect

VirtualProtect takes four parameters:

VirtualProtect(lpAddress, size, NewProtect, lpOldProtect)

The new protection is PAGE_EXECUTE_READWRITE because write permission is needed. The old protection value is saved into flOldProtect to be restored later.

Next, the program allocates 255 bytes of memory:

10001227 push    0FFh            ; size_t
1000122C call    malloc
10001231 add     esp, 4
10001234 mov     [ebp+var_8], eax

The address of the newly allocated memory is saved into var_8.

What’s this memory region for?

This is the memory region that the malware uses to create a trampoline. The purpose of the trampoline is to jump back into the hooked function bypassing the hook itself! At the beginning of the trampoline the malware must also save the first 5 bytes of the send function that were overwritten by the hook (the “stolen bytes”).

So I will rename var_8 to Address_Trampoline, because it is the address of a memory region the serves the purpose of a trampoline.

In the next five instructions, the malware saves the address of the send function in the first 4 bytes of the trampoline region, then it writes the value 5 into the next byte. I don’t know why… and I won’t care!

10001237 mov     eax, [ebp+Address_Trampoline]
1000123A mov     ecx, [ebp+Address_send]
1000123D mov     [eax], ecx
1000123F mov     edx, [ebp+Address_Trampoline]
10001242 mov     byte ptr [edx+4], 5

Then the malware copies (memcpy) the first 5 bytes of the send function into the trampoline region pointed to by (Address_Trampoline + 5 bytes):

10001246 push    5               ; size_t
10001248 mov     eax, [ebp+Address_send]        ; source
1000124B push    eax             ; void *
1000124C mov     ecx, [ebp+Address_Trampoline]  ; destination
1000124F add     ecx, 5
10001252 push    ecx             ; void *
10001253 call    memcpy
10001258 add     esp, 0Ch

Here the malware is assuming that the send function’s first instructions align exactly on 5 bytes, which might not always be the case.

Immediately following in the trampoline region, the malware writes the byte 0xE9:

1000125B mov     edx, [ebp+Address_Trampoline]
1000125E mov     byte ptr [edx+0Ah], 0E9h

What is this? E9 is the opcode of the unconditional JMP instruction; the 4 bytes that follow must be the displacement of the relative jump. Since we are jumping from the trampoline back into the send function, the displacement is calculated as (the values shown here come from OllyDbg):

displacement = Address_send - Address_Trampoline - 0x0A
             = 0x71AB4C27 - 0x003625A0 - 0x0A
             = 0x7175267D

0x0A is the sum of the length of the JMP instruction (5 bytes) and 5 more bytes to jump into the send function in order to bypass the hook!

The 4-byte displacement is written immediately after the E9 opcode (starting at offset 0x0B)>

10001262 mov     eax, [ebp+Address_send]
10001265 sub     eax, [ebp+Address_Trampoline]
10001268 sub     eax, 0Ah
1000126B mov     ecx, [ebp+Address_Trampoline]
1000126E mov     [ecx+0Bh], eax

This is what gets written in the trampoline region:

003625A0  27 4C AB 71 05 8B FF 55
003625A8  8B EC E9 7D 26 75 71
003625B0  

When the Instruction Pointer points at 0x003625AA, i.e. at opcode E9, the JMP will be executed; the new instruction pointer is then calculated as follows:

IP(new) = IP(current) + 5 + displacement
        = 0x003625AA + 5 + 0x7175267D
		    = 0x71AB4C2C

This is exactly 5 bytes into the send function, so bypassing the hook!

We can see the same thing by disassembling the dump in OllyDbg:

003625AA  -E9 7D267571      JMP WS2_32.71AB4C2C

Next the JMP opcode E9 is copied into the beginning of the send function, followed by the displacement Var_DisplaceToHooking to jump from the send function to the hooking function:

10001271 mov     edx, [ebp+Address_send]
10001274 mov     byte ptr [edx], 0E9h
10001277 mov     eax, [ebp+Address_send]
1000127A mov     ecx, [ebp+Var_DisplaceToHooking]
1000127D mov     [eax+1], ecx

Now a new call to VirtualProtect restores the original protection settings on the memory region of the send function.

Lastly, Address_Dword_10003484 is modified to point to 5 bytes into the trampoline, i.e. where the stolen bytes of the send function were saved. I renamed Address_Dword_10003484 to Address_ToTrampoline.

10001294 mov     edx, [ebp+Address_Trampoline]
10001297 add     edx, 5              ; 5 bytes into the trampoline
1000129A mov     eax, [ebp+Address_ToTrampoline]
1000129D mov     [eax], edx

Placing hook wrap-up

So to wrap up so far, the function sub_100014B6 installs a hook at the beginning of the send function - I will rename it to Sub_InstallHook. The hook jumps to the hooking function which I’ll be analyzing in a moment. The hook installation routing also create a trampoline to jump back into the send function bypassing the hook.

The last function left for analysis is the hooking function that actually performs the malicious behavior.

sub_1000113D function (Sub_Hooking)

The function sub_1000113D is the hooking function that performs the malicious behavior. It takes the same arguments as the hooked function send (it can be redefined in IDA Pro by using Set Function Type):

send(SOCKET s, char * buf, int len, int flags)

The Sub_Hooking function searches for the substring RCPT TO: in the buffer associated to the socket.

If none is found then it jumps to location 0x100011E4 and calls the original send function using the Address_ToTrampoline (at 0x100011F4). If that string is found, then the program builds the string RCPT TO: <billy@malwareanalysisbook.com>\r\n and places it into the buffer.

In other words, the hooking function adds a new recipient to any email that is sent out using one of those email clients. Then it calls Address_ToTrampoline to jump back into the hooked function.

Hooking diagram

This is a diagram to show how the hooking works:

hooking diagram

Running the malware

Let’s try running the malware and observing its behavior!

I will run Outlook Express and send an email to a fake SMTP server running on my REMnux host. I’m going to use ApateDNS to resolve every DNS name query to my REMnux host and INetSim as the fake SMTP server.

To install the malware I will manually place the .ini file under %SystemRoot%\System32 and then run this command:

rundll32.exe Lab11-02.dll,installer

If I check in the registry, now the key SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows has AppInit_DLLs set to %SystemRoot%\System32\spoolvxx32.dll: that’s the location where the malware placed a copy of itself.

Next I start Outlook Express: looking at the loaded DLLs with Process Explorer, I can spot the malicious spoolvxx32.dll.

msimn with malicious dll loaded

Then I compose a new email and send it to johnny@malware.lab. Looking at the SMTP server log, we can see that the message has two recipients indeed: the second one billy@malwareanalysisbook.com was added by the malware.

connect
send: 220 mail.inetsim.org INetSim Mail Service ready.
recv: HELO remwindowsxp
send: 250 mail.inetsim.org
recv: MAIL FROM: <johnny@malware.lab>
send: 250 2.1.0 Ok
recv: RCPT TO: <billy@malwareanalysisbook.com>
send: 250 2.1.5 Ok
recv: RCPT TO: <buddy@somewhere.net>
send: 250 2.1.5 Ok
recv: DATA
send: 354 End data with <CR><LF>.<CR><LF>
recv: <(MESSAGE)> (505 bytes)
recv: .
info: Message id: <CD43BED5-ed51bfbe05aa0559b04dfd109370f10c1be1ba67@mail.inetsim.org>

If we attach to the Outlook Express process msimn.exe with OllyDbg, we can see the hooked function send in WS2_32.dll. Notice the JMP instruction right at the beginning to jump to the hooking function (0x1000113D) in the malicious spoolvxx DLL:

hook

And this is the trampoline: the malware saved here the 5 bytes stolen from the hooked function (8B FF 55 8B EC which indeed disassemble to those instructions) followed by a jump back into the hooked function to bypass the hook (to 0x71AB4C2C):

msimn with malicious dll loaded

That’s all for this lab!


Twitter Google+ Facebook LinkedIn