Security Research

A Brief History of SmokeLoader, Part 1

6月 11, 2024 - 15 分で読了


This is Part 1 in our series on the history of SmokeLoader. Go to Part 2.

In May 2024, Zscaler ThreatLabz technical analysis of SmokeLoader supported an international law enforcement action known as Operation Endgame, which remotely disinfected tens of thousands of infections. In the process of providing assistance to law enforcement for the operation, ThreatLabz has documented SmokeLoader for nearly all known versions.

In this two-part blog series, we explore the evolution of SmokeLoader. Initially used as a first-stage downloader to deploy other malware families, SmokeLoader has evolved to include its own framework and expand its capabilities with information stealing functionalities. Over the years, the malware has undergone significant development and improvements, with the latest known version appearing in 2022. This blog provides an overview of SmokeLoader’s origins and initial transformation into a modular operation.

Key Takeaways

  • SmokeLoader is a modular malware family that was first advertised on criminal forums in 2011.
  • Smoke’s primary function is to serve as a downloader and execute second stage malware.
  • SmokeLoader possesses the capability to download a range of additional modules that enhance the malware's capabilities, enabling it to perform tasks such as stealing data, launching distributed denial of service attacks, and mining cryptocurrency.
  • SmokeLoader detects analysis environments, generates fake network traffic, and obfuscates code to evade detection and hinder analysis.
  • SmokeLoader has undergone extensive evolution over the years, with new features, alongside improved encryption, compression, and hash algorithms.


The figure below is a comprehensive timeline of SmokeLoader’s evolution from 2011 to the present.

Figure 1: A timeline of SmokeLoader’s evolution from 2011 to 2022.

Figure 1: A timeline of SmokeLoader’s evolution from 2011 to 2022.

2011-2013: Prehistoric

The first SmokeLoader samples we analyzed date back to 2011. These samples display notable differences and are more rudimentary compared to subsequent iterations. We refer to the earliest SmokeLoader samples as prehistoric because they do not contain a version number.

These early versions of SmokeLoader include an initial module that serves as the foundation for establishing initial communication with the command-and-control (C2) server. The malware achieves this through the utilization of two embedded shellcodes that are injected into two newly created instances of svchost.exe. During this prehistoric period, the code was modified several times, with some samples performing process injection by creating shared sections (using ZwCreateSection and ZwMapViewOfSection) and resuming the main process thread (using ZwResumeThread) to execute the injected code.

Another sample dating to 2012 uses an Asynchronous Procedure Calls (APC) queue technique to inject SmokeLoader into the hollowed svchost.exe processes. The figure below shows SmokeLoader’s process of building the shellcode and injecting it into svchost.exe using the APC queue injection technique.

Figure 2: Early version of SmokeLoader building and injecting shellcode into svchost.exe.

Figure 2: Early version of SmokeLoader building and injecting shellcode into svchost.exe.

One of the shellcodes sends the getload (with the argument login) command to the C2 server, while the other shellcode queries the C2 server with the getgrab command.

The figure below shows the svchost.exe shellcodes for the SmokeLoader sample under analysis.

Figure 3: The svchost.exe shellcodes for a Smokeloader sample circa 2012.

Figure 3: The svchost.exe shellcodes for a Smokeloader sample circa 2012.

Using the getload command, the bot registers itself within the C2 server using HTTP GET requests. Example network traffic from this early Smoke version is shown below.

Figure 4: Example C2 requests from the SmokeLoader version 2012.

Figure 4: Example C2 requests from the SmokeLoader version 2012.

SmokeLoader sends two parameters to the C2 server using the getload command as shown in the table below: 




This specifies the bot ID, which is calculated as a simple MD5 hash of the victim’s computer name.


This specifies the hardcoded seller ID (a.k.a., affiliate ID), which varies per sample (e.g., 77777).

Table 1: SmokeLoader 2012 parameters for the getload command.

In modern versions, SmokeLoader still continues to send a bot ID and seller ID to register itself with the C2 server. Once registered, the server responds to the getload command with a payload that the malware writes to disk and executes.

In addition, the getgrab command can download an additional SmokeLoader grabber module. In this case, the data returned by the C2 server is encrypted with a simple XOR algorithm. Once decrypted, the Portable Executable (PE) file is mapped directly in the current process context by the shellcode.

Information stealer plugins

The source code for the SmokeLoader 2012 panel was leaked. The code sample below shows how the panel manages the various commands it supports.

if ($command == "getgrab") {
} elseif ($command == "getproxy") {
} elseif ($command == "getspoof") {
} elseif ($command == "getcmdshell") {
} elseif ($command == "getload" && isset($cmd["doubles"])) {
} elseif ($command == "getload" && isset($cmd["final"])) {
} elseif ($command == "getload" && isset($cmd["personal"])) {

The getgrab command retrieves the content of the file ./mods/grab on the C2 server. The first 4 bytes of this file correspond to the XOR key used for decrypting the remaining content. The grab executable is a fully-featured information stealer module that is capable of stealing email, FTP, and email passwords.

Unlike the primary SmokeLoader communication channel which uses HTTP GET requests, the communication between the grabber and the C2 is conducted through HTTP POST queries.

Within the mods folder of the leaked panel, there is also a ./mods/shell file that implements a simple remote shell. The leaked panel's source code references other modules including those mentioned by the getproxy and getspoof commands.

First anti-analysis techniques

SmokeLoader is notorious for employing various anti-analysis techniques, which have been incrementally improved with each new version. This section examines the initial anti-analysis techniques observed in the 2012 version of SmokeLoader.

Instead of storing the names of exported functions, the malware utilizes a hash-based approach to locate the addresses of the required APIs. In this version, the algorithm used to hash the API names is relatively simple, as shown in the Python code sample below.

def calc_hash_smoke2012(apiname):
   hash = 0
   for byte in apiname:
       hash = (hash << 8 | hash >> (32 - 8)) & 0xFFFFFFFF
       hash = byte ^ hash
   return hash

The strings used by the malware’s first stage are encrypted. Each encrypted string in the binary is structured as follows: the initial 4 bytes correspond to an XOR key, which is then followed by the encrypted data.

The Python implementation of the algorithm to decrypt the strings is shown in the code sample below.

def smoke2012_string_decrypt(data, key):
   aligned = data
   unaligned = b''
   unaligned_dec = b''
   if n_unaligned := len(data) % 4:
       aligned = data[0:-n_unaligned]
       unaligned = data[-n_unaligned:]
   aligned_dec = xor(aligned, key)
   unaligned_dec = xor(unaligned, key[0:1])
   return aligned_dec + unaligned_dec

In this version, the Smoke C2 URLs are stored among the list of encrypted strings.

2014: Ancient Modularizations

The following section covers new features and modifications to SmokeLoader version 2014, which include a multi-stage loading process, an updated algorithm for generating the bot ID, a separate encrypted C2 list, and more.

During the analysis of a sample dating back to 2014, we discovered the introduction of the string s2k14 that is used as the name of a file mapping. We believe s2k14 is a reference to SmokeLoader version 2014. The figure below shows this string referenced in the code.

Figure 5: SmokeLoader version 2014 string for a file mapping name called s2k14.

Figure 5: SmokeLoader version 2014 string for a file mapping name called s2k14.

One of the most interesting features in this version of SmokeLoader is the malware was split into several loading stages. The introduction of a stager component marked a significant change that became standard in all subsequent versions. Each version since 2014 consists of a stager, a main module, and plugins that implement additional features.

In SmokeLoader version 2014, the ./mods/ folder in the panel includes a ./mods/plugins file that combines multiple plugins into a single file. The prior ./mods/grab information stealing module from previous SmokeLoader versions was split into multiple stealing modules like an FTP/mail stealer module, browser stealer module, and keylogger module, and then packaged in this plugins file. This modification to the plugins persists from this version onward until the most recent version of SmokeLoader.

aPLib stager

The stager introduced in SmokeLoader version 2014 is quite simple. The stager performs the following actions:

  • Decrypts a section of data using a single byte XOR key. 
  • Decompresses the data using aPLib. 
  • Maps the main module in a buffer allocated in the same process context.
  • Executes some simple anti-analysis measures by checking the Process Environment Block (PEB) such as the BeingDebugged and NtGlobalFlags fields.

Then, the stager transfers execution to the main module, creates an instance of svchost.exe, and injects SmokeLoader into this newly created svchost.exe process using APC queue code injection.

The figure below shows how the 2014 version of the SmokeLoader aPLib stager works.

Figure 6: SmokeLoader version 2014 code unpacking and injecting into svchost.exe.

Figure 6: SmokeLoader version 2014 code unpacking and injecting into svchost.exe.

SmokeLoader’s stager underwent significant evolution in subsequent versions, incorporating advanced obfuscation techniques and additional anti-analysis measures. Nevertheless, even in this version, we can observe the presence of rudimentary obfuscation tricks.

For instance, SmokeLoader employs non-polymorphic decryption loops to unravel layers of XOR encryption for functions that are invoked, as shown in the figure below.

Figure 7: SmokeLoader version 2014 stager function decryption.

Figure 7: SmokeLoader version 2014 stager function decryption.


The SmokeLoader seller has provided threat actors with an option to build a sample with (or without) persistence. SmokeLoader’s approach to achieve persistence on the victim's system has undergone numerous changes over time. In earlier versions (2011-2017), SmokeLoader would leverage common Run registry keys and create a startup shortcut as a fallback option (if setting the registry values failed). In addition, SmokeLoader would establish two dedicated threads responsible for safeguarding the modified registry keys.

Bot ID

In the initial version of SmokeLoader, dating from approximately 2011 to 2012, we observed that the bot ID was generated by taking a simple MD5 hash of the victim machine's computer name. Over time, the algorithm to generate the bot ID has undergone slight modifications. Notably, in version 2014, SmokeLoader employed a CRC32 and XOR based algorithm.

Starting from 2014, all versions of SmokeLoader calculate the ID using both the computer name and the volume information, as shown in the figure below. 

Figure 8: SmokeLoader version 2014 bot ID generation.

Figure 8: SmokeLoader version 2014 bot ID generation.

Anti-analysis tricks and plain text strings

Interestingly, SmokeLoader version 2014’s main module stored the malware’s strings in plaintext as shown in the figure below. This is a departure from the initial version where the strings were encrypted.

Figure 9: SmokeLoader version 2014 plaintext strings.

Figure 9: SmokeLoader version 2014 plaintext strings.

This is the first version of the malware that searches for sbiedll, dbghelp, qemu, virtual, vmware, and xen strings to check for libraries and processes related to malware analysis environments. If an analysis environment is detected, SmokeLoader terminates itself to evade detection.

Encrypted C2s

The algorithm used to decrypt the list of encrypted C2s is one of the SmokeLoader components that has undergone the most changes across versions.

While the strings in the main module of SmokeLoader version 2014 are stored in plaintext, the list of C2 servers is encrypted. To decrypt the list, a custom XOR-based decryption algorithm is employed. The code contains a table of pointers, with each pointer referencing a string that is prepended by a byte that is used as an XOR key, which is followed by three unused bytes. The next byte is the size of the encrypted C2 URL and the remaining bytes are the encrypted C2 URL. The Python code below demonstrates the C2 decryption algorithm used in SmokeLoader version 2014.

def smoke2014_c2_xor_decrypt(enc_data):
   key = enc_data[0]
   size = enc_data[4]
   enc = enc_data[5:]
   dec = b''
   for i in range(0, len(enc)-1, 2):
       dec += (0xff&((enc[i] ^ key) - 
              (enc[i+1] ^ key))).to_bytes(1, 'little')
   return dec

Anti-C2 patching mechanism

An interesting addition to SmokeLoader version 2014 is the implementation of what appears to be a simple copy-protection mechanism as shown in the figure below. 

Figure 10: Simple copy-protection mechanism implemented in SmokeLoader version 2014 version.

Figure 10: Simple copy-protection mechanism implemented in SmokeLoader version 2014 version.

The malware calculates the CRC32 value of the C2 URL string and compares it with a predefined expected value at various points in the code. This mechanism is likely designed to prevent other hackers from creating a builder that patches samples with new C2s, and therefore reduce potential sales of SmokeLoader.


The communication protocol for SmokeLoader version 2014 is similar to prior versions. SmokeLoader uses a simple text-based protocol that is encrypted and sent to a C2 server. The syntax for the protocol is the following:


SmokeLoader’s version 2014 panel recognizes the following arguments:




The C2 panel accepts a set of commands. Depending on the specified command additional arguments must be specified.


This argument is the bot ID and its length must be 40 bytes.


Additional information given with some commands.


Operating system version.


Victim’s Windows operating system architecture.


Mainly used together with the getload command to request updates or tasks.


Used in different commands for different purposes. For example, to ask for an update or to indicate that the update was successfully executed.


Used together with the getsocks command to specify the proxy’s port.


Process name included with the procmon command.


Additional flag that may be sent with the getload command.


Additional flag that may be sent with the getload command.


Additional flag that may be sent with the getload command.


If the getshell command is sent to the server, this argument contains the results of the executed commands.


Used together with the formgrabftpgrab, and keylog commands. This argument contains the stolen information.


If a file is submitted, the filedata argument contains the content of the file.

Table 2: SmokeLoader version 2014 network protocol.

The panel is able to handle the following commands (i.e., the command name specified in the cmd argument):

Command (cmd)




*with additional arguments

One of the main commands and differs depending on specified arguments:

  • file: If the getload command is included with the file argument, the panel handles the command in different ways depending on the value of the file argument:
    • If the file argument's value is u and the run argument is not set, the bot asks for an update. The server could return the update executable or a URL to download the update from.
    • If the file argument's value is u and the run argument is set, the bot confirms the successful execution of the update.
    • If the file argument's value is not u and the run argument is not set, the bot asks for the next task. The server tracks in its database the last task given to each bot. When this command is received, the server returns the next task (if any).
    • If the file argument's value is not u and the run argument is set, SmokeLoader confirms whether the last task was executed correctly.
  • doubles: If the bot specifies this argument together with the getload command, the panel sets the doub flag in the database for the associated victim ID. The purpose of this flag is unknown.
  • removed: The bot can specify a removed argument together with the getload command to confirm an uninstall request was received from the server. The panel deletes the bot ID from the database upon receipt.
  • personal: A personal argument can be given together with the getload command to ask for personal tasks. The argument should contain the ID of the personal task. If the configured task is set as local in the database, the server would return a file in the response to be executed by the bot. Otherwise, a URL is provided and the bot downloads and executes the content from the given URL.



*without arguments

If this command is received without arguments, it could be interpreted as a hello or knock query, and used by the bot to start the conversation with the server. When a server receives a getload command without arguments, the response could vary depending on the database configuration.

  • If there is a pending update for the bot, the server responds with the string “Smku”.
  • If there is a personal task for the bot, the server responds with the string “Smki”.
  • If there is a pending removal request for the bot, the server responds with the string “Smkr”.
  • In the remaining cases, the server responds with the total number of configured tasks followed by the configuration’s rules for each plugin. Each configuration item starts with the “|:|” string followed by the name of the ruleset and the plugin’s rules:
    • |:|socks_rules=
    • |:|hosts_rules=
    • |:|shell_rules=
    • |:|fakedns_rules=
    • |:|filesearch_rules=
    • |:|procmon_rules=
    • |:|ddos_rules=
    • |:|keylog_rules=


Used by the bot to inform the server that it has enabled the SOCKS proxy feature. The server will attempt to validate the bot proxy is active by connecting to the bot’s IP address on the specified port.


Confirms that the hosts specified in the hosts_rules have been successfully spoofed.


The bot submits results from executed shell commands to the server. The shell argument contains the results.


Submits the results from the form grabber plugin to the server. The grab argument must contain a base64 encoded string that, once decoded, contains a comma separated list of grabbed data.


Submits the results from the ftp grabber plugin to the server. The grab argument must contain a base64 encoded string with the stolen data.


Submits stolen information to the server. The argument data contains the stolen information.


Submits information about installed security products to the server. The information is submitted in the info argument. The submitted string is split by the delimiter 777. The first substring contains information about the installed antivirus. The second substring contains information about installed firewalls.


If one of the processes configured with the rules in the procmon_rules configuration item is found on the victim machine, the bot notifies the server about the presence of the process, submitting the name of the process in the procname argument. The server could respond with a file to be executed or a URL to download the file from.


Confirms the DDoS attack configured with the ddos_rules has been successfully performed.


Submits the information captured by the keylogger plugin to the server. The grab argument must contain a base64 encoded string with captured data.


If the HTTP query is application/bin, and the command is getfilesearch, the bot submits to the server the content of the files that were found by the filesearch plugin according to the rules specified in the filesearch_rules configuration item. The content of the file is given in the filedata argument. 

Table 3: The commands supported by SmokeLoader’s 2014 C2 server.

SmokeLoader version 2012 sent commands and arguments as plaintext using HTTP GET requests. In version 2014, the network protocol was updated to send the command and argument data via HTTP POST requests. The POST request body consists of an initial DWORD containing the size of the data, followed by a DWORD that functions as an RC4 key required to decrypt the remaining data.

To Be Continued

In our journey through Part 1 of this series, we explored the early versions of SmokeLoader, from its initial rudimentary framework to its adoption of a modular structure and encrypted network protocol. In Part 2, we dive deeper into SmokeLoader’s progression toward a more sophisticated, modular malware family with advanced anti-analysis techniques.

Zscaler Coverage

Zscaler sandbox coverage

In addition to sandbox detections, Zscaler’s multilayered cloud security platform detects indicators related to SmokeLoader at various levels with the following threat names:


form submtited


dots pattern


このフォームを送信することで、Zscalerのプライバシー ポリシーに同意したものとみなされます。