Reverse engineering

TDSS part 2: Ifs and Bots

April 19, 2011 by ESET Team


In our previous Infosec Institute article, “TDSS part 1: the x64 Dollar Question”, we looked at the distribution and installation mechanisms used by TDL4. For the second part of the series, we look in more depth at the internals of the malware, starting with the user-mode implementation of the bootkit’s bot functionality.

1 The Bot

TDL4 injects two modules into processes in the system, cmd.dll and cmd64.dll, as described in corresponding subsections below. Firstly, though, we look at the configuration file cfg.ini, which stores configuration information within the hidden file system. The general structure of the file is essentially the same as in the TDL3/TDL3+ rootkit but includes some additions and modifications:

// main section with information on kernel-mode driver and partner

version=0.03         // version of the kernel-mode driver
aid=30067            // affiliate ID
sid=0                // sub affiliate account ID
builddate=351        // kernel-mode driver build date
rnd=920026266        // random number
knt=1298317270       // time of the last connection with the command server

// list of the modules to inject into processes
*=new_cmd.dll        // module to inject into 32-bit processes
* (x64)=cmd64.dll    // module to inject into 64-bit processes

// setcion specific to cmd.dll
version=0.167        // version of the payload
bsh=75adb55bf6a0db37c8726416b55df6dfc03e7d8a    // bot id

// setcion specific to cmd64.dll

1.1 Cmd.dll

According to cfg.ini, cmd.dll is injected into each 32-bit process in the system in which the kernel32.dll library is loaded. However, it is actually only able to operate inside processes where the executable names contain the following substrings:

svchost.exe started with netsvcs parameter

cmd.dll might perform a number of tasks:

  • requesting and dispatching commands from C&C servers;
  • dispatching tasks received from C&C;
  • clicking;
  • Blackhat SEO (as described in the sectin “TDL4 and Glupteba” in the previous article);
  • Injecting HTML code into an HTML document.

1.2 Network communication

All the communication between the bot and C&C is carried over the HTTP/HTTPS protocols. There are several types of C&C servers with which the bot can communicate:

Types of C&C servers Description
command servers (“srv” key in cfg.ini) intended to send commands to bots
pservers ( “psrv” key in cfg.ini) intended to send URLs that should be opened in browser
click servers (“csrv” key in cfg.ini); intended to send URLs with which the clicker should work
wservers (“wsrv” key in cfg.ini) intended to substitute result of search providers
kservers (“ksrv” key in cfg.ini used to inject malicious “iframes” into HTML documents.


The data transmitted to and from C&C over HTTP/HTTPS are encrypted with the RC4 cipher, where the C&C server host name is used as the key, and are then encoded with BASE64 encoding (as shown in figure 1). In addition to the encryption, in some cases the data are mangled after encoding. Strings generated according to certain rules (described in the appendix) are prepended and appended to the data. This last measure is intended to avoid detection by IDS (Intrusion Detection Systems).

Figure 1 – The Format of Requests to the C&C Server

1.2.1 Communication with command servers

The bot periodically requests commands from C&C servers. The configuration file contains parameters that determine how frequently the bot should connect to these servers:

Parameters Description
knt Stores the time when the command servers were last accessed (in seconds since the year 1970)
delay time interval expressed in seconds between requests to the list of command servers
retry time interval in seconds between requests to command server within the list

The request to a command server prior to encryption and encoding looks like this:

Parameters Description
bid bot identifier (assigned by C&C or “noname” by default)
aid affiliate identifier
sid affiliate sub account identifier
tdl_ver version of the bootkit (0.03)
bot_ver version of cmd.dll/cmd64.dll (0.169)
os_ver version of operating system (5.1 2600 SP3.0)
locale current locale of the system
browser default browser of a user
tdl_build build date of the bootkit
tdl_install install date of the bootkit
rnd random number

The command server replies with a list of commands separated by semicolons. Each command is formatted as follows

command_name.method_name(Param1, Param2, …),

where command_name can be either cmd or name of an executable in the hidden file system of the bootkit. method_name can take the following values:

Command Description
DownloadCrypted download encrypted binary, decrypt it (RC4 cipher with bot_id as a key),and if its name has “.dll” extension then load it into address space of the current process
DownloadCrypted2 download encrypted binary, decrypt it (RC4 cipher with custom key), and if its name has “.dll” extension then load it into address space of the current process
DownloadAndExecute download executable and run it in a new process
DownloadCryptedAndExecute download encrypted executable, decrypt it (RC4 cipher with bot_id as a key) and run it in a new process
DownloadCryptedAndExecute2 download encrypted executable, decrypt it (RC4 cipher with custom key) and run it in a new process
Download download executable and load it into address space of the current process
ConfigWrite write a string in cfg.ini
SetName assign name to the bot
Name of exported function Name of exported function from command_name executable to call

The method parameters can be of the following types:

  • String (Unicode, ASCII);
  • Integers;
  • Floats.

Here is an example of a set of commands received from the C&C:

C&C commands Example of parameters
cmd.ConfigWrite (‘cmd’,’delay’,’7200′)
cmd.ConfigWrite (‘cmd’,’srv’,’;;;;’)
cmd.ConfigWrite (‘cmd’,’wsrv’,’;;;;’)
cmd.ConfigWrite (‘cmd’,’psrv’,’’)
cmd.ConfigWrite (‘cmd’,’csrv’,’’)
cmd.DownloadCrypted (‘’,’cmd.dll’)
cmd.DownloadCrypted (‘’,’bckfg.tmp’)
cmd.DownloadAndExecute (‘’)

1.2.2 Tasks

Once every 10 minutes the bot scans the “[tasks]” section of the configuration file to retrieve tasks for execution. The tasks are encoded as follows


Tasks Description
file_name name of the file in the hidden file system or random number
task_code 1 download binary from URL determined by para2, and decrypt with para1 key (if specified)
2 download binary from URL determined by para2, and decrypt with para1 key (if specified), then run as standalone application
3 delete file with file_name name
retry_count maximum number of attempts to execute the task. Each attempt this value is decremented and when reaches zero the task is deleted
para1, para2 parameters of the task

1.2.3 The Clicker

The module cmd.dll implements clicker functionality. It requests links from the servers listed under csrv key in cfg.ini file by using the URLs formatted as:


where bot_id, aff_id, sub_id, install_date have the same meaning as the corresponding values in communication with command server. The request is encoded and mangled. As a reply cmd.dll receives list of the values:


Parameters Description
x_url target URL
x_ref Referrer
dword_1,dword_2 unsigned integers specifying delay between receiving data from click servers and going to target URL

The clicker’s engine is implemented by means of the “WebBrowser” ActiveX control. For this purpose cmd.dll creates a window class with the name “svchost”. For each URL received from the click-servers the bot creates a window of class “svchost” with the name “svchost-XX”, where XX corresponds to the current thread ID passing target URL as lpParam to the CreateWindowEx function.

Figure 2 – Creating a New WIndow for Clicker

When the WindowProc function of the registered window class receives a WM_CREATE message, it creates the “WebBrowser” ActiveX control in the window and sets up properties: Silent – False, Visible – True. Then it navigates to the target URL by calling the Navigate method defined in the IWebBrowser2 interface with the following flags:

  • navNoHistory;
  • navNoReadFromCache;
  • navUntrustedForDownload;
  • navBrowserBar;
  • navHyperlink;
  • navEnforceRestricted.

Then the clicker waits for the NavigateCoplete2 event, signifying that at least part of the document has been received from the server, and that the document viewer has been created. At this point the clicker compares the current URL with the one requested and if they match (i.e. the request has not been redirected) it emulates surfing the web:

  • It scans the downloaded HTML document for elements with the tags “object” or “iframe” and links pointing to objects inside the same security domain as the requested document;
  • It emulates a user gradually moving mouse pointer to the element of the document and pressing the left mouse button.

1.2.4 Hooking mswsock.dll

In order to intercept and modify the data exchanged over the network the bot hooks several functions from the Microsoft Windows Socket Provider mswsock.dll:

  • WSPSend;
  • WSPRecv;
  • WSPCloseSocket.


By hooking the WSPSend routine the bot is able to intercept all the outgoing network traffic generated by the process into which cmd.dll is injected. Prior to forwarding the intercepted data to the destination host, the bot looks for the “windowsupdate” string in the data buffer. If it finds the string, then it immediately returns the error WSAENETRESET (normally signifying that the connection has been broken due to the remote host resetting), thereby disabling the Windows Update service.

Otherwise it calls the original WSPSend routine and if the operation has been completed successfully, it parses the outgoing data buffer to determine whether this is an HTTP request. If so it gets the following parameters from the header:

  • requested resource;
  • host;
  • accept-language;
  • referrer;
  • cookie;
  • user-agent.

Depending on the values these parameters may take, and information stored in additional files in the hidden files system, the bot performs the following actions:

  • injects additional functionality into HTML document through “iframe” tag;
  • fetches keywords from requests to search providers and stores them in “keywords” file;
  • substitutes results from search providers.

All these operations are performed in the WSPSend hook and stored in the binary tree data structure to be used in the WSPRecv hook.


In the WSPRecv hook the bot actually replaces the data obtained from the destination with information it generates in WSPSend hook.


In the WSPCloseSocket hook the bot releases all the resources allocated to handling and interception of data for a specific connection.

1.3 Cmd64.dll

Cmd64.dll is the payload to be injected into 64-bit processes only. It is a limited version of cmd.dll and its functionality is restricted to communications with command servers and executing tasks (without hooking mswsock.dll and clicker). These communications functions, however, are fully equivalent to the corresponding functions in cmd.dll.

1.4 TDL4 Tracker

During our investigation of the malware, a TDL4 tracking system has been implemented which monitors and logs all the communication between the bot and C&C servers. The system is able to intercept and decrypt all kinds of messages, even those transmitted over HTTPS, which allows us to gain access to all commands, updates and additional downloaded modules. The output of the system is presented as an appendix to the third article in this series: “TDSS: Bootkit on the Other Foot.” However, here is an example of the kind of detail it shows.

21/02/2011 21:36:16 SEND: PID=820,MODULE=C:WINDOWSSystem32svchost.exe,P1=,P2=Accept-Language: en-us,P3=00C7FE14,P4=00C7FE10,P5=(null),P6=(null)

21/02/2011 21:36:16 RECV: PID=820,MODULE=C:WINDOWSSystem32svchost.exe,FILEBUFFER=C:tdl421_02_2011_21_36_016_buf.txt,FILEDLL=NO

21/02/2011 21:41:41 SEND: PID=820,MODULE=C:WINDOWSSystem32svchost.exe,P1=|noname|40379|0|0.03|0.15|5.1 2600 SP3.0|en-us|iexplore|0|0|57989841,P2=(null),P3=00C3FEB4,P4=00C3FEA8,P5=(null),P6=noname

21/02/2011 21:41:44 RECV: PID=820,MODULE=C:WINDOWSSystem32svchost.exe,FILEBUFFER=NO,FILEDLL=NO

2 Kernel-mode components

In this section we describe the kernel-mode components of the bootkit, namely, drv32.sys and drv64.sys, which correspond to the x86 and x64 operating systems. The kernel-mode drivers constitute the most important part of the bootkit and execute the following tasks:

  • performing self-defense;
  • maintaining the hidden file system in which the bootkit’s components are stored;
  • injecting the payload into processes in the system.

In general the x86 and x64 binaries of the TDL4 are quite similar, and indeed are compiled from a single set of source files. Unlike the TDL3/TDL3+ kernel-mode component, which is stored in the hidden file system as a piece of code (independent of the base address), TDL4’s kernel-mode components are valid PE images.

2.1 Self-defense

2.1.1 Kernel-mode hooks

The bootkit conceals its presence in the system by setting up hooks to the storage miniport driver, in the same way as its predecessor TDL3/TDL3+. The hooks enable the bootkit to intercept read/write requests to the hard drive and thereby counterfeit data being read or written.

Figure 3 represents the relationship between the miniport device object and its corresponding driver object after the bootkit sets up the hooks which modify the StartIo field of the target device’s driver object and the DriverObject field of the target device object. The bootkit also excludes the target device from the driver object’s linked list.

After these manipulations, all the requests addressed to the miniport device object are dispatched by the corresponding handlers of the bootkit’s driver object. The bootkit controls the following areas of the hard drive:

  • The boot sector. When an application reads the boot sector, the bootkit counterfeits data and returns the original contents of the sector (i.e. as it was prior to infection), and it also protects the sector from overwriting;
  • The hidden file system. On any attempt to read sectors of the hard disk where the hidden file system is located, the bootkit returns a zeroed buffer as well as protecting the area from overwriting.

Figure 3 ­– The Bootkit’s Kernel-mode Hooks

The bootkit contains code that performs additional checks to prevent the malware from being detected, deactivated or removed. When the bootkit’s driver is loaded and properly initialized it queues WORK_QUEU_ITEM which performs the following tasks at one-second intervals:

  • Reads the contents of the boot sector, compares it to the infected image and if there is a difference between them writes an infected MBR into the boot sector (in case something managed to overwrite it);
  • Sets the DriverObject field of the miniport device object to point to the bootkit’s driver object;
  • Hooks the DriverStartIo field of the miniport’s driver object;
  • Checks the integrity (the first 16 bytes) of the IRP_MJ_INTERNAL_DEVICE_CONTROL handler of the miniport’s driver object.

2.1.2 Cleaning up traces

The bootkit also takes care of cleaning up the traces it left during the loading of the bootkit at boot time (see the final article in this series, “TDSS: Bootkit on the Other Foot). Specifically:

  • It restores the original kdcom.dll library in kernel-mode address space. The bootkit loads the library and correspondingly fixes dependencies (imported symbols from the library) of ntoskrnl.exe and hal.dll;
  • It modifies the registry value SystemStartupOptions of the HKLMSystemCurentControlSetControl registry key to remove the /MININT (IN/MINT) option, which was distorted at boot time, from the list of boot options which was used to load the kernel.

2.2 Maintaining the hidden file system

In order to covertly store its malicious components, the bootkit implements a hidden file system. The general structure of the file system remains the same as in the case of TDL3/TDL3+: the bootkit reserves some space at the end of the hard drive, regardless whether this space is being used by operating system.

The bootkit’s file system is maintained by a set of device objects. Here we can see a volume device object representing a logical volume (partition) hosting TDL4’s files, and a so called physical device object responsible for handling IO requests from the bootkit’s payload. These two device objects are connected with each other by means of a volume parameter block – a special system structure linking a volume device object with the corresponding physical device object. This enhancement appeared for the first time when the TDL3+ version of the rootkit was released.

Figure 4 – TDL4 File System Device Relationship

As we can see from the figure above, the volume device object is created as a device object belonging to the DriverPnpManager driver object, so that all the requests are handled by this driver. In order to conceal the volume, the bootkit removes the device object from PnpManager’s device object linked list.

The hidden file system is configured so that TDL4’s components access files stored on it using the following paths:

?globalrootdeviceXXXXXXXXYYYYYYYYfile_name – for user-mode components


deviceXXXXXXXXYYYYYYYYfile_name – for kernel-mode components.

Here we can see that TDL4 appends 8 random hexadecimal digits to the volume device object, and these are generated on loading of the bootkit. If this condition is not met, a STATUS_OBJECT_NAME_INVALID error code is returned.

2.2.1 TDL4 file system layout

TDL4 uses the same technique for allocating space on a hard drive for its file system as its predecessor; namely, it starts at the last but one sector of the hard drive and grows towards start of the disk space.

Figure 5 ­– Location of the Hidden File System on Disk

There are some changes in the layout of the file system compared to the TDL3 file system layout. Each block of the file system has the following format:

typedef struct _TDL4_FS_BLOCK
// Signature of the block
// DC - root directory
// FC - block with file data
// NC - free bock
WORD Signature;
// Size of data in block
WORD SizeofDataInBlock;
// Offset of the next block relative to file system start
WORD NextBlockOffset;
// File table or file data
BYTE Data[506];

Here is the format of the root directory:

typedef struct _TDL4_FS_ROOT_DIRECTORY
// Signature of the block
// DC - root directory
WORD Signature;
// Set to zero
DWORD Reserved;
// Array of entries corresponding to files in FS
TDL4_FS_FILE_ENTRY FileTable[15];

typedef struct _TDL4_FS_FILE_ENTRY
// File name - null terminated string
char FileName[16];
// Offset from beginning of the file system to file
DWORD FileBlockOffset;
// Reserved
DWORD dwFileSize;
// Time and Date of file creation
FILETIME CreateTime;

2.2.2       Encrypted File System

The bootkit protects the contents of its file system by encrypting its blocks. As with TDL3 it uses the RC4 encryption algorithm, which is a stream cipher with varying key length. Unlike TDL3, where the “tdl” string is used as a key, TDL4 uses the 32-bit integer LBA of the sector block being encrypted. (You may recall that TDL3+ encrypts its file system by XORing contents with a single byte incremented with each XOR operation).

2.3        Injecting payload into processes

The way TDL4 injects its payload into processes in the system hasn’t changed significantly since the previous version of the rootkit, but as it wasn’t described in our report on TDL3 (, we are going to address it here.

To track creation of a new process in the system, TDL4 registers the LoadImageNotificationRoutine and waits until the “kernel32.dll” system library is mapped into memory. Once that happens, the bootkit obtains the addresses of exported symbols LoadLibraryEx, GetProcAddress, VirtualFree and queues a special kernel-mode APC ,which in turn queues a work item performing injection of the payload. The work item executing in the context of the “System” process attaches itself to the target process by calling the KeStackAttachProcess system routine. When the address space of the process is switched to that of the target process, the bootkit maps payload to it and applies relocations. The next step is to allocate a buffer in the user-mode address space of the process and fill it with the path to the payload, and code initializing the import address table and calling the payload’s entry point. When this is done the bootkit queues the user-mode APC executing user-mode code.

To be precise the user-mode code initializes the import address table of the executable and calls its entry point, passing as parameters the following values:

  • Base address of the payload;
  • DWORD set to 0x0000001 (DLL_PROCESS_ATTACH);
  • Path to the payload in the hidden file system, i.e. ASCII string ?globalrootdeviceXXXXXXXXYYYYYYYYpaylod.dll.

If the entry point returns zero then the code frees memory allocated for the payload image and overwrites the path to the payload in the user-mode buffer with zeros.

The following figure illustrates the overall process.

Figure 6 – Process of Injecting Payload into Processes in the System

2.4 Comparison with TDL3/TDL3+

Compared to its predecessors (TDL3 and TDL3+) there are some significant changes in the kernel-mode components of the bootkit which affect the following aspects of its work: kernel-mode code layout, surviving a reboot, self-defense against removal from the system, and supported platforms. These points are summarized in the table below.

Comparison of TDL kernel-mode components

Kernel-mode code representation Base independent piece of code in hidden file system PE image in the hidden file system
Surviving after reboot Infecting disk miniport/random kernel-mode driver Infecting MBR of the disk
Self-defense Kernel-mode hooks, registry monitoring Kernel-mode hooks, MBR monitoring
Injecting payload into processes in the system tdlcmd.dll cmd.dll/cmd64.dll
x64 support + (drv64)


In the final article of the series, we will discuss the normal boot process and how the bootkit exploits it.

TDSS part 3: Bootkit on the Other Foot

Appendix: Mangling algorithm in python

from random import randint

# mangle rules
mangle_rules = [
(1, "*"),
(1, "AaKhQqYy"),
(1, "*"),
(1, "123"),
(1, "*"),
(3, "eElLdCUExX"),
(1, "01"),
(1, "*"),
(1, "34567"),
(1, "mFyYjJqQXx"),
(1, "*"),
(2, "GgOoSsUu"),
(1, "789"),
(1, "@"),
(1, "5678"),
(1, "1234"),
(1, "AchIwWqQ")

def mangle_request(original_request):
# mangled result
mangled_request = ""

# run through the list of rules
for rule in mangle_rules:
rule[1] == "@": # copy original request mangled_request += original_request
else:       # add a number of random characters to the request according to the rule
i in xrange(rule[0]):
rule[1] == "*":
char_set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"
char_set = rule[1]
# select random character
mangled_request += char_set[randint(0, len(char_set) - 1)]

return mangled_request

Posted: April 19, 2011
View Profile

Aleksandr Matrosov is a Senior Malware Researcher at ESET. He is also a Lecturer at the Cryptology and Discrete Mathematics at National Nuclear Research University MEPh. He specializes in the analysis of malicious threats and cybercrime activity. Eugene Rodionov is a malware researcher for ESET. Rodionov also holds the position of Lecturer at the National Nuclear Research University MEPhI in Russia. His interests include kernel-mode programming, anti-rootkit technologies, reverse engineering and cryptology. David Harley is a Senior Research Fellow at ESET. He is a Director of the Anti-Malware Testing Standards Organization, Chief Operations Officer at AVIEN, and CEO of Small Blue-Green World. He is a Fellow of the BCS Institute and holds qualifications in security management, service management (ITIL), BSI security audit and medical informatics.