[RE026] A Deep Dive into Zloader - the Silent Night


1. Overview

Zloader, a notorious banking trojan also known as Terdot or Zbot. This trojan was first discovered in 2016, and over time its distribution number has also continuously increased. The Zloader's code is said to be built on the leaked source code of the famous ZeuS malware. In 2011, when source code of ZeuS was made public and since then, it has been used in various malicious code samples.

Zloader has all the standard functionality of a trojan such as being able to fetch information from browsers, stealing cookies and passwords, capturing screenshots, etc. and for making analysis difficult, it applies advanced techniques, including code obfuscation and string encryption, masking Windows APIs call. Recently, CheckPoint expert published an analysis of a Zloader distribution campaign whereby the infection exploited Microsoft's digital signature checking process. In addition, Zloader has also recently partnered with different ransomware gangs are Ryuk and Egregor. This can indicate that the actors behind this malware are still looking for different ways to upgrade it to bypass the defenses. Here is the ranking of Zloader according to the rating from the AnyRun site:

Source: https://any.run/malware-trends/zloader

Most recently, multiple telecommunication providers and cybersecurity firms worldwide partnered with Microsoft's security researchers throughout the investigative effort, including ESET, Black Lotus Labs, Palo Alto Networks' Unit 42, and Avast. They took legal and technical steps to disrupt the ZLoader botnet, seizing control of 65 domains that were used to control and communicate with the infected hosts.

In this article, we will provide detailed analysis and techniques that Zloader uses, including:

  • How to unpack to dump Zloader Core Dll.
  • The technique that Zloader makes difficult as well as time consuming in the analysis process.
  • Decrypt strings used by Zloader by using both IDAPython and AppCall methods.
  • Apply AppCall to recover the Windows API calls.
  • Process Injection technique that Zloader uses to inject into the msiexec.exe process.
  • Decrypt configuration information related to C2s addresses.
  • How Zloader collects and saves information in the Registry.
  • The Persistence technique.

The analyzed sample used in the article: 034f61d86de99210eb32a2dca27a3ad883f54750c46cdec4fcc53050b2f716eb

2. Unpacking Zloader Core Dll

First, check the sample with Nauz File Detector:

By collecting and combining information about sections from ExeInfo, entropy in DiE as well as the size of the DLL file, we can confirm that this DLL is packed:

For unpacking, use x64dbg to load Dll file, set a bp NtAllocateVirtualMemory. Then, modify the breakpoint's condition as follows:

Execute with F9 and wait until the breakpoint is hit (after about 1126120 hits):

Following the allocated memory regions, after the 3rd hit, the core Dll of Zloader will be unpacked:

Dump this Dll to disk, the file has MD5: 9b5589fcd123a3533584a62956f2231b.

3. Anti-analysis

To consume time of the analyst, Zloader uses meaningless functions, or rewrites functions that look very complicated but only to perform simple tasks such as AND, OR, XOR, ADD, SUB, etc.

For example, a function that does a meaningless task, however it can cause a delay in execution in a sandbox environment:

Functions that perform AND, OR operations:

4. Decrypt wide string

4.1. Use IDAPython

All strings that the core DLL uses are encrypted. The wide string decoder function will take two parameters as input:

  • First parameter: the address containing the encrypted string.
  • Second parameter: the address where the string is stored after decoding.

The pseudocode at the f_zl_decrypt_wstring decryption function looks confusing, but if we look closely, the function performs a simple xor loop with the decryption key is "PgtrIPF-2ftOj00Ox":

Based on the above pseudocode, the python code that performs decryption as follows:

With the help of IDAPython, we can automate the whole process of string decoding and add annotations at the decryption functions in IDA for further analysis. The entire python code is as follows:

The results before and after the script execution will make the analysis easier:

4.2. Use IDA AppCall

If you don't have time to dig into the decryption implementation of the function, or when the algorithm is too complex, we can use IDA's useful feature known as AppCall, to help decrypt the data. Basically, Appcall is a mechanism used to call functions inside the debugged program from the IDA debugger. Before applying AppCall, the first thing is to given a function with a correct prototype. For example, the function f_zl_decrypt_wstring has the following protoype:

wchar_t *__cdecl f_zl_decrypt_wstring(wchar_t *encString, wchar_t *decString);

Note again that in order to use AppCall, the program must be debugged. As shown below, IDA is stopping at the breakpoint set at DllEntryPoint:

Then execute the below python script to decode and add comments related to decoded strings at the functions:

The final result should be similar to the image below:

5. Decrypt ansi string

5.1. Use IDAPython

Besides the function to decode wide strings, Zloader also uses the function to decode ansi strings. This function also accepts two arguments:

  • First parameter: the address containing the encrypted string.
  • Second parameter: the address where the string is stored after decoding.

Similar to the above f_zl_decrypt_wstring function, the pseudocode of the f_zl_decrypt_string function looks quite messy, but it still uses an xor loop to decrypt with the decryption key still "PgtrIPF-2ftOj00Ox":

Here is the full python code to automate the whole process of decoding strings and adding comments at functions:

The results before and after the script execution

5.2. Use IDA AppCall

To use AppCall, same as above, need to define correctly the prototype for the f_zl_decrypt_string function as follows: char *__cdecl f_zl_decrypt_string(char *encString, char *decString);

Slightly modified the script used for decoding the wide strings above:

Result after running the script:

6. List of Dlls used by Zloader

In the list of strings decrypted by the f_zl_decrypt_string function above, there is a string after the decryption that is quite meaningless. Going to this address, after diving into it I noticed that the first parameter passed to the function is an array containing the addresses of the encrypted strings. Based on the corresponding index value of the array will access the address containing the corresponding encrypted string:

Going to the g_ptr_enc_dll_str array (renamed above) will see a list of addresses as shown below:

Modify the script to decode the specific Dll strings, the results obtained when executing the script are as follows:

To summarize, we have a list of indexes corresponding to the DLLs that Zloader can use to retrieve the addresses of APIs:


Dll Name













































7. Dynamic APIs resolve

Similar to other advanced malware… Zloader will also get the address of API function(s) through searching by pre-computed hash value based on API function name.

As shown in the above figure, the f_zl_resolve_api_func_ex function takes two parameters:

  • (1): The first parameter is dll_index. Based on this parameter, the function will decode the name of the corresponding Dll, then call the LoadLibraryA function to get the base address of this Dll.

  • (2): The second parameter is pre_api_hash. This parameter is the pre-computed hash of the API function name. The function f_zl_resolve_api_func_ex will call f_zl_resolve_api_func to retrieve the corresponding API address:

The pseudocode at the f_zl_resolve_api_func function as follows:

The entire pseudocode of the function that performs the hash calculation by the API function name is as follows:

Based on the above pseudocode, re-implement using Python code as follows:

Results when using the above function to find API functions corresponding to hash values hash 0xFDA8B77, 0xB1C1FE3, 0x8ADF2D1

With all the above analysis results, it is possible to write an IDAPython script to recover all the APIs that Zloader uses. However, to avoid having to dig into Zloader's hashing algorithm for each analysis, here I will use AppCall to do this task. The python code that uses AppCall is as follows:

Note, Zloader has many areas of code that call to the f_zl_resolve_api_func_ex function, but there will be areas of code that do not have any reference to it and that area has not been defined as a complete function. Therefore, to be able to run the above script, it is necessary to create functions for those first. The final result after executing the script will be as follows:

However, as shown in the figure there are still places where the API function can't be recovered, that's because Zloader has performed the previous calculation of the dll_index and pre_api_hash values and saved them in the register. After that, call the f_zl_resolve_api_func_ex function:

8. Process Injection Technique

Zloader, when executed, will inject Core Dll into the msiexec.exe process. The whole process is as follows:

  • Use the CreateProcessA API function to create the msiexec.exe process in the SUSPENDED state.

  • Get SizeOfImage value of Zloader Dll being loaded by rundll32.exe/regsvr32.exe. Use the VirtualAllocEx API function to allocate new memory inside the msiexec.exe process:

  • Allocate heap memory, copy the entire contents of the Dll into this heap:

  • Generate a random number and use it to encrypt the entire payload stored in the heap:

  • Use the WriteProcessMemory API function to write the entire encrypted payload from the heap to the previously allocated memory in the msiexec.exe process:

  • Continue to use the VirtualAllocEx API function to allocate a second memory region has size of region are 66 bytes in the msiexec.exe process. This memory region will be used to decrypt the entire encrypted Dll above. Update the STARTUPINFO structure created by the CreateProcessA function before, the data here are the assembly code that will be used to decrypt the encrypted Dll. Then, call the WriteProcessMemory function to write the updated contents of STARTUPINFO to the newly created memory region.

  • Finally, use the GetThreadContext, SetThreadContext, ResumeThread or CreateRemoteThread API functions to execute the msiexec.exe process. At this point, the entry point executed at msiexec.exe will be the memory region that containing the code to perform the decrypting mission:

  • After decrypting the entire Zloader Dll, it will jump to the RVA address of 0xF270 (File offset: 0xE670) to execute the main tasks of the malware:

9. Decrypt Zloader config

The configuration info of the Zloader has been encrypted and stored in the .rdata section. The decrypt function takes two parameters are the encrypted configuration data and the key used to decrypt:

Inside the function f_zl_decrypt_config will use the RC4 algorithm to decrypt the data:

With the analyzed results, we can use IDAPython code below to perform the decoding:

Result after executing the script:

10. Collect and save configuration in Registry

When first executed, Zloader will collect information about the victim including volume_GUID, Computer_Name, Windows version, Install Date, create random folders at %APPDATA%, generate a random registry key at HKEY_CURRENT_USER\Software\Microsoft, then encrypt all relevant information and save it in the created registry:

The information stored in the registry is similar to the following:

To decrypt the data stored in the above Registry, use the decoded embedded RC4 key above. With the support of CyberChef, we can easily decrypt data as follows below:

11. Persistence technique

Zloader reads the entire contents of the core Dll from disk into the memory region, then writes to a random dll in a directory created above at %APPDATA%:

Create persistence key at HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run:

12. References

Click here for Vietnamese version.

Tran Trung Kien (aka m4n0w4r) 

Malware Analysis Expert


R&D Center - VinCSS (a member of Vingroup)

No comments:

Post a Comment