Early this year, in March 2018, Microsoft’ Windows Defender Research Team in Redmond published some interesting insights into a massive malware campaign distributing a dropper/loader called Smoke Loader (also known as Dofoil). The main purpose of the documented campaign was to distribute a coin miner payload that is using infected machines to mine crypto currencies. Within 12 hours, Windows Defender recorded more than 400,000 instances, but could deploy appropriate countermeasures on computers running Windows within seconds. As further analysis from Spamhaus Malware Labs revealed, these countermeasures did not stay unattended by the malware authors behind Smoke Loader.

Apparently, as a reaction on Microsoft’ countermeasures, the malware authors behind Smoke Loader made some significant code changes in order to bypass Windows Defender and other Antivirus software. These code changes include:

  • Change in the infection techniques
  • Introduction of 64bit payload

Anti-VM and Anti-Analysis techniques in the packer

What stares out with Smoke Loader is that the packer and the main executable (unpacked payload) is related to each other. It is necessary for the unpacked payload to be loaded by the packer in order to run. In addition, the unpacked code checks for certain markers created by the packer in order to run. When Smoke Loader gets executed in a sandbox (for example a virtual environment), the sample fails to start. The reason for this are Anti-VM and Anti-Analysis techniques that Smoke Loader implemented in the code recently. An initial examination under IDA reveals that the code is obfuscated with jump chains whose sole purpose is to make the static analysis harder.


The code at line number 19 (0040295C Main CMP DWORD PTR DS:[EAX+A4],) reveals that Smoke Loader checks the version of the operating system in PEB structure. In case the operating system where the malware sample gets executed on is less than version 6 (Windows NT 6, which equals to Windows Vista), the malware sample immediately stop the execution. In addition, there are a handful other checks based on debugging flags, which can be traced back using the same tracing technique.

Furthermore, the recent Smoke Loader version also overwrites some of its own code section with new instruction that do also contain anti-analysis code and code related to packer loading.

A call trace helps to determine the functionality of that modified mode as shown below.

Anti-VM checks

Anti-VM checks

The code snipped shown above show code that checks for certain signs of a virtual environment, for example the presence of certain drivers of VirtualBox or certain strings that would trace the environment where the malware sample gets executed to Qemu.

In previous versions, Smoke Loader would create a hallow process and then inject the unpacked code into it. However, after Microsoft spoiled the massive Smoke Loader campaign in March 2018, the most recent version of Smoke Loader injects itself into a running instance of Windows Explorer (explorer.exe) instead of creating a hallow process. The injection is now based on the same technique as used by PowerLoader, which uses SendNotifyMessage for code injection. Also, while previous versions of Smoke Loader were using 32bit code, the most recent version of Smoke Loader contains 64bit code in order to inject itself into explorer.exe on computers that are running a 64bit operating system.

Smoke Loader injecting into explorer.exe
Smoke Loader injecting into explorer.exe

The following code change highlights that the final payload is supposed to be run as thread instead of a separate process.

Process based
Previous version (process based)

Thread based
Recent version (thread based)

The packer creates a shared file map which contains various information on the initial infection, such as the packed binary. This file map is later being used by the executing thread.

Shared file map
Shared file map

The name of the shared file map is generated from VolumeSerialNumber of root drive of the infected machine. This shared file map can be used as an indicator of compromise (IOC).

Additional changes in the code

While the previously string encoding algorithm used by Smoke Loader was based on xor, the most recent version includes an RC4 based string encryption as highlighted on the screenshot above.

RC4 based string decryption
RC4 based string decryption

The following IDA python script can help with static decoding of Smoke Loader: download

In earlier versions of Smoke Loader, the botnet controller domain names (C&C) were encoded using an algorithm that was based on a simple xor subtraction:

def Decodec2(data):
    XorKey = struct.unpack("<B", data[0])[0]
    dst = array.array("B")
    base = data[5:]
    PackLen = struct.unpack("<B", data[4])[0]
    print PackLen
    for i in range(0, PackLen - 1, 2):
        #print  chr(   (   ( ord (base[i]) ^ XorKey) & 0xff) - ((ord(base[i + 1] ) ^ XorKey) & 0xff) & 0xff ),
        dst.append((   ( ord (base[i]) ^ XorKey) & 0xff) - ((ord(base[i + 1] ) ^ XorKey) & 0xff) & 0xff )
    return dst.tostring()

The most recent version of Smoke Loader has been modified by the authors to make use of a more complex encoding scheme which is based on multiple operations:

def swap32(x):
    return (((x << 24) & 0xFF000000) |
            ((x <<  8) & 0x00FF0000) |
            ((x >>  8) & 0x0000FF00) |
            ((x >> 24) & 0x000000FF))
def Decodec2(buf):
    BufLen = struct.unpack("<B", buf[0])[0]
    print "[] Buf len = %d" % BufLen
    XORDword = struct.unpack("<I", buf[BufLen + 1 : BufLen + 1 + 4])[0]
    print "[] XorDword is %d" % XORDword
    XORDword = swap32(XORDword)
    print hex(XORDword)
    x = 0
    dst = array.array("B")

    for i in buf[1:BufLen + 1]:
        x = ord(i) 

        x =  x ^ (XORDword & 0xff) 
        XORDword =  (XORDword >> 8 ) 
        x = x ^ (XORDword & 0xff)

        XORDword =  (XORDword >> 8 ) 
        x = x ^ (XORDword & 0xff)
        XORDword = (XORDword >> 8 ) 
        x = x ^ (XORDword & 0xff)
        x = x - (1 << 8)
        x = -x & 0xff
        print chr(x- 1), 
        dst.append ((x-1))
        XORDword  = swap32(struct.unpack("<I", buf[BufLen + 1 : BufLen + 1 + 4])[0])
    return dst.tostring()

An HTTP request from Smoke Loader to the botnet controller (C&C server) consists of some internals constants as well as system information from the infected machine. The request is formatted as shown below.

Process based
C2 packet format

The HTTP response from the botnet controller (C&C server) is typically an RC4 encrypted payload that can include multiple, so called “plugins” (such as the coin miner mentioned by the Windows Defender Team). The RC4 encrypted payload also includes one of the following commands:

  • i – Download a file from http location field from using command ID 102
  • r – Uninstall Dofoil from system ( followed by ack packet using command ID 114)
  • u – Update dofoil ( download from http location field updated binary )

Based on way Smoke Loader calculates the mutex name an infected machine, we can create a vaccine to prevent Smoke Loader from infecting a machine:

#include <windows.h>

#include <wincrypt.h>

void MD5(BYTE* data, ULONG len, unsigned char *out)
	HCRYPTPROV hProv = 0;
	HCRYPTPROV hHash = 0;
	BYTE rgbHash[16]= {0};
	DWORD cbHash = 16;
	char hash[3] = {0};
	int i = 0;
	CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);

	CryptHashData(hHash, data, len, 0);

	CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0);
	for (i = 0 ; i < 16; i++)
		sprintf(hash, "%.2X", rgbHash[i]);
		strcat(out, hash);
	CryptReleaseContext(hProv, 0);


int main(int argc, char **argv)
	unsigned char *Source = (unsigned char *) malloc(sizeof(char) * 265);
	unsigned char *md5Sum = (unsigned char *) malloc(sizeof(char) * 34);
	DWORD lpVolumeSerialNumber = 0;
	unsigned char *FtString = (unsigned char *) malloc(sizeof(char) * 34);
	int ComNameSize = 16;
	char CompName[MAX_COMPUTERNAME_LENGTH + 0x10] = {0};

	memset(md5Sum, 0x00, 34);
	memset(FtString, 0x00, 34);
	memset(Source, 0x00, 265);
	GetSystemDirectoryA(Source, 260);
	Source[3] = 0x00; 
	GetVolumeInformationA(Source, 0, 0, &lpVolumeSerialNumber, 0, 0, 0, 0);
	sprintf(FtString, "%s%08X%08X", CompName, 0xFEE7D621, lpVolumeSerialNumber);
	MD5(FtString, strlen(FtString), md5Sum);
	sprintf(md5Sum, "%s%08X", md5Sum, lpVolumeSerialNumber);
	printf("%s", md5Sum);
	while(1) Sleep(0x1000);


During the binary code analysis, Spamhaus Malware Labs found some sections in the code that are obviously being used by the author of Smoke Loader for debug purpose. This proves that Smoke Loader is still under heavy development of its authors and is constantly evolving.

Debug variables
Debug variables


Since late 2017, Spamhaus Malware Labs could identify more than 8,000 smoke loader malware samples which call out to over 1,000 unique botnet controllers (C&C servers). In addition, to the latest code changes made by the authors of Smoke Loader in response to the countermeasures by Windows Defender, we do also see a trend in certain Smoke Loader campaigns that are shifting away from the official TLDs over to decentralized TLDs (dTLDs) such as Namecoins .bit. By using decentralized TLDs for botnet C&C hosting, botnet operators try to make their botnet C&C infrastructure more resilient against takedown attempts by security researchers and law enforcement agencies (LEA).

Spamhaus Malware Labs continues to follow the further development of Smoke Loader and takes the appropriate actions to protect Spamhaus users from this threat.

Further reading

Related Spamhaus products

Source link


Please enter your comment!
Please enter your name here