This chapter examines one of the stealthiest bootkits ever seen in the wild: the Win32/Gapz bootkit. We’ll cover its technical characteristics and functionality, beginning with the dropper and bootkit components and moving on to the user-mode payload.
In our experience, Gapz is the most complex bootkit ever analyzed. Every feature of its design and implementation—its elaborate dropper, advanced bootkit infection, and extended rootkit functionality—ensures that Gapz is able to infect and persist on victims’ computers and stay under the radar for a long time.
Gapz is installed onto the victim’s system by a dropper that exploits multiple local privilege escalation vulnerabilities and implements an unusual technique for bypassing Host Intrusion Prevention Systems (HIPS).
After successfully penetrating the victim’s system, the dropper installs the bootkit, which has a very small footprint and is hard to spot on the infected system. The bootkit loads malicious code that implements the Gapz rootkit functionality into kernel mode.
The rootkit functionality is very rich, comprising a custom TCP/IP network stack, advanced hooking engine, crypto library, and payload injection engine.
This chapter takes a deep dive into each of these powerful features.
Gapz is installed onto the target system by an elaborate dropper. There are several variations of the Gapz dropper, all containing a similar payload, which we’ll cover later in “Gapz Rootkit Functionality” on page 191. The difference between the droppers lies in the bootkit technique and the number of local privilege escalation (LPE) vulnerabilities they each exploit.
The first instance of Gapz discovered in the wild was Win32/Gapz.C, in April 2012.1 This variation of the dropper employed an MBR-based bootkit—the same technique covered in Chapter 7 for the TDL4 bootkit—to persist on a victim’s computer. What made Win32/Gapz.C remarkable was that it contained a lot of verbose strings for debugging and testing and that its early distribution was very limited. This suggests that the first versions of Gapz weren’t intended for mass distribution but rather were test versions to debug the malware’s functionality.
The second variation, Win32/Gapz.B, didn’t install a bootkit on the targeted system at all. To persist on the victim’s system, Gapz simply installed a malicious kernel-mode driver. However, this approach wouldn’t work on Microsoft Windows 64-bit platforms due to the lack of a valid digital signature for the kernel-mode driver, limiting this modification to Microsoft Windows 32-bit operating systems only.
The last known and the most interesting iteration of the dropper, Win32/Gapz.A, is the version we’ll focus on in this chapter. This version came with a VBR bootkit. In the rest of the chapter, we will simply use “Gapz” to refer to Win32/Gapz.A.
Table 12-1 summarizes the different versions of the dropper.
Table 12-1: Versions of the Win32/Gapz Dropper
Detection name |
Compilation date |
LPE exploits |
Bootkit technique |
Win32/Gapz.A |
09/11/2012 10/30/2012 |
CVE-2011-3402 CVE-2010-4398 COM Elevation |
VBR |
Win32/Gapz.B |
11/06/2012 |
CVE-2011-3402 COM Elevation |
No bootkit |
Win32/Gapz.C |
04/19/2012 |
CVE-2010-4398 CVE-2011-2005 COM Elevation |
MBR |
The detection name column lists the Gapz variation adopted by the antivirus industry. The entries in the compilation date column are taken from the Gapz droppers’ PE header, which is believed to be an accurate timestamp. The Bootkit technique column shows what kind of bootkit the dropper employs.
Finally, the LPE exploits column lists a number of LPE vulnerabilities exploited by Gapz droppers in order to get administrator privileges on the victim systems. The COM elevation vulnerability is used to bypass the User Account Control (UAC) security feature in order to inject code into a system process that is whitelisted for UAC. The CVE-2011-3402 vulnerability relates to the TrueType font–parsing functionality implemented in the win32k.sys module. The CVE-2010-4398 vulnerability is due to a stack-based buffer overflow in the RtlQueryRegistryValues routine, also located in the win32k.sys module. The CVE-2011-2005 vulnerability, located in the afd.sys (ancillary function driver) module, allows attackers to overwrite data in kernel-mode address space.
All of the variations of the Gapz dropper listed in Table 12-1 contain the same payload.
Before examining the Gapz dropper more closely, let’s recap what it needs in order to silently and successfully install Gapz onto the system.
First, the dropper requires administrative privileges to access the hard drive and modify MBR/VBR/IPL data. If the dropper’s user account lacks administrator privileges, it must raise its privileges by exploiting LPE vulnerabilities in the system.
Second, it needs to bypass security software, such as antivirus programs, personal firewalls, and Host Intrusion Prevention Systems. To stay under the radar, Gapz uses advanced tools and methods, including obfuscation, antidebugging, and antiemulation techniques. In addition to these methods, the Gapz dropper employs a unique and rather interesting technique to bypass HIPS, as discussed later in the chapter.
Taking these obstacles into account, these are the steps the Gapz dropper performs to successfully infect a system:
When the unpacked dropper is loaded into the IDA Pro disassembler, its export address table will look something like Figure 12-1. The export address table shows all the symbols exported from the binary and nicely sums up the steps in the dropper execution algorithm.
Figure 12-1: Export address table of the Gapz dropper
There are three routines exported by the binary: one main entry point and two routines with randomly generated names. Each routine has its own purpose:
start Injects the dropper into the explorer.exe address space
icmnf Exploits LPE vulnerabilities in the system to elevate privileges
isyspf Infects the victim’s machine
Figure 12-1 also shows the exported symbol gpi. This symbol points to a shared memory in the dropper image, used by the preceding routines to inject the dropper into the explorer.exe process.
Figure 12-2 depicts these stages. The main entry point doesn’t infect the system with the Gapz bookit. Instead it executes the start routine to inject the dropper into explorer.exe in order to bypass detection by security software. Once the dropper is injected, it attempts to acquire administrator privileges by exploiting LPE vulnerabilities in the system with the icmnf routine. Once the dropper gains the required privileges, it executes the isyspf routine to infect the hard drive with the bootkit.
Figure 12-2: Gapz dropper workflow
Let’s take a closer look at the process of injecting the dropper and bypassing HIPS.
Computer viruses have many methods of camouflaging themselves as benign software to avoid attracting the attention of security software. The TDL3 rootkit we discussed in Chapter 1 employs another interesting technique for bypassing HIPS, which abused AddPrintProvidor/AddPrintProvider system APIs to stay under the radar. These API functions are used to load custom modules into a trusted system process, spoolsvc.exe, that is responsible for printing support on Windows systems. The AddPrintProvidor (sic) routine, an executable module used to install a local print provider onto the system, is frequently excluded from the list of items monitored by security software. TDL3 simply creates an executable file with malicious code and loads it into spoolsvc.exe by running AddPrintProvidor. Once the routine is executed, the malicious code runs within the trusted system process, allowing TDL3 to attack without worrying about being detected.
Gapz also injects its code into a trusted system process in order to bypass HIPS, but it uses an elaborate nonstandard method, the core aim of which is to inject shellcode that loads and executes the malicious image into the explorer process. These are the steps the dropper takes:
The section objects are used to share part of a certain process’s memory with other processes; in other words, they represent a section of memory that can be shared across the system processes. Listing 12-1 shows the section objects in BaseNamedObjects for which the malware looks in step 1. These section objects correspond to system sections—that is, they are created by the operating system and contain system data. Gapz iterates through the list of section objects and opens them to check whether they exist in the system. If a section object exists in the system, the dropper stops iterating and returns a handle for the corresponding section.
char _stdcall OpenSection_(HANDLE *hSection, int pBase, int *pRegSize)
{
sect_name = L"\BaseNamedObjects\ShimSharedMemory";
v7 = L"\BaseNamedObjects\windows_shell_global_counters";
v8 = L"\BaseNamedObjects\MSCTF.Shared.SFM.MIH";
v9 = L"\BaseNamedObjects\MSCTF.Shared.SFM.AMF";
v10 = L"\BaseNamedObjectsUrlZonesSM_Administrator";
i = 0;
while ( OpenSection(hSection, (§_name)[i], pBase, pRegSize) < 0 )
{
if ( ++i >= 5 )
return 0;
}
if ( VirtualQuery(*pBase, &Buffer, 0xlCu) )
*pRegSize = v7;
return 1;
}
Listing 12-1: Object names used in the Gapz dropper
Once it opens the existing section, the malware proceeds with injecting its code into the explorer.exe process, as shown in Listing 12-2.
char __cdecl InjectIntoExplorer()
{
returnValue = 0;
if ( OpenSectionObject(&hSection, &SectionBase, &SectSize) ) // open some of SHIM sections
{
➊ TargetBuffer = (SectionBase + SectSize - 0x150); // find free space in the end
// of the section
memset(TargetBuffer, 0, 0x150u);
qmemcpy(TargetBuffer->code, sub_408468, sizeof(TargetBuffer->code));
hKernel32 = GetModuleHandleA("kernel32.dll");
➋ TargetBuffer->CloseHandle = GetExport(hKernel32, "CloseHandle", 0);
TargetBuffer->MapViewOfFile = GetExport(hKernel32, "MapViewOfFile", 0);
TargetBuffer->OpenFileMappingA = GetExport(hKernel32, "OpenFileMappingA", 0);
TargetBuffer->CreateThread = GetExport(hKernel32, "CreateThread", 0);
hUser32 = GetModuleHandleA("user32.dll");
TargetBuffer->SetWindowLongA = GetExport(hUser32, "SetWindowLongA", 0);
➌ TargetBuffer_ = ConstructTargetBuffer(TargetBuffer);
if ( TargetBuffer_ )
{
hWnd = FindWindowA("Shell_TrayWnd", 0);
➍ originalWinProc = GetWindowLongA(hWnd, 0);
if ( hWnd && originalWinProc )
{
TargetBuffer->MappingName[10] = 0;
TargetBuffer->Shell_TrayWnd = hWnd;
TargetBuffer->Shell_TrayWnd_Long_0 = originalWinProc;
TargetBuffer->icmnf = GetExport(CurrentImageAllocBase, "icmnf", 1);
qmemcpy(&TargetBuffer->field07, &MappingSize, 0xCu);
TargetBuffer->gpi = GetExport(CurrentImageAllocBase, "gpi", 1);
BotId = InitBid();
lstrcpynA(TargetBuffer->MappingName, BotId, 10);
if ( CopyToFileMappingAndReloc(TargetBuffer->MappingName, CurrentImageAllocBase,
CurrentImageSizeOfImage, &hObject) )
{
BotEvent = CreateBotEvent();
if ( BotEvent )
{
➎ SetWindowLongA(hWnd, 0, &TargetBuffer_->pKiUserApcDispatcher);
➏ SendNotifyMessageA(hWnd, 0xFu, 0, 0);
if ( !WaitForSingleObject(BotEvent, 0xBB80u) )
returnValue = 1;
CloseHandle(BotEvent);
}
CloseHandle(hObject);
}
}
}
NtUnmapViewOfSection(-1, SectionBase);
NtClose(hSection);
}
return returnValue;
}
Listing 12-2: Injecting the Gapz dropper into explorer.exe
The malware uses 336 (0x150) bytes ➊ of the space at the end of the section to write the shellcode. To ensure the shellcode executes correctly, the malware also provides the addresses of some API routines used during the injection process: CloseHandle, MapViewOfFile, OpenFileMappingA, CreateThread, and SetWindowLongA ➋. The shellcode will use these routines to load the Gapz dropper into the explorer.exe memory space.
Gapz executes the shellcode using the return-oriented programming (ROP) technique. ROP takes advantage of the fact that in x86 and x64 architectures, the ret instruction can be used to return control to the parent routine after execution of a child subroutine. The ret instruction assumes that the address to which control is returned is on the top of the stack, so it pops the return address from the stack and transfers control to that address. By executing a ret instruction to gain control of the stack, an attacker can execute arbitrary code.
The reason Gapz uses the ROP technique to execute its shellcode is that the memory corresponding to the shared section object may not be executable, so an attempt to execute instructions from there will generate an exception. To overcome this limitation, the malware uses a small ROP program that’s executed before the shellcode. The ROP program allocates some executable memory inside the target process, copies the shellcode into this buffer, and executes it from there.
Gapz finds the gadget for triggering the shellcode in the routine ConstructTargetBuffer ➌. In the case of 32-bit systems, Gapz uses the system routine ntdll!KiUserApcDispatcher to transfer control to the ROP program.
Once it has written the shellcode to the section object and found all the necessary ROP gadgets, the malware proceeds to the next step: modifying the Shell_TrayWnd window procedure. This procedure is responsible for handling all the events and messages that occur and are sent to the window. Whenever the window is resized or moved, a button is pressed, and so on, the Shell_TrayWnd routine is called by the system to notify and update the window. The system specifies the address of the window procedure at the time of the window’s creation.
The Gapz dropper retrieves the address of the original window procedure, in order to return to it after injection, by executing the GetWindowLongA ➍ routine. This routine is used to get window parameters and takes two arguments: the window handle and an index of the parameter to be retrieved. As you can see, Gapz calls the routine with the index parameter 0, indicating the address of the original Shell_TrayWnd window procedure. The malware stores this value in the memory buffer in order to restore the original address after injection.
Next, the malware executes the SetWindowLongA routine ➎ to modify the address of the Shell_TrayWnd window procedure to the address of the ntdll!KiUserApcDispatcher system routine. By redirecting to an address within the system module and not the shellcode itself, Gapz further protects itself against detection by security software. At this point, the shellcode is ready to be executed.
Gapz triggers the execution of the shellcode by using the SendNotifyMessageA API ➏ to send a message to the Shell_TrayWnd window, passing control to the window procedure. As explained in the previous section, after the address of the window procedure is modified, the new address points to the KiUserApcDispatcher routine. This eventually results in control being transferred to the shellcode mapped within the explorer.exe process address space, as shown in Listing 12-3.
int __stdcall ShellCode(int a1, STRUCT_86_INJECT *a2, int a3, int a4)
{
if ( !BYTE2(a2->injected) )
{
BYTE2(a2->injected) = 1;
➊ hFileMapping = (a2->call_OpenFileMapping)(38, 0, &a2->field4);
if ( hFileMapping )
{
➋ ImageBase = (a2->call_MapViewOfFile)(hFileMapping, 38, 0, 0, 0);
if ( ImageBase )
{
qmemcpy((ImageBase + a2->bytes_5), &a2->field0, 0xCu);
➌ (a2->call_CreateThread)(0, 0, ImageBase + a2->routineOffs, ImageBase, 0, 0);
}
(a2->call_CloseHandle)( hFileMapping );
}
}
➍ (a2->call_SetWindowLongA)(a2->hWnd, 0, a2->OriginalWindowProc);
return 0;
}
Listing 12-3: Mapping the Gapz dropper image into the address space of explorer.exe
You can see the usage of the API routines OpenFileMapping, MapViewOfFile, CreateThread, and CloseHandle, whose addresses were populated earlier (at ➋ in Listing 12-2). Using these routines, the shellcode maps the view of the file that corresponds to the dropper into the address space of explorer.exe (➊ and ➋). Then it creates a thread ➌ in the explorer.exe process to execute the mapped image and restores the original index value that was changed by the SetWindowLongA WinAPI function ➍. The newly created thread runs the next part of the dropper, escalating its privileges. Once the dropper obtains sufficient privileges, it attempts to infect the system, which is when the bootkit feature comes into play.
Gapz uses two distinct variations of infection technique: one targeting the MBR of the bootable hard drive and the other targeting the VBR of the active partition. The bootkit functionality of both versions, however, is pretty much the same. The MBR version aims to persist on a victim’s computer by modifying MBR code in a similar way to the TDL4 bootkit. The VBR version uses subtler and stealthier techniques to infect the victim’s system, and as mentioned, that’s the one we’ll focus on here.
We briefly touched on the Gapz bootkit technique in Chapter 7, and now we’ll elaborate on the implementation details. The infection method Gapz uses is one of the stealthiest ever seen in the wild, modifying only a few bytes of the VBR and making it very hard for security software to detect it.
The main target of the malware is the BIOS parameter block (BPB) data structure located in the VBR (see Chapter 5 for more details). This structure contains information about the filesystem volume located on the partition and has a crucial role in the boot process. The BPB layout differs across various filesystems (FAT, NTFS, and so on), but we will focus on NTFS. The contents of the BPB structure for NTFS are shown in Listing 12-4 (this is excerpted from Listing 5-3 for convenience).
typedef struct _BIOS_PARAMETER_BLOCK_NTFS {
WORD SectorSize;
BYTE SectorsPerCluster;
WORD ReservedSectors;
BYTE Reserved[5];
BYTE MediaId;
BYTE Reserved2[2];
WORD SectorsPerTrack;
WORD NumberOfHeads;
➊ DWORD HiddenSectors;
BYTE Reserved3[8];
QWORD NumberOfSectors;
QWORD MFTStartingCluster;
QWORD MFTMirrorStartingCluster;
BYTE ClusterPerFileRecord;
BYTE Reserved4[3];
BYTE ClusterPerIndexBuffer;
BYTE Reserved5[3];
QWORD NTFSSerial;
BYTE Reserved6[4];
} BIOS_PARAMETER_BLOCK_NTFS, *PBIOS_PARAMETER_BLOCK_NTFS;
Listing 12-4: Layout of the BIOS_PARAMETER_BLOCK for NTFS
As you may recall from Chapter 5, the HiddenSectors field ➊, located at offset 14 from the beginning of the structure, determines the location of the IPL on the hard drive (see Figure 12-3). The VBR code uses HiddenSectors to find the IPL on the disk and execute it.
Figure 12-3: Location of IPL on the hard drive
Gapz hijacks the control flow at system bootup by manipulating the HiddenSectors field value inside the BPB. When infecting a computer, Gapz writes the bootkit body before the very first partition if there is enough space or after the last partition otherwise, and it modifies the HiddenSectors field to point to the start of the rootkit body on the hard drive rather than to the legitimate IPL code (see Figure 12-4). As a result, during the next bootup, the VBR code loads and executes the Gapz bootkit code from the end of the hard drive.
Figure 12-4: Gapz bootkit infection layout
What makes this technique particularly clever is that it modifies only 4 bytes of the VBR data, considerably less than other bootkits. For instance, TDL4 modifies the MBR code, which is 446 bytes; Olmasco changes an entry in the MBR partition table, which is 16 bytes; and Rovnix alters IPL code that takes up 15 sectors, or 7,680 bytes.
Gapz appeared in 2012, at a time when the security industry had caught up with modern bootkits and MBR, VBR, and IPL code monitoring had already become normal practice. However, by altering the HiddenSectors field of the BPB, Gapz pushed bootkit infection techniques one step further and left the security industry behind. Before Gapz, it wasn’t common for security software to inspect the BPB’s fields for anomalies. It took some time for the security industry to get wise to its novel infection method and develop solutions.
Another thing that sets Gapz apart is that the contents of the field HiddenSectors aren’t fixed for BPB structures—they can differ from one system to another. The value of HiddenSectors depends largely on the partition scheme of the hard drive. In general, security software cannot determine whether a system is infected or not using just the HiddenSectors value; it must perform a deeper analysis of the actual code located at the offset.
Figure 12-5 displays the contents of the VBR taken from a real system infected with Gapz. The BPB is located at offset 11 and the HiddenSectors field, holding the value 0x00000800, is highlighted.
Figure 12-5: The HiddenSectors value on an infected system
To be able to detect Gapz, the security software must analyze the data located at offset 0x00000800 from the beginning of the hard drive. This is where the malicious bootloader is located.
As with many modern bootkits, the main purpose of the Gapz bootkit code is to compromise the operating system by loading malicious code into kernel-mode address space. Once the Gapz bootkit code receives control, it proceeds with the regular routine of patching OS boot components, as described in previous chapters.
Once executed, the bootkit code hooks the INT 13h handler in order to monitor data being read from the hard drive. Then it loads the original IPL code from the hard drive and executes it to resume the boot process. Figure 12-6 shows the boot process in a system infected with Gapz.
After hooking INT 13h ➊, the malware monitors data read from the hard drive and looks for the bootmgr module, which in turn patches in memory in order to hook the Archx86TransferTo32BitApplicationAsm (Archx86TransferTo64BitApplicationAsm for x64 Windows platforms) routine ➋. This routine transfers control from bootmgr to the entry point of winload.exe. The hook is used to patch the winload.exe module. Once the hook in bootmgr is triggered, winload.exe is already in memory and the malware can patch it. The bootkit hooks the OslArchTransferToKernel routine ➌ in the winload.exe module.
As discussed in the previous chapter, Rovnix also started by hooking the INT 13h handler, patching bootmgr, and hooking OslArchTransferToKernel. But, unlike Gapz, in the next step Rovnix compromised the kernel by patching the kernel KiSystemStartup routine.
Figure 12-6: The workflow of the bootkit
Gapz, on the other hand, hooks another routine in the kernel image: IoInitSystem ➍. The purpose of this routine is to complete the kernel initialization by initializing different OS subsystems and calling the entry points of the boot start drivers. Once IoInitSystem is executed, the malicious hook is triggered, restoring the patched bytes of the IoInitSystem routine and overwriting IoInitSystem’s return address on the stack with an address to the malicious code. The Gapz bootkit then releases control back to the IoInitSystem routine.
Upon completion of the routine, control is transferred back to the malicious code. After IoInitSystem executes, the kernel is properly initialized, and the bootkit can use the services it provides to access the hard drive, allocate memory, create threads, and more. Next, the malware reads the rest of the bootkit code from the hard drive, creates a system thread, and, finally, returns control to the kernel. Once the malicious kernel-mode code is executed in the kernel-mode address space, the bootkit’s job is finished ➎.
In this section, we’ll focus on the rootkit functionality of the malware, the most interesting aspect of Gapz after its bootkit functionality. We’ll refer to the Gapz rootkit functionality as the kernel-mode module since it isn’t a valid kernel-mode driver, in the sense that it isn’t a PE image at all. Rather, it’s laid out as position-independent code consisting of several blocks, each of which implements specific functionality of the malware to complete a certain task. The purpose of the kernel-mode module is to secretly and silently inject a payload into the system processes.
One of the most interesting aspects of the Gapz kernel-mode module is that it implements a custom TCP/IP network stack to communicate with C&C servers; it uses a crypto library with custom implementations of such crypto primitives as RC4, MD5, SHA1, AES, and BASE64, to protect its configuration data and C&C communication channel. And, as with any other complex threat, it implements hidden storage to secretly store its user-mode payload and configuration information. Gapz also includes a powerful hooking engine with a built-in disassembler to set up persistent and stealthy hooks. In the rest of this section, we will consider these and more aspects of the Gapz kernel-mode module in detail.
The Gapz kernel-mode module isn’t a conventional PE image but rather is composed of a set of blocks with position-independent code (PIC), which doesn’t use absolute addresses to reference data. Therefore, its memory buffer may be located at any valid virtual address in a process’s address space. Each block serves a specific purpose. A block is preceded by a header describing its size and position in the module and some constants used to calculate the addresses of the routines implemented within that block. The layout of the header is shown in Listing 12-5.
struct GAPZ_BASIC_BLOCK_HEADER
{
// A constant that is used to obtain addresses
// of the routines implemented in the block
➊ unsigned int ProcBase;
unsigned int Reserved[2];
// Offset to the next block
➋ unsigned int NextBlockOffset;
// Offset of the routine performing block initialization
➌ unsigned int BlockInitialization;
// Offset to configuration information
// from the end of the kernel-mode module
// valid only for the first block
unsigned int CfgOffset;
// Set to zeroes
unsigned int Reserved1[2];
}
Listing 12-5: Gapz kernel-mode module block header
The header starts with the integer constant ProcBase ➊, used to calculate the offsets of the routines implemented in a basic block. NextBlockOffset ➋ specifies the offset of the next block within the module, allowing Gapz to enumerate all the blocks in the kernel-mode module. BlockInitialization ➌ contains the offset from the beginning of the block to the block initialization routine, executed at the kernel-mode module initialization. This routine initializes all the necessary data structures specific to the corresponding block and should be executed before any other function implemented in the block.
Gapz uses a global structure that holds all the data related to its kernel-mode code: addresses of the implemented routines, pointers to allocated buffers, and so on. This structure allows Gapz to determine the addresses of all the routines implemented in the position-independent code blocks and then execute them.
The position-independent code references the global structure using the hexadecimal constant 0xBBBBBBBB (for an x86 module). At the very beginning of the malicious kernel-mode code execution, Gapz allocates a memory buffer for the global structure. Then it uses the BlockInitialization routine to run through the code implemented in each block and substitute a pointer to the global structure for every occurrence of 0xBBBBBBBB.
The disassembly of the OpenRegKey routine implemented in the kernel-mode module looks something like Listing 12-6. Again, the constant 0xBBBBBBBB is used to refer to the address of the global context, but during execution, this constant is replaced with the actual address of the global structure in memory so that the code will execute correctly.
int __stdcall OpenRegKey(PHANDLE hKey, PUNICODE_STRING Name)
{
OBJECT_ATTRIBUTES obj_attr; // [esp+Oh] (ebp-1Ch)@1
int _global_ptr; // [esp+18h] (ebp-4h)@1
global ptr = OxBBBBBBBB;
obj_attr.ObjectName = Name;
obj_attr.RootDirectory = 0;
obj_attr.SecurityDescriptor = 0;
obj_attr.SecurityQualityOfService = 0;
obj_attr.Length = 24;
obj_attr.Attributes = 576;
return (MEMORY[0xBBBBBBB] ->Zw0penKey)(hKey, 0x20019 &ob attr);
}
Listing 12-6: Using global context in Gapz kernel-mode code
In total, Gapz implements 12 code blocks in the kernel-mode module, listed in Table 12-2. The last block implements the main routine of the kernel-mode module that starts the execution of the module, initializes the other code blocks, sets up hooks, and initiates communication with C&C servers.
Table 12-2: Gapz Kernel-Mode Code Blocks
Block number |
Implemented functionality |
1 |
General API, gathering information on the hard drives, CRT string routines, and so on |
2 |
Cryptographic library: RC4, MD5, SHA1, AES, BASE64, and so forth |
3 |
Hooking engine, disassembler engine |
4 |
Hidden storage implementation |
5 |
Hard disk driver hooks, self-defense |
6 |
Payload manager |
7 |
Payload injector into processes’ user-mode address space |
8 |
Network communication: data link layer |
9 |
Network communication: transport layer |
10 |
Network communication: protocol layer |
11 |
Payload communication interface |
12 |
Main routine |
Like most bootkits, Gapz implements hidden storage to store its payload and configuration information securely. The image of the hidden filesystem is located in a file on the hard drive at ??C:System Volume Information<XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX> where X signifies hexadecimal numbers generated based on configuration information. The layout of the hidden storage is a FAT32 filesystem. Figure 12-7 shows an example of the content of the usroverlord hidden storage directory. You can see three files stored in the directory: overlord32.dll, overlord64.dll, and conf.z. The first two files correspond to the user-mode payload to be injected into system processes. The third file, conf.z, contains configuration data.
Figure 12-7: Contents of the hidden storage usroverlord directory
To keep the information stored within the hidden filesystem secret, its content is encrypted, as shown in Listing 12-7.
int stdcall aes_crypt_sectors_cbc(int 1V, int c_text, int p_text, int num_of_sect,
int bEncrypt, STRUCT_AES_KEY *Key)
{
int result; // eax01
int _iv; // edi02
int cbc_iv[4]; // [esp+0h] [ebp-14h)@3
STRUCT_IPL_THREAD_1 *gl_struct; // [esp+10h] [ebp-4h}@1
gl_struct = 0xBBBBBBBB;
result = num_of_sect;
if ( num_of_sect )
{
➊ _iv = IV;
do
{
cbc_iv[3] = 0;
cbc_iv[2] = 0;
cbc_iv[1] = 0;
cbc iu[0] = _iv; // CBC initialization value
result = (gl_struct->crypto->aes_crypt_cbc)(Key, bEncrypt, 512, cbc_iv,
p_text, c_text);
p_text += 512; // plain text
c text += 512; // ciper text
➋ ++_iv;
--num_of_sect;
}
while( num_of_sect );
}
return result;
}
Listing 12-7: Encryption of sectors in the hidden storage
To encrypt and decrypt each sector of the hidden storage, Gapz utilizes a custom implementation of the Advanced Encryption Standard algorithm with a key length of 256 bits in cipher block chaining (CBC) mode. Gapz uses the number of the first sector ➊ being encrypted or decrypted as the initialization value (IV) for CBC mode, as shown in Listing 12-7. Then the IV for every sector that follows is incremented by 1 ➋. Even though the same key is used to encrypt every sector of the hard drive, using different IVs for different sectors results in different ciphertexts each time.
To protect itself from being removed from the system, Gapz hooks two routines on the hard disk miniport driver: IRP_MJ_INTERNAL_DEVICE_CONTROL and IRP_MJ_DEVICE_CONTROL. In the hooks the malware is interested only in the following requests.
These hooks protect the infected VBR or MBR and the Gapz image on the hard drive from being read and overwritten.
Unlike TDL4, Olmasco, and Rovnix, which overwrite the pointer to the handlers in the DRIVER_OBJECT structure, Gapz uses splicing: that is, it patches the handlers’ code itself. In Listing 12-8, you can see the hooked routine of the scsiport.sys driver image in memory. In this example, scsiport.sys is a disk miniport driver that implements the IOCTL_SCSI_XXX and IOCTL_ATA_XXX request handlers, and it is the main target of the Gapz hooks.
SCSIPORTncsiPortGlobalDispatch:
f84ce44c 8bff mov edi,edi
➊ f84ce44e e902180307 jmp ff4ffc55
f84ce453 088b42288b40 or byte ptr [ebx+408B2842h],c1
f84ce459 1456 adc a1,56h
f84ce45b 8b750c mov esi,dword ptr [ebp+0Ch]
f84ce45e 8b4e60 mov ecx,dword ptr [esi+60h}]
f84ce461 0fb609 movzx ecx,byte ptr [ecx]
f84ce464 56 push esi
f84ce465 52 push edx
f84ce466 ff1488 call dword ptr [eax+ecx*4]
f84ce469 5e pop esi
f84ce46a 5d pop ebp
f84ce46b c20800 ret 8
Listing 12-8: Hook of the scsiport!ScsiPortGlobalDispatch routine
Notice that Gapz doesn’t patch the routine at the very beginning (at 0xf84ce44c) ➊ as is so often the case with other malware. In Listing 12-9, you can see that that it skips some instructions at the beginning of the routine being hooked (for example, nop and mov edi, edi).
One possible reason for this is to increase the stability and stealthiness of the kernel-mode module. Some security software checks only the first few bytes for modifications to detect patched or hooked routines, so skipping the first few instructions before hooking gives Gapz a chance to bypass security checks.
Skipping the first few instructions of the hooked routine also prevents Gapz from interfering with the legitimate hooks already placed on the routines. For instance, in “hot-patchable” executable images for Windows, the compiler inserts the mov edi, edi instructions at the very beginning of the functions (as you can see in Listing 12-8). This instruction is a placeholder for a legitimate hook that the OS may set up. Skipping this instruction ensures that Gapz doesn’t break the OS code-patching capabilities.
The snippet in Listing 12-9 shows code from the hooking routine that analyzes the instructions of the handler to find the best location to set up the hook. It checks the operation codes of the instructions 0x90 (corresponding to nop) and 0x8B/0x89 (corresponding to mov edi, edi). These instructions may signify that the target routine belongs to a hot-patchable image and thus may be potentially patched by the OS. This way, the malware knows to skip these instructions when placing the hook.
for ( patch_offset = code_to_patch; ; patch_offset += instr.len )
{
(v42->proc_buff_3->disasm)(patch_offset, &instr);
if ( (instr.len != 1 || instr.opcode != 0x90u)
&& (instr.len != 2 || instr.opcode != 8x89u &&
instr.opcode != Ox8Bu || instr.modrm_rm != instr.modrm_reg) ) )
{
break;
}
}
Listing 12-9: Gapz using a disassembler to skip the first bytes of hooked routines
To perform this analysis, Gapz implements the hacker disassembler engine, which is available for both x86 and x64 platforms. This allows the malware to obtain not only the length of the instructions but also other features, such as the operation code of the instruction and its operands.
The Gapz kernel-mode module injects the payload into the user-mode address space as follows:
The sys directory within the hidden filesystem contains a configuration file specifying which payload modules should be injected into specific processes. The name of the configuration file is derived from the hidden filesystem AES encryption key via a SHA1 hashing algorithm. The configuration file consists of a header and a number of entries, each of which describes a target process, as shown in Figure 12-8.
Figure 12-8: Layout of the configuration file for payload injection
Each process entry has the layout shown in Listing 12-10.
struct GAPZ_PAYLOAD_CFG
{
// Full path to payload module into hidden storage
char PayloadPath[128];
// name of the process image
➊ char TargetProcess[64];
// Specifies load options: x86 or x64 and and so on
➋ unsigned char LoadOptions;
// Reserved
unsigned char Reserved[2];
// Payload type: overlord, other
➌ unsigned char PayloadType;
}
Listing 12-10: Layout of a payload configuration entry in the configuration file
The TargetProcess field ➊ contains the name of the process into which to inject the payload. The LoadOptions field ➋ specifies whether the payload module is a 32- or 64-bit image, depending on the infected system. The PayloadType field ➌ signifies whether the module to be injected is an “overlord” module or any other payload.
The module overlord32.dll (overlord64.dll for 64-bit process) is injected into the svchost.exe processes in the system. The purpose of the overlord32.dll module is to execute the Gapz commands issued by the malicious kernel-mode code. These executed commands might perform the following tasks:
The results of those commands are then transmitted back to the kernel mode. Figure 12-9 shows an example of some configuration information extracted from the hidden storage on the infected system.
Figure 12-9: An example of a payload configuration file
You can see the two modules—overlord32.dll and overlord64.dll—intended for injection into the svchost.exe processes on x86- and x64-bit systems, respectively.
Once a payload module and a target process have been identified, Gapz allocates a memory buffer in the target process address space and copies the payload module into it. Then the malware creates a thread in the target process to run the loader code. If the operating system is Windows Vista or higher, Gapz can create a new thread by simply executing the system routine NtCreateThreadEx.
In pre-Vista operating systems (such as Windows XP or Server 2003), things are a bit more complicated because the NtCreateThreadEx routine is not exported by the OS kernel. In these cases, Gapz reimplements some of the NtCreateThreadEx functionality in the kernel-mode module and follows these steps:
The loader code is responsible for mapping the payload into a process’s address space and is executed in user mode. Depending on the payload type, there are different implementations for the loader code, as shown in Figure 12-10. For payload modules implemented as DLL libraries, there are two loaders: a DLL loader and a command executer. For payload modules implemented as EXE modules, there are also two loaders.
Figure 12-10: Gapz injection capabilities
We’ll look at each loader now.
The Gapz DLL loader routine is responsible for loading and unloading DLLs. It maps an executable image into the user-mode address space of the target process, initializes its IAT, fixes relocations, and executes the following export routines depending on whether the payload is loaded or unloaded:
Export routine #1 (loading payload) Initializes the loaded payload
Export routine #2 (unloading payload) Deinitializes the loaded payload
Figure 12-11 shows the payload module overlord32.dll.
Figure 12-11: Export address table of the Gapz payload
Figure 12-12 illustrates the routine. When unloading the payload, Gapz executes export routine #2 and frees memory used to hold the payload image. When loading the payload, Gapz performs all the necessary steps to map the image into the address space of the process and then execute export routine #1.
Figure 12-12: Gapz DLL payload-loading algorithm
The command executor routine is responsible for executing commands as instructed by the loaded payload DLL module. This routine merely calls export routine #3 (Figure 12-11) of the payload and passes all the necessary parameters to its handler.
The two remaining loader routines are used to run downloaded executables in the infected system. The first implementation runs the executable payload from the TEMP directory: the image is saved into the TEMP directory and the CreateProcess API is executed, as indicated in Figure 12-13.
Figure 12-13: Gapz EXE payload-running algorithm via CreateProcess
The second implementation runs the payload by creating a suspended legitimate process, then overwriting the legitimate process image with the malicious image; after that, the process is resumed, as illustrated in Figure 12-14.
Figure 12-14: Gapz EXE payload-running algorithm via CreateProcessAsUser
The second method of loading the executable payload is stealthier and less prone to detection than the first. While the first method simply runs the payload without any precautions, the second method creates a process with a legitimate executable first and only then replaces the original image with the malicious payload. This may trick the security software into allowing the payload to execute.
In order to communicate with the injected payload, Gapz implements a specific interface in quite an unusual way: by impersonating the handler of the payload requests in the null.sys driver. This technique is shown in Figure 12-15.
Figure 12-15: Gapz payload interface architecture
The malware first sets the DriverUnload field ➊ of the DRIVER_OBJECT structure corresponding to the DeviceNull device object to 0 (storing a pointer to the handler that will be executed when the OS unloads the driver) and hooks the original DriverUnload routine. Then it overwrites the address of the IRP_MJ_DEVICE_CONTROL handler in the DRIVER_OBJECT with the address of the hooked DriverUnload routine ➋.
The hook checks the parameters of the IRP_MJ_DEVICE_CONTROL request to determine whether the request was initiated by the payload. If so, the payload interface handler is called instead of the original IRP_MJ_DEVICE_CONTROL handler ➌.
A snippet of the DriverUnload hook is shown in Listing 12-11.
hooked_ioctl = MEMORY[0xBBBBBBE3]->IoControlCode_HookArray;
➊ while ( *hooked_ioctl != IoStack->Parameters.DeviceIoControl_IoControlCode )
{
++1; // check if the request comes from the payload
++hooked_ioctl;
if ( i >= IRP_MJ_SYSTEM_CONTROL )
goto LABEL_11;
}
UserBuff = Irp->UserBuffer;
IoStack = IoStack->Parameters_DeviceIoControl.OutputBufferLength;
OutputBufferLength = IoStack;
if ( UserBuff )
{
// decrypt payload request
➋ (MEMORY [0xBBBBBBBF]->rc4)(UserBuff, IoStack, MEMORY [0xBBBBBBBB]->rc4_key, 48);
v4 = 0xBBBBBBBB;
// check signature
if ( *UserBuff == 0x34798977 )
{
hooked_ioctl = MEMORY [0xBBBBBBE3];
IoStack = i;
// determine the handler
if ( UserBuff[1] == MEMORY [0xBBBBBBE3]->IoControlCodeSubCmd_Hook[i] )
{
➌ (MEMORY [0xBBBBBBE3] ->IoControlCode_HookDpc[i])(UserBuff);
➍ (MEMORY [0xBBBBBBBF]( ->rc4)( // encrypt the reply
UserBuff,
OutputBufferLength,
MEMORY [0xBRBBBBBB] ->rc4_key,
48);
v4 = 0xBBBBBBBB;
}
_Irp = Irp;
}
}
Listing 12-11: Hook of DriverUnload of null.sys
Gapz checks at ➊ if the request is coming from the payload. If so, it decrypts the request using the RC4 cipher ➋ and executes the corresponding handler ➌. Once the request is handled, Gapz encrypts the result ➍ and sends it back to the payload.
The payload can send requests to the Gapz kernel-mode module using the code in Listing 12-12.
// open handle for DeviceNULL
➊ HANDLE hNull = CreateFile(_T("\??\NUL"), ...);
if(hNull != INVALID_HANDLE_VALUE) {
// Send request to kernel-mode module
➋ DWORD dwResult = DeviceIoControl(hNUll, WIN32_GAPZ_IOCTL, InBuffer, InBufferSize, OutBuffer,
OutBufferSize, &BytesRead);
CloseHandle(hNull);
}
Listing 12-12: Sending a request from the user-mode payload to the kernel-mode module
The payload opens a handle to the NULL device ➊. This is a system device, so the operation shouldn’t draw the attention of any security software. Once the payload obtains the handle, it communicates with the kernel-mode module using the DeviceIoControl system API ➋.
The bootkit communicates with C&C servers over the HTTP protocol, whose main purpose is to request and download the payload and report back the bot status. The malware enforces encryption to protect the confidentiality of the messages being exchanged and to check the authenticity of the message source in order to prevent subversion by commands from fake C&C servers.
The most striking feature of the network communication is the way in which it is implemented. There are two ways the malware sends a message to the C&C server: by using the user-mode payload module (overlord32.dll or overlord64.dll) or using a custom kernel-mode TCP/IP protocol stack implementation. This network communication scheme is shown in Figure 12-16.
The user-mode payload, overlord32.dll or overlord64.dll, sends the message to the C&C server using a Windows socket implementation. The custom implementation of the TCP/IP protocol stack relies on the miniport adapter driver. Normally, network communication requests pass through the network driver stack, and at different layers of the stack they may be inspected by security software drivers. According to Microsoft’s Network Driver Interface Specification (NDIS), the miniport driver is the lowest driver in the network driver stack, so by sending network I/O packets directly to the miniport device object, Gapz can bypass all the intermediate drivers and avoid inspection (see Figure 12-17).
Figure 12-16: Gapz network communication scheme
Figure 12-17: Gapz custom network implementation
Gapz obtains a pointer to the structure describing the miniport adapter by manually inspecting the NDIS library (ndis.sys) code. The routine responsible for handling NDIS miniport adapters is implemented in block #8 of the kernel-mode module.
This approach allows Gapz to use the socket interface to communicate with the C&C server without being noticed. The architecture of the Gapz network subsystem is summarized in Figure 12-18.
Figure 12-18: Gapz network architecture
As you can see, the Gapz network architecture implements most layers of the Open Systems Interconnection (OSI) model: data link, transport, and application. To send and receive network packets to and from the physical device object that represents the network interface card, Gapz uses a corresponding interface available in the system (provided by the network card driver). However, all the work related to creating and parsing network frames is entirely implemented in the malware’s custom network stack.
As you’ve seen, Gapz is complex malware with a very elaborate implementation and one of the most remarkably covert bootkits due to its VBR infection technique. No previously known bootkit can boast such a simultaneously elegant and subtle infection approach. Its discovery forced the security industry to step up its bootkit detection approaches and dig deeper into MBR/VBR scanning, looking not only at MBR/VBR code modifications but also at parameters and data structures that were previously considered out of scope.
18.218.11.211