Controlling the Flow of Data:
To fulfill their mission of stealth, kernel rootkits must modify the control flow or the data flow (or both) of the kernel’s system calls, wherever the OS’s original control or data flow would reveal the presence of any of the malware’s components at rest (for example, files) or any of its running tasks or artifacts (such as kernel data structures). To do so, rootkits typically inject their code somewhere on the execution path of the system call implementation; the placement of these code hooks is one of the most instructive aspects of rootkits.
Bring Your Own Linker:
Hooking is essentially linking. Modern rootkits bring their own linkers to link their code with the system, a design pattern we call Bring Your Own Linker. In order to embed these “linkers” stealthily, the TDL3 follows a few common malware design principles.
First, the target must remain robust despite the injected extra code, as the attacker has nothing to gain and a lot to lose from crashing the targeted software. From a software engineering point of view, hooking is a form of software composition and requires a careful approach. The attacker www.EBooksWorld.ir 8 Chapter 1 must make sure that the system reaches the new code only in a predictable state so the code can correctly process, to avoid any crashing or abnormal behavior that would draw a user’s attention. It might seem like the placement of hooks is limited only by the rootkit author’s imagination, but in reality, the author must stick to stable software boundaries and interfaces they understand really well. It is not surprising, then, that hooking tends to target the same structures that are used for the system’s native dynamic linking functionality, whether publicly documented or not. Tables of callbacks, methods, and other function pointers that link abstraction layers or software modules are the safest places for hooks; hooking function preambles also work well.
Secondly, the hook placement should not be too obvious. Although early rootkits hooked the kernel’s top-level system call table, this technique quickly became obsolete because it was so conspicuous. In fact, when used by the Sony rootkit in 2005,3 this placement was already considered behind the times and raised many eyebrows as a result. As rootkits grew more sophisticated, their hooks migrated lower down the stack, from the main system call dispatch tables to the OS subsystems that presented uniform API layers for diverging implementations, such as the Virtual File System (VFS), and then down to specific drivers’ methods and callbacks. TDL3 is a particularly good example of this migration.
Also Read:AutoLOG Keylogger download 2023
How TDL3’s Kernel-Mode Hooks Work:
In order to stay under the radar, TDL3 employed a rather sophisticated hooking technique never before seen in the wild: it intercepted the read and write I/O requests sent to the hard drive at the level of the storage port/miniport driver (a hardware storage media driver found at the very bottom of the storage driver stack). Port drivers are system modules that provide a programming interface for miniport drivers, which are supplied by the vendors of the corresponding storage devices. Figure 1-2 shows the architecture of the storage device driver stack in Microsoft Windows.
The processing of an I/O request packet (IRP) structure addressed to some object located on a storage device starts at the filesystem driver’s level. The corresponding filesystem driver determines the specific device where the object is stored (like the disk partition and the disk extent, a contiguous storage area initially reserved for a filesystem) and issues another IRP to a class driver’s device object. The latter, in turn, translates the I/O request into a corresponding miniport device object.
According to the Windows Driver Kit (WDK) documentation, storage port drivers provide an interface between a hardware-independent class driver and an HBA-specific (host-based architecture) miniport driver. Once that interface is available, TDL3 sets up kernel-mode hooks at the lowest possible hardware-independent level in the storage device driver stack, thus bypassing any monitoring tools or protections operating at the level of the filesystem or storage class driver. Such hooks can be detected only by tools that are aware of the normal composition of these tables for a particular set of devices or of a known good configuration of a particular machine.
In order to achieve this hooking technique, TDL3 first obtains a pointer to the miniport driver object of the corresponding device object. Specifically, the hooking code tries to open a handle for \??\PhysicalDriveXX (where XX corresponds to the number of the hard drive), but that string is actually a symbolic link pointing to the device object \Device\HardDisk0\DR0, which is created by a storage class driver. Moving down the device stack from \Device\ HardDisk0\DR0, we find the miniport storage device object at the very bottom. Once the miniport storage device object is found, it’s straightforward to get a pointer to its driver object by following the DriverObject field in the documented DEVICE_OBJECT structure. At this point, the malware has all the information it needs to hook the storage driver stack.
Next, TDL3 creates a new malicious driver object and overwrites the DriverObject field in the miniport driver object with the pointer to a newly created field, as shown in Figure 1-3. This allows the malware to intercept read/write requests to the underlying hard drive, since the addresses of all the handlers are specified in the related driver object structure: the MajorFunction array in the DRIVER_OBJECT structure.
The malicious major handlers shown in Figure 1-3 intercept IRP_MJ _INTERNAL_CONTROL and IRP_MJ_DEVICE_CONTROL for the following Input/Output Control (IOCTL) code in order to monitor and modify read/write requests to the hard drive, storing the infected driver and the image of the hidden filesystem implemented by the malware:
• IOCTL_ATA_PASS_THROUGH TDL3
prevents hard drive sectors containing protected data from being read by the Windows tools or accidentally overwritten by the Windows filesystem, thus protecting both the stealth and the integrity of the rootkit. When a read operation is encountered, TDL3 zeros out the return buffer on completion of the I/O operation, and it skips the whole read operation in the event of a write data request. TDL3’s hooking technique allows it to bypass some kernel patch detection techniques; that is, TDL3’s modifications do not touch any of the frequently protected and monitored areas, including system modules, the System Service Descriptor Table (SSDT), the Global Descriptor Table (GDT), or the Interrupt Descriptor Table (IDT). Its successor, TDL4, takes the same approach to bypassing kernel-mode patch protection PatchGuard available on 64-bit Windows operating systems, as it inherits a great deal of kernel-mode functionality from TDL3, including these hooks into the storage miniport driver.
The Hidden Filesystem:
TDL3 was the first malware system to store its configuration files and payload in a hidden encrypted storage area on the target system, instead of relying on the filesystem service provided by the operating system. Today, TDL3’s approach has been adopted and adapted by other complex threats such as the Rovnix Bootkit, ZeroAccess, Avatar, and Gapz.
This hidden storage technique significantly hampers forensic analysis because the malicious data is stored in an encrypted container located www.EBooksWorld.ir What’s in a Rootkit: The TDL3 Case Study 11 somewhere on the hard drive, but outside the area reserved by the OS’s own native filesystem. At the same time, the malware is able to access the contents of the hidden filesystem using conventional Win32 APIs like CreateFile, ReadFile, WriteFile, and CloseHandle. This facilitates malware payload development by allowing the malware developers to use the standard Windows interfaces for reading and writing the payloads from the storage area without having to develop and maintain any custom interfaces. This design decision is significant because, together with the use of standard interfaces for hooking, it improves the overall reliability of the rootkit; from a software engineering point of view, this is a good and proper example of code reuse! Microsoft’s own CEO’s formula for success was “Developers, developers, developers, developers!”—in other words, treating existing developer skills as valuable capital. TDL3 chose to similarly leverage the existing Windows programming skills of developers who had turned to the dark side, perhaps both to ease the transition and to increase the reliability of the malcode.
TDL3 allocates its image of the hidden filesystem on the hard disk, in sectors unoccupied by the OS’s own filesystem. The image grows from the end of the disk toward the start of the disk, which means that it may eventually overwrite the user’s filesystem data if it grows large enough. The image is divided into blocks of 1,024 bytes each. The first block (at the end of the hard drive) contains a file table whose entries describe files contained within the filesystem and include the following information:
• A filename limited to 16 characters, including the terminating null
• The size of the file
• The actual file offset, which we calculate by subtracting the starting offset of a file, multiplied by 1,024, from the offset of the beginning of the filesystem
• The time the filesystem was created The contents of the filesystem are encrypted with a custom (and mostly ad hoc) encryption algorithm on a per-block basis. Different versions of the rootkit have used different algorithms. For instance, some modifications used an RC4 cipher using the logical block address (LBA) of the first sector that corresponds to each block as a key. However, another modification encrypted data using an XOR operation with a fixed key: 0x54 incremented each XOR operation, resulting in weak enough encryption that a specific pattern corresponding to an encrypted block containing zeros was easy to spot.
From user mode, the payload accesses the hidden storage by opening a handle for a device object named \Device\XXXXXXXX\YYYYYYYY where XXXXXXXX and YYYYYYYY are randomly generated hexadecimal numbers. Note that the codepath to access this storage relies on many standard Windows components—hopefully already debugged by Microsoft and therefore reliable. The name of the device object is generated each time the system boots and then passed as a parameter to the payload modules. The rootkit is responsible for maintaining and handling I/O requests to this www.EBooksWorld.ir 12 Chapter 1 filesystem. For instance, when a payload module performs an I/O operation with a file stored in the hidden storage area, the OS transfers this request to the rootkit and executes its entry point functions to handle the request. In this design pattern, TDL3 illustrates the general trend followed by rootkits. Rather than providing brand-new code for all of its operations, burdening the third-party malware developers with learning the peculiarities of that code, a rootkit piggybacks on the existing and familiar Windows functionality—so long as its piggybacking tricks and their underlying Windows interfaces are not common knowledge. Specific infection methods evolve with changes in mass-deployed defensive measures, but this approach has persisted, as it follows the common code reliability principles shared by both malware and benign software development.
Conclusion: TDL3 Meets Its Nemesis:
As we have seen, TDL3 is a sophisticated rootkit that pioneered several techniques for operating covertly and persistently on an infected system. Its kernel-mode hooks and hidden storage systems have not gone unnoticed by other malware developers and thus have subsequently appeared in other complex threats. The only limitation to its infection routine is that it’s able to target only 32-bit systems.
When TDL3 first began to spread, it did the job the developers intended, but as the number of 64-bit systems increased, demand grew for the ability to infect x64 systems. To achieve this, malware developers had to figure out how to defeat the 64-bit Kernel-Mode Code Signing Policy in order to load malicious code into kernel-mode address space. As we’ll discuss in Chapter 7, TDL3’s authors chose bootkit technology to evade signature enforcement.