← Back to list

Dissecting Emotet

Amit Moses · 2024-04-23 11:34 · 0 claps · 12.8 min read
#cybersecurity #malware-analysis #emotet
Open on Medium ↗
Wiki topics: 🔒 · Cybersecurity

Dissecting Emotet

Introduction

In this blog, I will present the static and dynamic analysis performed on an Emotet variant, starting from the initial malicious document containing vba macros, followed by the dissection of the malicious dll fetched from the malware’s infrastructure. Big shout out to Uriel Kosayev, the author of the CMAP malware course in which I did this analysis.

Emotet Background

Emotet (AKA: Heodo, SpmTools, Geode) was developed by a russian originating group dubbed Mummy Spider. In general, it serves as a botnet and is used as a distribution network for other criminal malware payloads. Emotet was first observed in mid 2014 as a banking trojan. Emotet is a Downloader malware, but has different plugins developed over the years such as: Outlook harvester, Smbspeader, Wifi spreader, Spam plugin and more.

Initial delivery — Malicious doc analysis

Static analysis

Examining the file in DIE shows it Is identified as an MS office document:

Looking at the magic bytes shows it is a Compound File Binary Format, which also include .doc files.

Examining the strings in the file using the “Strings” applications shows the string “VBA” was detected.

Another interesting indicator of it containing macros is by looking in the doc’s Details tab, where we can see a 150 line count even though the document itself only contains a picture:

doc with the lure image to “enable content”

doc with the lure image to “enable content”

Running Oledump shows two macros that could potentially be malicious:

Reviewing the VBA macro in Microsoft Word’s Visual Basic Editor,I saw that the function Zrr234efv7j6dfwr is the initial trigger of the vba macro, executing when a Document_open event occurs. When the event fires, it executes a function named Nauw80ycpl9g4a8c, which is part of the main function named Cn9ibqhh7rb

The highly obfuscated Nauw80ycpl9g4a8c function convinced me that dynamic analysis will be more time efficient to get the final payload that is executed.

nope, no static cleaning up of this mess.

nope, no static cleaning up of this mess.

Dynamic analysis

I decided to first run the document with Process Explorer to see what processes spawn before debugging the code, and saw that a WMI process launches a command line process which initiates Powershell.

In addition, it spawns an “error message” that looks like it was made by the malware author.

Next, I started debugging the document. In summary, the script performs the following actions:

  • builds up an obfuscated string and uses a “replace” method with the key “sg yw ah” to deobfuscate it. It is translated to the phrase “winmgmts:win32_process” (A WMI class that represents a process on Windows).
  • creates a process with a command that contains both the fake “word error” prompt, and an obfuscated PowerShell command that is passed to the process instantiation as the Commandline argument.
  • The PowerShell's code iterates a list of URLs trying to download the Emotet first stage; if the download is successful, it tries launching it with Rundll32.exe.

Here are visual findings of the information mentioned above:

Obfuscated string, soon-to-be the WMI process object

Obfuscated string, soon-to-be the WMI process object

The replace statement decoding the WMI process object string

The replace statement decoding the WMI process object string

final string

final string

Instantiation of the WMI process object

Instantiation of the WMI process object

Instantiation of the WMI process object#2

Instantiation of the WMI process object#2

The creation of the WMI process from the WMI process object, with KK being the CommandLine argument. Other arguments were empty.

The creation of the WMI process from the WMI process object, with KK being the CommandLine argument. Other arguments were empty.

The Windows Word VBA Debugger character limit breaks the PowerShell command, so I fetched it through ProcessExplorer, which contained both an obfuscated PowerShell command, and the fake “error” message shown before. The PowerShell command was base64 encoded and had null bytes separating the string’s characters. after we decoded that, the PowerShell command below appeared.

Cleaning it up showed the below final payload: it creates a new folder under the home directory, and tries fetching the first stage DLL’s from a list of URLS. then it checks if the size of the downloaded file is larger than 33571 bytes. If the condition is met, the dll is run with rundll32.exe.

First stage — DLL analysis

Static analysis

The sections’ entropy is relatively low, so it didn’t seem packed.

Reviewing the file in hex editor brought up the following findings:

Magic bytes and NT headers shows it is a valid PE file

The file is signed by a self-signed certificate, perhaps as means to increase credibility in front of less sophisticated AV solutions.

self signed#1

self signed#1

self signed#2

self signed#2

The PE file has the below section headers. There are a couple of .text sections, which isn’t something that I have encountered before.

I noticed that the code from .text4’s beginning had similar patterns that looked like chunks of obfuscated code, delimited by 120 bytes of the hex code 21 (represented as “!” in ascii). I assumed that at some point this data will be concatenated and will be served as the next stage. This hypothesis got stronger after viewing the relative size of .text4 compared to the other sections (93%)

code chunks delimited by exclamation mark delimiters

code chunks delimited by exclamation mark delimiters

.text4 is 93% of the file’s size

.text4 is 93% of the file’s size

Imports the DLL used was suspiciously low (21 functions from 4 DLL’s in total). Watching this along with the presence of GetProcAddress and LoadLibrary helps understand that more DLLs and functions will be loaded in runtime. Reviewing the strings of the application did not bring interesting findings.

Dynamic Analysis

Methodology

Since I didn’t want to miss anything Emotet is doing, I decided to debug it in IDA (freeware btw) with a local debugger. I wanted to truly “feel” the malware and its rhythm. Although this was hella tedious, I feel like I learned a lot and I guess that this is what really matters.

First stage — loading the malicious executable

In the first stage of the DLL execution, it performs a couple of memory allocations, and by a few times of copying data from the .text4 section and decrypting it, it finally sets up the second stage of Emotet in the original offset that the DLL was loaded before.

In addition, the DLL loads additional DLLs and functions to be used later in the execution chain.

Execution breakdown:

Before the main logic starts, there’s a software breakpoint trap serving as a simple anti-debugging technique:

After that, the malware decrypts a string into a registry key:

The key being checked is: Interface{b196b287-bab4–101a-b69c-00aa00341d07}; This key is related to the IEnumConnections interface which is a part of the Component Object Model.

**While I am not sure of the reason the malware developer chose to check for the existence of this specific key, I did read that there are some malwares like Wastedlocker that do the exact same. if someone knows the reason, please dm me, this haunts my dreams :>

If the key exists, the malware continues the execution of the main logic. If it doesn’t, it enters an infinite loop where the process runs but nothing actually happens.

The malware then starts multiple occasions of allocating new memory and copying data. for the sake of clarity, I will name each new memory as “memoryX”, with “X” being the memory allocation count.

First, the DLL loads VirtualAlloc and uses it to allocate 132kb (20A00h) of memory with wrx permissions using an indirect Syscall (will now be referrenced as memory1)

Next, the malware begins copying the code chunks delimited by the “!!!…” characters into memory1

After the transition is complete, the malware starts xoring the memory1’s data in chunks of 32bits. The data is xored with the hardcoded key 0129CCh + the relative offset from which the data is being copied from.

memory1’s data: in the beginning, different Winapi functions followed by memory1’s code

memory1’s data: in the beginning, different Winapi functions followed by memory1’s code

The malware then jumps to the code in memory1. First, lt loads kernelbase.dll by traversing the PEB’s InLoadOrderModuleList.

It then uses GetProcAddress to locate the LoadLibraryEx from kernelbase.dll to load kernel32.dll

string concatenation of LoadLibraryEx and kernel32.dll

string concatenation of LoadLibraryEx and kernel32.dll

LoadLibraryEx is then used to reload kernel32.dll to memory.

After loading all the functions that were visible to us in HxD (seen in the screenshot before), using GetProcAddress the malware then loads VirtualAlloc from kernelbase.dll.

**I am not sure exactly why the malware author won’t use the already loaded VirtualAlloc from kernel32.dll like before. again, i’ll be happy someone sharing the reason and i’ll post his name here with the answer :>

It then allocates an additional memory space (memory2) with the size of 128kb (1FC00h) with wrx permissions.

second memory allocation

second memory allocation

memory section as seen in Process Explorer

memory section as seen in Process Explorer

the malware then starts copying data from memory1 at offset 0xE00 (basically all the code that wasn’t executed yet) to memory2.

The code at memory2 is then decrypted again using xor with the key 3E9h.

After the xoring operation I could see a mapped MZ file in memory2.

The malware then allocates a third memory (memory3) with rwx privileges sized at 144kb, and starts copying data from memory2 to memory3. Interestingly enough, the copying is made in chunks from different locations of the second memory region; this supports the theory that it is indeed a whole PE file but pieces of it. after the copying was finished, memory3 contained the second stage of Emotet.

function used to copy the data from the different memory regions

function used to copy the data from the different memory regions

The malware then calls UnmapViewOfFile to unmap the original DLL’s memory space, and again allocates a new memory of 144kb with rwx permission in that exact location.

It then copies memory3 into the original DLL’s memory space, completing the second stage creation.

Second stage analysis

Before diving into what the second stage is doing, I'd like to point three interesting behaviors:

  • Emotet does heavy use of stack-based value manipulation. I would usually see functions start with a few dozen rows of mov, xor, imul, add, shl instructions on multiple stack variables before the logic actually starts doing something. sometimes the values being changed on stack were used and sometimes they weren’t. Ididn’t find common ground to differentiate those.

stack-based value manipulation example

stack-based value manipulation example

  • Most of the functions’ execution logic is flagged-based; in an effort to make the analysis of the Emotet tedious, each of these functions uses a large while loop, comparing a register that holds a hex value that serves as the flag. Inside the while loop, there are multiple conditional jumps where a hard coded hex value is compared against the flag value that is currently held in that register. This way the malware maintains the right execution flow while making it much harder to track.in the end of each function, the flag value is changed to the flag value of the next function to be executed and the while loop continues. It’s important to note that usually before the flags being checked, it was preceded in a stack-based manipulation routine as mentioned in the previous section. Below is a general diagram I made that portraits this behavior. In the comments we can see the numbers of required execution orders. The source code would generally look like this:

general diagram showcasing the big while loop logic used in many Emotet functions

general diagram showcasing the big while loop logic used in many Emotet functions

  • Emotet uses api hashing to resolve the location of a wanted Winapi function, and executes it using indirect calls right after it detects the function’s location. This is made with a designated function that receives two hashes as arguments. First one is to resolve the DLL, and the other is to resolve the Winapi function inside that DLL.

first one is used to find the DLL, second one is to find the function inside of it

first one is used to find the DLL, second one is to find the function inside of it

Below we can see the first hash passed to the function:

Then, the comparison is made from within the function, to return the DLL’s base address into eax:

Here we can see the second hash being used to resolve the memory location of the wanted Winapi function:

below we can see just how many times it was used in the DLL:

Now when that’s out of the way, I’ll explain the first actions performed by Emotet:

Process information gathering

The DLL grabs a snapshot of all the system’s processes using CreateToolHelp32Snapshot. Below is the function that calls it:

ESI set to 0x2 -> captures all processes

ESI set to 0x2 -> captures all processes

The dwFlags value is set to 0x2, meaning that all processes will be snapshotted.

We can see the handle to the snapshot with Process hacker:

Just to be sure, I dumped that memory and indeed saw the process names:

one of the running process captured inside the snapshot

one of the running process captured inside the snapshot

Using Process32FirstW and then followed by Process32NextW, the traverses the snapshot’s processes, and copies all of the processes names into a destined buffer in ASCII format.

computer processes delimited with a comma

computer processes delimited with a comma

f. Then, the snapshot handle is removed using CloseHandleA.

After collecting both the computer name and the processes name to a new heap memory, using CryptEncrypt, and specifically RSA, it encrypts EA24h(60,000 bits )worth of data from the heap, starting with the information mentioned above.

the soon-to-be encrypted data

the soon-to-be encrypted data

CryptEncrypt function

CryptEncrypt function

It also uses CryptExportKey to export a keyblob, and then executes CryptDestroyKey to destroy the key that was used for the encryption.

using CryptGetHashParam, the malware stores data from which the encrypting hash can be restored.

I assume that all of the above information is sent to the c2 and will be used to decrypt that data on the server-side of Emotet.

Unfortunately I didn’t manage to break down the rest of the information that existed in that heap memory, but can say that the information of the processes, computer name, and cryptographic components required for decryption, are all sent to the c2.

C2 Communications

Then, Emotet starts preparing for the c2-communication to send the above data. the IPs stored in Emotet are decoded, and are resolved before they are needed as a parameter for the used Winapi function. if the communication fails, the steps described below are re-run for the next IP address. The steps are:

  • Using ObtainUserAgentString to fetch the User-Agent HTTP request header that is currently being used.

  • Opening up a connection with wininet’s InterneOpenW.

  • Running InternetConnectW with the IP in queue.

  • Loading http OpenRequestW with POST as the http method. the following HTTP headers are loaded ( the referrer part is also stored within emotet and changes per call of this Winapi function).

  • Adds the following data to be appended to the post request.

  • In summary, we can see that the malware is trying to send a file, whilst here we see the Content-Disposition header that provides the network stack with the first file’s subpart’s information. Unfortunately, since the c2’s are probably not active, the three way handshake fails so we don’t even get to see the information being sent.

  • to make sure this is the end of things, I dynamically ran the malware and saw it continues to try connecting to different c2’s, with different ports but same length:

I made a yara rule that can be used to detect one of Emotet’s maldocs. i found the decoding string key to be unique and effective to find those in VT.

If you made it this far, then thank you :) community feedback is always welcome. hope you enjoyed this blog!


메타데이터
post_id
3533f07b6ff5
slug
dissecting-emotet-3533f07b6ff5
url
https://medium.com/@mozs.amit/dissecting-emotet-3533f07b6ff5
canonical_url
https://medium.com/@mozs.amit/dissecting-emotet-3533f07b6ff5
author_url
https://medium.com/@mozs.amit
status
ok
fetched_at
2026-07-24 00:18:42