分类: 网络与安全
2011-08-19 17:24:33
In this final article in this series, we will describe the process of loading the bootkit previously discussed in “” and “”. First of all we explain briefly how the normal boot process is handled on different systems, presenting only the minimum information necessary to understand the overall process. Then we show how the bootkit exploits certain features of the boot process so as to get itself loaded.
Booting BIOS firmwareWhen the computer is switched on, the BIOS (Basic Input/Output System) firmware is loaded into memory and performs initialization and POST (Power On Self Test). Then it looks for a bootable disk drive and reads its very first sector, the boot sector. The sector contains the disk’s partition table and code responsible for further handling of the boot process: these together are referred to as the MBR (Master Boot Record). The MBR code reads the partition table, looks for an active partition and loads its first sector (VBR, Volume Boot Record), which contains file system-specific boot code. Up to this point the boot process is the same for both Windows Vista family operating systems (Windows Vista, Windows Server 2008, and Windows 7) and pre-Windows Vista operating systems (Windows 2000, Windows XP, Windows Server 2003) but thereafter it’s handled differently. We’ll describe the boot process for each class of operating systems in separate subsections.
Booting OS’s prior to Windows VistaThe VBR contains code that reads ntldr (an application loading kernel, nt loader) from the root directory of the hard drive into memory and transfers control to it. Ntldr consists of two parts:
As soon as ntldr starts to execute it switches the processor into protected mode, loads the embedded PE image (osloader.exe) and transfers control to it. Osloader.exe is responsible for reading configuration information (boot.ini file, system hive), gathering information about hardware in the system (this feature is implemented in a separate module ntdetect.com), and loading the appropriate version of the kernel and its dependencies, which are described in the table below with more detail given in Figure 1.
Module name |
Description |
hall.dll |
hardware abstraction layer |
bootvid.dll |
the module responsible for displaying graphical images during boot time |
kdcom.dll |
the module implementing the debugger interface through the serial port |
Figure 1 – Dependencies of ntoskrnl.exe
In addition, osloader.exe loads the file system driver and boot start drivers. Although the code of osloader.exe is executed in protected mode, it still relies on BIOS services to perform IO operations to and from hard drive and console (in the case of IDE disks). To be able to call BIOS services which are executed in the 16-bit real mode execution environment, osloader.exe briefly switches the processor into real mode, executes a BIOS service and after that switches the processor back to protected mode. We’ll see later how the bootkit exploits this feature.
When all these operations are completed, osloader.exe proceeds with calling the entry point of the kernel image — KiSystemStartup. The last element to mention plays an important role in the process of loading the bootkit — during the kernel initialization the exported function KdDebuggerInitialize1 from kdcom.dll library is called in order to initialize the debugging facilities of the system.
Figure 2– Boot process of a pre-Windows Vista OS
Booting Post Windows XP
In the case of operating systems of the Windows Vista family (Windows Vista, Windows 7, Windows Server 2008) the boot process is rather different to that of earlier OS versions. First of all, the code stored in the VBR loads bootmgr instead of loading ntldr — bootmgr is a boot time application introduced for the first time in Windows Vista OS for compatibility with the UEFI (Unified Extensible Firmware Interface: ) specification. Essentially, bootmgr has a similar structure to ntldr: that is, it consists of a 16-bit stub and a 32-bit PE image. The stub is executed in the real-mode execution environment and is responsible for switching the processor into 32-bit protected mode, as well as providing an interface for invoking 16-bit real mode BIOS services (as ntdlr does).
Bootmgr reads the BCD (boot configuration data) and then proceeds with loading either winload.exe or winresume.exe (to restore the state of the hibernating system). Winload.exe is similar in functionality to osloader.exe (embedded PE image in ntldr) and performs initialization of the system based on parameters provided in BCD, before transferring control to the kernel image:
Kernel-mode code integrity policy determines the way the system checks the integrity of all the modules loaded into kernel-mode address space, including system modules loaded at boot time. Kernel-mode integrity policy is controlled by the following BCD options:
BCD options |
Description |
BcdLibraryBoolean_DisableIntegrityCheck | disables kernel-mode code integrity checks |
BcdOSLoaderBoolean_WinPEMode |
instructs kernel to be loaded in pre-installation mode, disabling kernel-mode code integrity checks as a byproduct |
BcdLibraryBoolean_AllowPrereleaseSignatures |
enables test signing |
Thus, if one of the first two options in BCD is set, then kernel-mode code integrity checks will be disabled.
When all the necessary modules are loaded winload.exe proceeds with transferring control to kernel’s entry point. As is the case with Windows operating systems prior to Windows Vista, the code performing kernel initialization calls the exported function KdDebuggerInitialize1 from the kdcom.dll library to initialize the debugging facilities of the system.
Figure 3 – Boot process of post Windows Vista OS
Loading the bootkitHere we describe how the bootkit is loaded in the system with respect to the boot process described in the corresponding subsections.
When the system is started, the BIOS reads the infected MBR into memory end executes its code, thereby loading the first part of the bootkit. The infected MBR locates the bootkit’s file system at the end of the bootable hard drive, and loads and executes a file with the name “ldr16″, which contains code responsible for hooking the BIOS 13th interrupt handler (disk service) and restoring the original MBR. This is stored in the file called “mbr” in the hidden file system (see figure 21).
Figure 4 – Hooking Int 13h Handler and Restoring Original MBR
When the control is transferred to the original MBR, the boot process continues as described in the previous sections while the bootkit is resident in memory, and controls all the IO operations to/from the hard drive. The most interesting part of “ldr16″ lies in the new Int 13h handler.
The code reading data from the hard drive during the boot process relies on the BIOS service, specifically, interrupt 13h, which is intercepted by the bootkit: thus, the bootkit is able to counterfeit any data read from the hard drive during the boot process. The bootkit takes advantage of the opportunity by replacing kdcom.dll with a file “ldr32″ or “ldr64″ (depending on the bit capacity of the operating system) from the hidden file system, substituting its content in the memory buffer during the read operation. “ldr32″ and “ldr64″ are essentially the same, functionally, except that “ldr32″ is a 32-bit DLL and “ldr64″ is a 64-bit DLL. Both of these modules export the same symbols as the original kdcom.dll library to conform to the requirements of the interface used to communicate between ntoskrnl.exe and the serial debugger.
Figure 5 – Export Table of ldr32 (ldr64)
All the exported functions from the malicious kdcom.dll do nothing but return 0, with the exception of KdDebuggerInitialize1 which, as you will remember, is called by ntoskrnl.exe during the kernel initialization. This function actually contains code loading the bootkit’s driver into the system in the following ways:
Replacing original “kdcom.dll” with a malicious DLL allows the bootkit to achieve two aims: to load the bootkit’s driver, and to disable kernel-mode debugging facilities.
In order to replace the original kdcom.dll with the malicious DLL, it is necessary on operating systems from Windows Vista onwards to disable kernel-mode code integrity checks: otherwise winload.exe will refuse to continue the boot process, and will report an error. The bootkit turns off code integrity checks by instructing winload.exe to load the kernel in pre-installation mode. This is achieved when bootmgr reads BCD from the hard drive by replacing BcdLibraryBoolean_EmsEnabled (encoded as 16000020 in BCD) element with BcdOSLoaderBoolean_WinPEMode (encoded as 26000022 in BCD) in the same way as it spoofs kdcom.dll:
Figure 6 – Enabling Preinstallation Mode
BcdLibraryBoolean_EmsEnabled is an inheritable object indicating whether global emergency management services redirection should be enabled, and is set to “true” by default. The bootkit turns on pre-installation mode for a while and disables it by corrupting the /MININT string option in the winload.exe image while reading winload.exe image from the hard drive:
Figure 7 – Subverting the /MININT Option
Winload.exe uses the /MININT option to notify the kernel that pre-installation mode is enabled. As a result of these manipulations, the kernel receives an invalid IN/MINT option and continues initialization normally, as if pre-installation mode wasn’t enabled. The process of loading the bootkit on the Windows Vista operating system is shown in figure 25.
Figure 8 – Process of Loading the Bootkit in Windows Vista OS
Bypassing kernel-mode driver signature checkFor the 64-bit version of Microsoft Windows Vista and later, kernel-mode code signing policy requires that all kernel-mode drivers must be signed, otherwise the driver won’t be loaded. Until now that was the major obstacle to creating a fully operational kernel-mode rootkit for 64-bit operating systems.
The bootkit takes quite an efficient approach to bypassing this policy. It penetrates into kernel-mode address space at the earliest stage of system initialization and loads its drivers without using any of the facilities provided by the operating system. In other words it performs the following steps:
After these steps the rootkit’s driver is loaded into kernel-mode address space and is fully operational.
Booting UEFI FirmwareIf the system’s firmware is compliant with the UEFI specification, the boot process is handled differently to the way in which BIOS firmware handles it. When the system starts up, the firmware stored in NVRAM reads BCD which is also located in NVRAM, and based on the available options, proceeds to execute winload.exe or winresume.exe. As we can see here, the MBR code is not executed at all, while BCD is read from nonvolatile RAM but not from the disk, so the bootkit fails to load on systems with such firmware.
ConclusionIn this research we focused on the most interesting and exceptional features of the Win32/Olmarik bootkit. Special attention was paid to the bootkit functionality which appeared in TDL4 and enabled it to begin its launch process before the OS is loaded, as well as its ability to load an unsigned kernel-mode driver — even on systems with kernel-mode code signing policy enabled — and bypassing kernel-mode patch protection mechanisms. These characteristics all make TDL4’s a prominent player on the malware scene.
Appendix: Network activity log from ESET TDL4 tracking system