The Mystery of Metador | Unpicking Mafalda’s Anti-Analysis Techniques

Overview

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.

String Obfuscation

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:

  • Splitting the strings into multiple portions, with a maximum portion length of 9 characters.
  • Encrypting and encoding each string portion. Mafalda encodes a portion of an obfuscated string using the bitmask 0x7F and XOR-encrypts the portion using a portion-specific XOR key of one byte.

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).

Mafalda’s function for decoding and decrypting string portions
Mafalda’s function for decoding and decrypting string portions

String Encryption

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.

Encrypted debugger messages
Encrypted debugger messages

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.


Encrypted (top) and plain text (bottom) Mafalda execution log
Encrypted (top) and plain text (bottom) Mafalda execution log

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.

Function Parameter Obfuscation

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).

Function parameter obfuscation; v53 is a portion of an obfuscated string
Function parameter obfuscation; v53 is a portion of an obfuscated string

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.

Incorrect string decoding and decryption
Incorrect string decoding and decryption

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.

Correct string decoding and decryption
Correct string decoding and decryption

Execution Flow Obfuscation

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.


Execution flow obfuscation through a thunk function
Execution flow obfuscation through a thunk function

Next, we discuss some of the obfuscation techniques that the developers of Mafalda have applied to the obfuscated code segments.

Purposeless Instruction(s)

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.
An example of some purposeless instructions in Mafalda
An example of some purposeless instructions in Mafalda

Opaque Predicates

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.

An opaque predicate
An opaque predicate

Unconditional Jump (JMP) Obfuscations

The obfuscated code segments in Mafalda contain instructions that obfuscate unconditional jumps to locations in the memory mapped to Mafalda. This involves:

  • Conditional execution based on a flag value in the 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; or
  • Use of multiple, instead of one,  unconditional jumps (trampolines) to direct execution to a given destination location.

The 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.


Unconditional jump obfuscations
Unconditional jump obfuscations

Conclusion

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.