At the inaugural LabsCon, we unveiled Metador, a previously unreported threat actor that targets telecommunications, internet service providers, and universities in the Middle East and Africa. We observed Metador using two versions of a feature-rich backdoor, dubbed ‘Mafalda’, one of which features anti-analysis techniques to make analysis challenging.
In this article, we provide a deep dive into the anti-analysis techniques that Mafalda implements. This article complements our previous report on Metador and offers a deeper understanding of how Mafalda tries to hinder analysis and make detection and attribution more challenging for analysts.
The implementation of Mafalda suggests that the malware is maintained and developed by a dedicated team. Mafalda includes comprehensive backdoor command documentation with comments for a separate group of operators. In addition, Mafalda implements an execution log that the malware maintains when it runs on an infected system. The log provides detailed information about the execution of the malware on the system and therefore is a rich resource to analysts. Our previous report discusses the functionalities of Mafalda in greater detail.
Throughout our analysis, we retrieved and analyzed two variants of Mafalda, which we refer to as ‘Mafalda clear build 144’ (compiled with a timestamp of April 2021) and its successor, ‘obfuscated Mafalda variant’ (compiled with a timestamp of December 2021). The newer, obfuscated Mafalda variant extends the backdoor functionalities that the older variant provides and implements the anti-analysis techniques that we cover in this article.
Mafalda uses obfuscated strings for different purposes, for example, to dynamically resolve library function addresses through library and library export names, or to store content in the execution log that Mafalda maintains. Mafalda obfuscates strings by:
Therefore, to restore an obfuscated string into a valid string, Mafalda first decodes and decrypts each of the string’s portions, and then concatenates the string portions together.
The figure below depicts a snippet of the function that Mafalda executes to decode and decrypt a portion of an obfuscated string (a2 is a portion of an obfuscated string, v2 is an XOR key).
In addition to the string obfuscation approach, Mafalda works with encrypted versions of strings that may represent an information source to malware analysts. Such strings include segments of the execution log and debugger messages that Mafalda generates.
We noted that Mafalda prints encrypted debugger messages if the name of the computer where it executes is WIN-K4C3EKBSMMI
, possibly indicating the name of the computer used by the developers.
In contrast to the Mafalda clear build 144, the obfuscated Mafalda variant writes encrypted strings to its execution log. Given that this log provides extensive information about the operation of the malware, encrypting the execution log serves to hinder analysis.
We did not discover evidence of functionality within Mafalda for decrypting the strings it encrypts. This suggests that string decryption takes place at Metador’s command-and-control servers – a simple yet effective technique for hindering analysis.
Mafalda often obfuscates numerical function parameters by calculating parameter values prior to function execution using arithmetics and bitwise operations. It may also first calculate a value using arithmetics and bitwise operations. If the computed value does or does not match a predefined value, Mafalda assigns the correct values to the obfuscated parameters. The alternative branch assigns wrong values to the obfuscated parameters.
Mafalda applies this obfuscation approach when it executes the function that the implant uses to decode and decrypt portions of obfuscated strings (labeled j_str_resolve_sub_18014FE4D
in the figure below).
This obfuscation technique may direct emulation tools to wrong execution branches and function parameter values – analysts may use emulation to automate the decryption and decoding of portions of obfuscated strings across the whole implementation of Mafalda. For example, the iterateAllPaths
feature of the flare-emu tool attempts to emulate all execution paths to a given function and the function itself. For automated deobfuscation, malware analysts typically use this feature to emulate functions that deobfuscate strings at runtime. When we used the iterateAllPaths
function to emulate j_str_resolve_sub_18014FE4D
, Mafalda often directed the tool to the wrong values of the function’s obfuscated parameters. This resulted in incorrect string decoding and decryption. In the figure below, rn
and 9
are incorrectly decoded and decrypted strings.
However, when we used the flare-emu emulateRange
functionality for emulating only specific implementation regions in which Mafalda invokes j_str_resolve_sub_18014FE4D
, the tool was more accurate in assigning correct function parameter values. This resulted in correct string decoding and decryption. In the figure below, Sleep
and kernel32
are correctly decoded and decrypted strings – Mafalda uses these strings to invoke the Sleep function that is implemented in the kernel32.dll
library file.
Mafalda is obfuscated at implementation-level such that the compiled code of the implant consists mainly of obfuscated and non-obfuscated code segments. The majority of the non-obfuscated code segments are functions that implement Mafalda functionalities. The obfuscated code segments contain heavily obfuscated code that serves no purpose but to confuse analysis tools and increase cognitive load.
In most cases, Mafalda directs execution to the obfuscated code segments through thunk functions – functions that implement only a single JMP
(jump) instruction that directs execution to a destination location. An obfuscated code segment ultimately returns execution to a location that is in the relative vicinity of the appropriate thunk function. This location is the beginning of a non-obfuscated code segment — often the prologue of a function that implements Mafalda functionalities. In summary, the obfuscated code segments effectively obfuscate the invocation of non-obfuscated functions.
The figure below depicts an instance of execution flow obfuscation through thunk functions. The thunk function entryRoutine
directs execution to the location entryRoutine_0
, which marks the beginning of an obfuscated code segment. This code segment ultimately returns the execution to a non-obfuscated code segment – the prologue of the function sub_17808D17767
.
Next, we discuss some of the obfuscation techniques that the developers of Mafalda have applied to the obfuscated code segments.
The obfuscated code segments in Mafalda contain instructions that serve no purpose in the execution of the code. These instructions exist only to increase the cognitive load when an analyst analyzes the instruction stream. In Mafalda, purposeless instructions are placed sequentially or are intertwined with other instructions.
The table below lists the majority of the purposeless instructions that we encountered in Mafalda’s obfuscated code segments (p denotes an instruction parameter).
Instructions | Description |
rol p,0 / ror p,0 | Rotates p left or right by 0 bits. |
xchg p1, p2 xchg p1, p2 |
Swaps p1 and p2 two times. |
xchg p, p | Swaps p with itself. |
pause | Provides a spin-wait loop hint to the processor. The Mafalda developers have placed this instruction very often in the obfuscated code segments to increase cognitive load. |
bswap p bswap p |
Reverses the byte order of p twice. |
push p pop p |
First preserves p on the stack and then restores p from the stack without modifying p between these actions. |
pushfq popfq |
First preserves the RFLAGS register on the stack and then restores RFLAGS from the stack without modifying RFLAGS between these actions. |
The obfuscated code segments in Mafalda implement simple opaque predicates. They involve first issuing the cmp
instruction for comparing a value against itself, which always evaluates to TRUE, and then evaluating the ZF
, PF
, or the SF
flag to direct the execution to a given execution branch.
The table below lists the majority of the opaque predicates that we encountered in Mafalda’s obfuscated code segments. p
denotes an instruction parameter and addr
a memory address mapped to Mafalda: a virtual address or a parameter to a conditional or unconditional jump instruction.
Instructions | Description |
cmp p, p JNP/JNZ/JNE/JS [addr1] [addr2]: [ . . . ] |
The branch at address [addr1] is never taken, the branch at address [addr2] is always taken. |
cmp p, p JP/JZ/JE/JNS [addr1] [addr2]: [ . . . ] |
The branch at address [addr1] is always taken, the branch at address [addr2] is never taken. |
cmp p, p JNP/JNZ/JNE/JS [addr1] JMP [addr2] [addr3]: [. . . ] |
The branch at address [addr1] is never taken, the branch at address [addr2] is always taken, the branch at address [addr3] is never taken. |
The execution branches that are always or never taken may contain any instructions, such as the purposeless instructions mentioned above.
The obfuscated code segments in Mafalda contain instructions that obfuscate unconditional jumps to locations in the memory mapped to Mafalda. This involves:
RFLAGS
register, for example, the ZF
or the PF
flag, such that any of the possible flag values (0 or 1) result in the execution of the code at a given destination location; orThe table below lists the majority of the unconditional jump obfuscations sets that we encountered in Mafalda’s obfuscated code segments (addr
denotes a memory address mapped to Mafalda: a virtual address or a parameter to a conditional or unconditional jump instruction).
Instructions | Description |
JP [addr1] JNP [addr1] [addr2]: [ . . . ] |
The branch at address [addr1] is always taken, the branch at address [addr2] is never taken. |
JS [addr1] JNS [addr1] [addr2]: [ . . . ] |
The branch at address [addr1] is always taken, the branch at address [addr2] is never taken. |
JB [addr1] JNB [addr1] [addr2]: [ . . . ] |
The branch at address [addr1] is always taken, the branch at address [addr2] is never taken. |
[addr]: call $ + [offset] [ . . . ] [addr+offset]: [. . . ] |
Executes the instructions placed at the offset [offset] from the address [addr] where the call instruction resides. The instructions between [addr] and [addr+offset] are never executed. |
JMP [addr1] [ . . . ] [addr2]: JMP [addr3] [ . . . ] [addr1]:: JMP [addr2] [ . . . ] [addrN]: JMP [dest_addr] [ . . . ] [dest_addr]: [ . . . ] |
Directs execution to multiple locations (addresses [addr1] to [addrN]) through trampolines until the final destination location at address [dest_addr] is reached. The instructions between the trampolines are never executed. We observed up to 17 trampolines as part of such an unconditional jump obfuscation. |
Mafalda’s anti-analysis techniques make the analysis of the malware challenging, which helps the Metador threat actor to delay effective defensive actions against its operations. Metador takes a number of measures at infrastructure- and network-level to hide and protect its operation from defenders. The techniques that this article discusses add to these measures at an executable, malware-implementation level.
By complementing our previous publication on Metador, we hope that this post will encourage collaboration towards further unveiling the mystery of this threat actor.