Table of Contents
In this Spotlight, we take another look at GuLoader. The malware family is active since at least 2020. It gained some attention because of its evasion techniques and abusing legitimate and popular cloud services to host its malicious payloads. The downloader is commonly used to deliver other malware families such as FormBook, XLoader, and Lokibot. After we took a closer look at GuLoader’s evasion techniques in a Threat Bulletin, we observed some additional behavior later that year.
Recently, we collected samples that are different from the samples we have seen before. The file that executes GuLoader’s shellcode has changed, and the functionality of GuLoader has been extended compared to our last Spotlight. The sample in discussion leads to the execution of Lokibot as indicated by the extracted configurations in Figure 1.
The main functionality of GuLoader is implemented as shellcode, and typically an executable takes care of loading the shellcode into memory and transferring the execution flow to it. So far this executable was written in VB6. However, the executable in this analysis is a signed NSIS installer that leads to the execution of GuLoader.
During the installation process, the installer extracts multiple files to the hard disk including a DLL (Dynamic Link Library) named “System.dll”, and a file named “Gestisk.For” (Figure 2.).
While the name for the DLL seems to be consistent across similar samples, the name of the second file can vary. After writing “System.dll” to the hard disk, it is loaded by the installer and used to call WinAPI functions to allocate memory where the shellcode will end up alter on.
Previous samples written in VB6 called the WinAPI functions directly instead of using a separate DLL.
At first glance, we can see the typical behavior of GuLoader. It tries to detect an analysis environment and if none was found it injects the shellcode into another process instance of the executable.
Next, the second instance downloads and executes the payload from the well-known cloud service Google Drive. When comparing the memory dump of the shellcode with memory dumps from older samples, we can see that GuLoader stopped storing the strings in plaintext. Instead, they are decrypted at runtime and stored in a separate memory region (Figure 3.).
Figure 3: Encrypted strings embedded in shellcode (left), and decrypted strings stored in a separate memory region (right).
VMRay Analyzer uses special triggers that allow obtaining the region which contains the decrypted strings.
Moving on to the observed function calls, we can see that the sample utilizes additional WinAPI functions compared to previous ones. Figure 4. lists additional function calls that we discuss next.
Figure 4: List of additional WinAPI functions observed in newer samples.
While we have seen calls to functions related to enumerating products and services in previous samples, the registration of a new exception handler and the examination of device drivers have been added recently. This leads to the assumption that GuLoader is still under active development.
Given the function log, we can see that the address of the exception handler is part of the shellcode (Figure 5.).
This exception handler first checks if the exception was raised because of a software breakpoint. Next, the function inspects the CPU registers to detect the presence of hardware breakpoints. In case no breakpoint is set, the handler continues to change the instruction pointer. The new value depends on the current instruction pointer and the byte followed after the int3 instruction that triggered the exception handler (Figure 6). If a hardware breakpoint is set, the handler doesn’t change the instruction pointer, subsequently executing invalid instructions.
Additionally, the function checks for int3 instructions between the current and the new instruction pointer value.
By registering the exception handler, GuLoader uses int3 instructions as relative jumps. Because debuggers like WinDbg and x64dbg use int3 instructions for software breakpoints, this approach interferes with debugging if the debugger handles these exceptions first.
A deeper look at the function log reveals that multiple WinAPI functions are called from the same address within the shellcode (Figure 7.). This is an indicator that some kind of wrapper function takes care of calling the WinAPI functions.
In this example, GuLoader uses such a function to partially overwrite its code before calling the actual WinAPI function. Figure 8. shows the part of the wrapper function that overwrites the code by xoring it with the return address before and after the call instruction.
By overwriting code before the calls, GuLoader avoids being extracted correctly by analysis tools that use WinAPI functions as memory dump trigger.
Looking at the list of called functions, we can see that GuLoader gathers information about the
- name of installed drivers EnumDeviceDrivers and GetDeviceDriverBaseNameA)
- publisher of installed products (MsiEnumProductsA and MsiGetProductInfoA)
- services in the SERVICES_ACTIVE_DATABASE
The resulting strings are then hashed using a customized djb2 algorithm and compared against a block list of pre-computed values of analysis environment artifacts.
Figure 9: Blocklist of pre-computed values of analysis environment artifacts
If the calculated value is present in the block list, GuLoader stops its execution and therefore evades the analysis.
This technique was used earlier with the original djb2 algorithm. In this particular sample, the djb2 algorithm is customized in a way that the hash is xored with the key 0x0C93EB2D8 in each iteration (Figure 9.)
def djb2_custom(s: bytes) -> int:
hash = 5381
for x in s:
hash = ((hash << 5) + hash) + x
hash = (hash ^ 0x0C93EB2D8) & 0xFFFFFFFF
Figure 10: Customized djb2 algorithm in Python
In general, values of the block list are indicators analysts can take advantage of for detection and identification as long as the algorithm remains the same across samples. GuLoader prevents this by slightly changing the algorithm.
Finally, GuLoader creates another process of the installer, injects code, and delivers the payload. In this case, the payload is Lokibot and hosted on Google Drive.
VMRay Analyzer extracts the malware configuration for both malware families, which eases the detection and identification of infected systems.
Extracted Payload URLs
In addition to Google Drive being abused to host the malicious payload, we have seen other services in our extracted configurations.
Figure 10. shows the distribution of hostnames. While Google Drive remains the most common one, other cloud services like Microsoft OneDrive are used a well.
In this post, we took another look at GuLoader with a focus on behavioral differences compared to past samples. We have seen that not only the executable, which leads to GuLoader’s shellcode has been changed but also its functionality has been further extended.
While GuLoader utilizes new techniques to search for artifacts revealing an analysis environment, some of the existing logic changed to further thwart detection and analysis attempts. Given VMRay Analyzer’s unique monitoring approach, GuLoader can’t detect the presence of the sandbox and reveal its malicious behavior leading to the delivery of Lokibot. The extracted malware configuration for both families allows analysts and incident responders to quickly take actions to prevent the infection and identify already compromised machines.
GuLoader Payload URL: