First off, before we get into the fun debugging/reversal, what do we know about Rustock? We know a lot! It's a fairly dated rootkit, and has been reversed time and time again by researchers, etc. It's a great example to use when showing some of the neat things a rootkit can do. It was originally developed to distribute spam email, which was way back in the day. It was first discovered in 2006, and began to increase by a significant number in 2008. By mid 2010, it was one of the most known rootkit related threats (and arguably malware in general).
Our second component is the driver installer, which runs in kernel-mode as a disguised Windows system driver (textbook rootkit behavior). It historically replaces drivers such as beep.sys as well as null.sys with a copy, and then afterwards replaces it once started. If however this replacement method is unsuccessful, it falls back to a method I've seen occur much more, which the dropper will instead use a randomly-generated or hard-coded filename for the driver.
Two hard-coded filenames have been glaide32.sys and lzx32.sys, with the latter being the most popular. As far as randomly-generated filenames go, 7005d59.sys was the most typical. Older versions of the rootkit would install themselves to null shares to hide in a system driver, and then proceed to drop the installer as an alternate data stream (ADS) (%Windir%\System32:lzx32.sys, for example). Modern versions of the rootkit however use system service hooking.
Our third and final component is the rootkit driver, which runs in kernel-mode like the driver installer. As we discussed above regarding our first component, this component is decrypted by the dropper which then allows the rootit driver to inject a copy of its decrypted code into itself before transferring control over to the newly instantiated copy. The decryption process is accomplished inside a buffer allocated in kernel memory by using ExAllocatePool. It specifically contains the code managing the backdoor functionality, such as the actual ability to contact the C&C server discussed above, and executing instructions sent by Rustock operators.
The kernel-mode side of the rootkit communicates with its user-mode bot component (C&C, etc) using INT 2Eh interrupts for NT/2k (a bit different for XP), which will be shown in action coming up. Aside from communication, the rootkit component hid itself by hooking different SSDT functions such as:
It hid its network/disk operations by hooking ntoskrnl.dll and ntdll.dll functions, as well as various network drivers such as:
It hooked the following network drivers to bypass firewalls and manipulate packets.
In addition to the INT 2Eh interrupts being shown in action, I'll also be showing all of the various hooking, etc.
Now that we've gotten some of the history and information out of the way, let's start with the debugging and reversal of the rootkit.
I had to go through a few hoops to create an environment in which Rustock.B could be properly examined. It wasn't unfortunately as simple as executing it on an XP VM, although it wasn't excruciatingly painful to set up either. Also, for any amateur malware analysts who get curious (like me) and try to execute Rustock on Windows 7 x86 to see what will happen, it throws an access violation : ) Nothing too cool, unfortunately! I have however read reports saying it runs on the beta of Vista.
After I had the basics done (isolated from host network, etc), I had to make three changes to get the rootkit to properly execute on an XP SP2 guest:
1. Disable both Physical Address Extension (PAE) and Data Execution Prevention (DEP). This is easily done by modifying the boot.ini to look like the following:
/execute parameter is another way of saying /noexecute=alwaysoff, which disables DEP and PAE.
/fastdetect parameter disables detection on all serial and parallel ports. It's not necessary in this case by any means, but it does allow for a slightly faster boot time. It's just a habit from the XP days : )
/NUMPROC=1 and NUMPROC=4 are almost self-explanatory, really. This parameter limits the OS when it boots to either 1 core or 4 cores. In our case, Rustock (afaik) cannot execute on anything more than 2 cores, so I went with 1 for safety (thanks EP_X0FF). In most cases, a lot of older malware cannot function when PAE is enabled, even if it's using modern day techniques. Here's what it looks like at the boot selection screen:
2. Uninstall VMware Tools, restart.
3 (optional). Insert the following into the VM config file:
After the above, Rustock executes as expected with no problems.
One of the first few things Rustock does as discussed above is create a registry subkey associated with a hidden service known as pe386. By using SwishDbgExt as we've used many times before in my blog posts, we can dump the list of services on the system using the !ms_services command:
As we can see, this successfully shows us our hidden service, and notes it is in fact running. With this said, we can confirm infection was a success.
As we discussed above, older versions of Rustock use alternate data streams (ADS). It goes one step further and prevents access from NTFS.sys (NT File System driver) or FASTFAT.sys (FAT File System driver), therefore they cannot directly communicate with the files in the data stream. It does this by hooking various file system related IRP functions that control create/delete operations regarding the ADS stream. Rustock often hooks IoCallDriver, which sends an IRP to certain drivers. We can the act of filtering in action here:
The poi operator is used so when the parameter contains IofCallDriver, WinDbg will break at the specified address.
The !address command is used afterwards on the SP to show memory region usage and attributes.
Rustock hooks IA32_SYSENTER_EIP (0x176) for XP (remember, INT 2Eh interrupts for NT/2k), which is the kernel's EIP for SYSENTER. SYSENTER is an Intel instruction which enables fast entry to the kernel, avoiding interrupt overhead. AMD's version is known as SYSCALL, which overall does the same thing, although operates a bit differently. In any case, as I discussed earlier in the post, this is what Rustock uses to communicate between user-mode and kernel-mode. It's also ultimately hooked to execute code every time a system call is made.
As we have a modified SYSENTER handler, this is where SSDT functions labeled above come into play. This was done to intercept system calls on a thread-level basis rather than using KeServiceDescriptorTable to hook on a global basis.
1. ZwOpenKey's API was modified so that whenever anything but services.exe tried to obtain a handle, it'd return STATUS_OBJECT_NAME_NOT_FOUND. This was done to prevent unauthorized access to the pe386 key.
2. ZwCreateKey's API was modified similarly to that of OpenKey, which is when any other process other than services.exe tries to create a key named pe386, CreateKey returns the same error as OpenKey.
3. ZwQuerySystemInformation's API was modified to zero out the usage time in kernel and user mode for services.exe, and adds it to the first process in the processes list (sysidle process). This was primarily done to counteract if a user were to check services.exe with Process Explorer, as it would raise red flags.
We can check for the 0x176 hook manually and automatically using a script. Let's first view the manual way:
Using our familiar !address command, we can see that to avoid easy hook detection, Rustock has the EIP address point to the same module as KiFastCallEntry (ntoskrnl.exe, or another variation of the NT Kernel). I've seen ntkrnlpa.exe as well.
dc is actually a parameter to show ASCII characters and dwords. d* on its own simply means 'display memory'. I've discussed this command in a previous blog post, but I believe it was dd that I used in that scenario. dd is the same as dc, except it doesn't display ASCII characters.
By using this command on the 0x176 MSR address, this is where we can see Rustock replaced the FATAL_UNHANDLED_HARD_ERROR string with malicious code that's ultimately used to execute various functions of the rootkit. Hilariously enough, the original meaning of this string is a bug check code (0x4C).
We can see where it performs a jump to its malicious code by further disassembling the MSR address. Unfortunately I forgot to bring the .txt file containing the WinDbg code, so I loaded up a snapshot and did the disassembly real quick to show in an image:
Now that we've seen how to manually view the 0x176 hook manually, let's view it automatically using another tool we've used before, the SysecLabs script:
In addition to hooking SYSENTER, it also hooks the Interrupt Descriptor Table (IDT). The IDT is used to properly respond to interrupts and exceptions. We can view the IDT with SwishDbgExt:
As mentioned earlier above, Rustock also hooks INT 2Eh to communicate between its user and kernel mode components. This is done specifically for older systems/hardware that don't support SYSENTER fastcalls, as KiSystemService is a user mode functions dispatcher and handler. We can see the hook here:
On a healthy x86 system, if you go ahead and dump the IDT, the only thing that should show is nt!KiSystemService. For example:
Removal
These days, the removal of Rustock is extremely trivial. When I ran GMER, Rustock would cause it to hang inevitably. I imagined this would occur, even with the random .exe name. However, I tried something strange out of curiosity and it ended up working, which was to run as owner. Before it successfully scanned however without hanging interruptions, here's what it displayed:
After pressing 'OK' for both, GMER successfully scanned. Here were the results:
We can see GMER detected the rootkit without too much issue, and we can also see our best friend pe386.
Removal was pretty painless, all I had to do was kill and delete the service by right-clicking it within GMER, and also ridding of the process, library, and module. After a restart was completed, performing a live debugging showed completely opposite (and normal) results. I will show them below, one at a time.
PE386
SYSENTER Hook - Manual
SYSENTER Hook - Script
IDT Hook
INT 2Eh Hook
Thanks so much for reading, I hope you enjoyed!
References
BackdoorRustockB
On the Cutting Edge: Thwarting Virtual Machine Detection
Malware 101 - Viruses
Hunting rootkits with Windbg (as always for the great reference).
Rustock has three encrypted components which we will discuss below, one at a time:
Dropper Component
The dropper is the bad guy, the guy nobody likes. Malware droppers have one primary job, and it's once they are executed, install the specified malware. Malware writers can have their droppers do other things however, which Rustock's of course does. They are called droppers because they essentially 'drop' the malware onto the target system.
Rustock's dropper runs specifically in user-mode, and decrypts/drops the rootkit component driver (our 3rd component that we will discuss later on). Interestingly enough, during the rootkit's time period of prevalence, the dropper also went ahead and contacted a Command and Control (C&C) Server to check for updates. C&C's have different structures, all of which are different. In most cases however, especially in its most basic definition, C&C's are used to send commands and receive outputs of machines part of a botnet.
In addition to contacting a C&C server, thedropper component also checks the registry to ensure that a previous Rustock infection hasn't already taken place so reinfection (which could cause obvious problems) doesn't happen. It checks the registry as there are keys which are installed when an infection takes place, such as PE386 (the key used to survive a reboot among other things).
Driver Installer Component
Two hard-coded filenames have been glaide32.sys and lzx32.sys, with the latter being the most popular. As far as randomly-generated filenames go, 7005d59.sys was the most typical. Older versions of the rootkit would install themselves to null shares to hide in a system driver, and then proceed to drop the installer as an alternate data stream (ADS) (%Windir%\System32:lzx32.sys, for example). Modern versions of the rootkit however use system service hooking.
Rootkit Driver Component
Our third and final component is the rootkit driver, which runs in kernel-mode like the driver installer. As we discussed above regarding our first component, this component is decrypted by the dropper which then allows the rootit driver to inject a copy of its decrypted code into itself before transferring control over to the newly instantiated copy. The decryption process is accomplished inside a buffer allocated in kernel memory by using ExAllocatePool. It specifically contains the code managing the backdoor functionality, such as the actual ability to contact the C&C server discussed above, and executing instructions sent by Rustock operators.
The kernel-mode side of the rootkit communicates with its user-mode bot component (C&C, etc) using INT 2Eh interrupts for NT/2k (a bit different for XP), which will be shown in action coming up. Aside from communication, the rootkit component hid itself by hooking different SSDT functions such as:
- ZwQuerySystemInformation
- ZwCreateKey
- ZwOpenKey
- ...will discuss later.
It hid its network/disk operations by hooking ntoskrnl.dll and ntdll.dll functions, as well as various network drivers such as:
- tcpip.sys
- wanarp.sys
- ndis.sys
It hooked the following network drivers to bypass firewalls and manipulate packets.
In addition to the INT 2Eh interrupts being shown in action, I'll also be showing all of the various hooking, etc.
Now that we've gotten some of the history and information out of the way, let's start with the debugging and reversal of the rootkit.
Rootkit Debugging/Reversal
I had to go through a few hoops to create an environment in which Rustock.B could be properly examined. It wasn't unfortunately as simple as executing it on an XP VM, although it wasn't excruciatingly painful to set up either. Also, for any amateur malware analysts who get curious (like me) and try to execute Rustock on Windows 7 x86 to see what will happen, it throws an access violation : ) Nothing too cool, unfortunately! I have however read reports saying it runs on the beta of Vista.
After I had the basics done (isolated from host network, etc), I had to make three changes to get the rootkit to properly execute on an XP SP2 guest:
1. Disable both Physical Address Extension (PAE) and Data Execution Prevention (DEP). This is easily done by modifying the boot.ini to look like the following:
Code:
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft XP Home Edition" /execute /fastdetect
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft XP Home Edition, 1 core" /execute /fastdetect /NUMPROC=1
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft XP Home Edition, 4 cores" /execute /fastdetect /NUMPROC=4
/execute parameter is another way of saying /noexecute=alwaysoff, which disables DEP and PAE.
/fastdetect parameter disables detection on all serial and parallel ports. It's not necessary in this case by any means, but it does allow for a slightly faster boot time. It's just a habit from the XP days : )
/NUMPROC=1 and NUMPROC=4 are almost self-explanatory, really. This parameter limits the OS when it boots to either 1 core or 4 cores. In our case, Rustock (afaik) cannot execute on anything more than 2 cores, so I went with 1 for safety (thanks EP_X0FF). In most cases, a lot of older malware cannot function when PAE is enabled, even if it's using modern day techniques. Here's what it looks like at the boot selection screen:
2. Uninstall VMware Tools, restart.
3 (optional). Insert the following into the VM config file:
Code:
isolation.tools.setPtrLocation.disable = "TRUE"
isolation.tools.setVersion.disable = "TRUE"
isolation.tools.getVersion.disable = "TRUE" [COLOR=#800080]// Thwarts backdoor I/O checks.[/COLOR]
monitor_control.disable_directexec = "TRUE" [COLOR=#800080]// Thwarts descriptor table registers checks. VMware interprets each assembly instruction instead of the processor executing them.[/COLOR]
monitor_control.disable_chksimd = "TRUE"
monitor_control.disable_ntreloc = "TRUE"
monitor_control.disable_selfmod = "TRUE"
monitor_control.disable_reloc = "TRUE"
monitor_control.disable_btinout = "TRUE"
monitor_control.disable_btmemspace = "TRUE"
monitor_control.disable_btpriv = "TRUE"
monitor_control.disable_btseg = "TRUE"
After the above, Rustock executes as expected with no problems.
One of the first few things Rustock does as discussed above is create a registry subkey associated with a hidden service known as pe386. By using SwishDbgExt as we've used many times before in my blog posts, we can dump the list of services on the system using the !ms_services command:
Code:
lkd> !ms_services
Implicit process is now 821ae9a0
Loading User Symbols
[209] | 0x01 | | [COLOR=#0000ff]pe386[/COLOR]| Win23 lzx files loader | [COLOR=#ff0000]SERVICE_RUNNING[/COLOR] | [COLOR=#0000ff]\Driver\pe386 [/COLOR]
As we can see, this successfully shows us our hidden service, and notes it is in fact running. With this said, we can confirm infection was a success.
As we discussed above, older versions of Rustock use alternate data streams (ADS). It goes one step further and prevents access from NTFS.sys (NT File System driver) or FASTFAT.sys (FAT File System driver), therefore they cannot directly communicate with the files in the data stream. It does this by hooking various file system related IRP functions that control create/delete operations regarding the ADS stream. Rustock often hooks IoCallDriver, which sends an IRP to certain drivers. We can the act of filtering in action here:
Code:
lkd> u poi(poi(iofcalldriver+2))
[COLOR=#ff0000]f6fb9dae[/COLOR] 56 push esi
f6fb9daf 57 push edi
f6fb9db0 8bf9 mov edi,ecx
f6fb9db2 8b7708 mov esi,dword ptr [edi+8]
f6fb9db5 3b352ceefbf6 cmp esi,dword ptr ds:[0F6FBEE2Ch]
f6fb9dbb 7509 jne f6fb9dc6
f6fb9dbd 52 push edx
f6fb9dbe 57 push edi
The poi operator is used so when the parameter contains IofCallDriver, WinDbg will break at the specified address.
Code:
lkd> !address f6fb9dae
address f6fb9dae not found in any known Kernel Address Range ----
The !address command is used afterwards on the SP to show memory region usage and attributes.
Rustock hooks IA32_SYSENTER_EIP (0x176) for XP (remember, INT 2Eh interrupts for NT/2k), which is the kernel's EIP for SYSENTER. SYSENTER is an Intel instruction which enables fast entry to the kernel, avoiding interrupt overhead. AMD's version is known as SYSCALL, which overall does the same thing, although operates a bit differently. In any case, as I discussed earlier in the post, this is what Rustock uses to communicate between user-mode and kernel-mode. It's also ultimately hooked to execute code every time a system call is made.
As we have a modified SYSENTER handler, this is where SSDT functions labeled above come into play. This was done to intercept system calls on a thread-level basis rather than using KeServiceDescriptorTable to hook on a global basis.
1. ZwOpenKey's API was modified so that whenever anything but services.exe tried to obtain a handle, it'd return STATUS_OBJECT_NAME_NOT_FOUND. This was done to prevent unauthorized access to the pe386 key.
2. ZwCreateKey's API was modified similarly to that of OpenKey, which is when any other process other than services.exe tries to create a key named pe386, CreateKey returns the same error as OpenKey.
3. ZwQuerySystemInformation's API was modified to zero out the usage time in kernel and user mode for services.exe, and adds it to the first process in the processes list (sysidle process). This was primarily done to counteract if a user were to check services.exe with Process Explorer, as it would raise red flags.
We can check for the 0x176 hook manually and automatically using a script. Let's first view the manual way:
Code:
lkd> rdmsr 0x176
msr[176] = [COLOR=#ff0000]00000000`806ccc3d [/COLOR]
Code:
lkd> !address 806ccc3d
804d7000 - 00215000
Usage KernelSpaceUsageImage
ImageName ntoskrnl.exe
Using our familiar !address command, we can see that to avoid easy hook detection, Rustock has the EIP address point to the same module as KiFastCallEntry (ntoskrnl.exe, or another variation of the NT Kernel). I've seen ntkrnlpa.exe as well.
Code:
lkd> dc 806ccc3d
806ccc3d 8ee6c2e9 4c444e76 485f4445 5f445241 ....[COLOR=#ff0000]vNDLED_HARD_[/COLOR]
806ccc4d 4f525245 000a0d52 1c000000 4e000000 [COLOR=#ff0000]ERROR[/COLOR]..........[COLOR=#ff0000]N[/COLOR]
806ccc5d 41505f4f 5f534547 49415641 4c42414c [COLOR=#ff0000]O_PAGES_AVAILABL[/COLOR]
806ccc6d 000a0d45 18000000 50000000 4c5f4e46 [COLOR=#ff0000]E[/COLOR]..........[COLOR=#ff0000]PFN_L[/COLOR]
806ccc7d 5f545349 52524f43 0d545055 1c00000a [COLOR=#ff0000]IST_CORRUPT[/COLOR].....
806ccc8d 4e000000 5f534944 45544e49 4c414e52 ...[COLOR=#ff0000]NDIS_INTERNAL[/COLOR]
806ccc9d 5252455f 0a0d524f 24000000 50000000 [COLOR=#ff0000]_ERROR[/COLOR].....[COLOR=#ff0000]$[/COLOR]...[COLOR=#ff0000]P[/COLOR]
806cccad 5f454741 4c554146 4e495f54 4e4f4e5f [COLOR=#ff0000]AGE_FAULT_IN_NON[/COLOR]
dc is actually a parameter to show ASCII characters and dwords. d* on its own simply means 'display memory'. I've discussed this command in a previous blog post, but I believe it was dd that I used in that scenario. dd is the same as dc, except it doesn't display ASCII characters.
By using this command on the 0x176 MSR address, this is where we can see Rustock replaced the FATAL_UNHANDLED_HARD_ERROR string with malicious code that's ultimately used to execute various functions of the rootkit. Hilariously enough, the original meaning of this string is a bug check code (0x4C).
We can see where it performs a jump to its malicious code by further disassembling the MSR address. Unfortunately I forgot to bring the .txt file containing the WinDbg code, so I loaded up a snapshot and did the disassembly real quick to show in an image:
Now that we've seen how to manually view the 0x176 hook manually, let's view it automatically using another tool we've used before, the SysecLabs script:
Code:
lkd> !!display_current_msrs
###################################
# Model-Specific Registers (MSRs) #
###################################
Processor 00
IA32_P5_MC_ADDR msr[00000000] = 0
IA32_P5_MC_TYPE msr[00000001] = 0
IA32_MONITOR_FILTER_LINE_SIZE msr[00000006] = 0
IA32_TIME_STAMP_COUNTER *msr[00000010] = 000006ea`1ef96bef
IA32_PLATFORM_ID msr[00000017] = 0
IA32_APIC_BASE *msr[0000001B] = 00000000`fee00900
MSR_EBC_HARD_POWERON msr[0000002A] = 0
MSR_EBC_SOFT_POWERON msr[0000002B] = 0
MSR_EBC_FREQUENCY_ID msr[0000002C] = 0
IA32_BIOS_UPDT_TRIG msr[00000079] = 0
IA32_BIOS_SIGN_ID *msr[0000008B] = 00000028`00000000
IA32_MTRRCAP *msr[000000FE] = 00000000`00000508
IA32_SYSENTER_CS *msr[00000174] = 00000000`00000008
IA32_SYSENTER_ESP *msr[00000175] = 00000000`f8974000
IA32_SYSENTER_EIP *[COLOR=#0000ff]msr[00000176][/COLOR] = -># [COLOR=#ff0000]HOOK[/COLOR] #<- [COLOR=#ff0000]00000000`806ccc3d[/COLOR] [COLOR=#800080]nt!_NULL_IMPORT_DESCRIPTOR <PERF> (nt+0x1f5c3d) (806ccc3d)[/COLOR] => [COLOR=#008000]Original : nt!KiFastCallEntry (804def6f)[/COLOR]
In addition to hooking SYSENTER, it also hooks the Interrupt Descriptor Table (IDT). The IDT is used to properly respond to interrupts and exceptions. We can view the IDT with SwishDbgExt:
Code:
lkd> !ms_idt
|-----|-----|--------------------|--------------------------------------------------------|---------|--------|
| Cre | Idx | Address | Name | Patched | [COLOR=#800080]Hooked[/COLOR] |
|-----|-----|--------------------|--------------------------------------------------------|---------|--------|
| 0 | 0 | 0xFFFFFFFF804DFBFF | nt!KiTrap00 | | No |
| 0 | 1 | 0xFFFFFFFF804DFD7C | nt!KiTrap01 | | No |
| 0 | 2 | 0x000000000000112E | *UNKNOWN* | | No |
| 0 | 3 | 0xFFFFFFFF804E015B | nt!KiTrap03 | | No |
| 0 | 4 | 0xFFFFFFFF804E02E0 | nt!KiTrap04 | | No |
| 0 | 5 | 0xFFFFFFFF804E0441 | nt!KiTrap05 | | No |
| 0 | 6 | 0xFFFFFFFF804E05BF | nt!KiTrap06 | | No |
| 0 | 7 | 0xFFFFFFFF804E0C33 | nt!KiTrap07 | | No |
| 0 | 8 | 0x0000000000001188 | *UNKNOWN* | | No |
| 0 | 9 | 0xFFFFFFFF804E1060 | nt!KiTrap09 | | No |
| 0 | 10 | 0xFFFFFFFF804E1185 | nt!KiTrap0A | | No |
| 0 | 11 | 0xFFFFFFFF804E12CA | nt!KiTrap0B | | No |
| 0 | 12 | 0xFFFFFFFF804E1530 | nt!KiTrap0C | | No |
| 0 | 13 | 0xFFFFFFFF804E1827 | nt!KiTrap0D | | No |
| 0 | 14 | 0xFFFFFFFF804E1F25 | nt!KiTrap0E | | No |
| 0 | 15 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 16 | 0xFFFFFFFF804E237F | nt!KiTrap10 | | No |
| 0 | 17 | 0xFFFFFFFF804E24BD | nt!KiTrap11 | | No |
| 0 | 18 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 19 | 0xFFFFFFFF804E262B | nt!KiTrap13 | | No |
| 0 | 20 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 21 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 22 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 23 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 24 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 25 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 26 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 27 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 28 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 29 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 30 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 31 | 0xFFFFFFFF806EDFD0 | *UNKNOWN* | | No |
| 0 | 42 | 0xFFFFFFFF804DF417 | nt!KiGetTickCount | | No |
| 0 | 43 | 0xFFFFFFFF804DF522 | nt!KiCallbackReturn | | No |
| 0 | 44 | 0xFFFFFFFF804DF6C7 | nt!KiSetLowWaitHighThread | | No |
| 0 | 45 | 0xFFFFFFFF804E0032 | nt!KiDebugService | | No |
| 0 | 46 | 0xFFFFFFFF806CCC38 | [COLOR=#ff0000]nt!_NULL_IMPORT_DESCRIPTOR (nt+0x1f5c38)[/COLOR] | | [COLOR=#0000ff]Yes[/COLOR] |
As mentioned earlier above, Rustock also hooks INT 2Eh to communicate between its user and kernel mode components. This is done specifically for older systems/hardware that don't support SYSENTER fastcalls, as KiSystemService is a user mode functions dispatcher and handler. We can see the hook here:
Code:
lkd> !idt 2e
Dumping IDT:
[COLOR=#800080]2e[/COLOR]: [COLOR=#ff0000]806ccc38 nt!_NULL_IMPORT_DESCRIPTOR <PERF> (nt+0x1f5c38) [/COLOR]
On a healthy x86 system, if you go ahead and dump the IDT, the only thing that should show is nt!KiSystemService. For example:
Code:
kd> !idt 2e
Dumping IDT: 823f7400
18b78dea0000002e: [COLOR=#0000ff]8185c77e nt!KiSystemService [/COLOR]
Removal
These days, the removal of Rustock is extremely trivial. When I ran GMER, Rustock would cause it to hang inevitably. I imagined this would occur, even with the random .exe name. However, I tried something strange out of curiosity and it ended up working, which was to run as owner. Before it successfully scanned however without hanging interruptions, here's what it displayed:
After pressing 'OK' for both, GMER successfully scanned. Here were the results:
We can see GMER detected the rootkit without too much issue, and we can also see our best friend pe386.
Removal was pretty painless, all I had to do was kill and delete the service by right-clicking it within GMER, and also ridding of the process, library, and module. After a restart was completed, performing a live debugging showed completely opposite (and normal) results. I will show them below, one at a time.
PE386
IofCallDriver Hook
Code:
lkd> u poi(poi(iofcalldriver+2))
nt!IopfCallDriver:
[COLOR=#ff0000]804e3d50[/COLOR] fe4a23 dec byte ptr [edx+23h]
804e3d53 8a4223 mov al,byte ptr [edx+23h]
804e3d56 84c0 test al,al
804e3d58 0f8e8b860300 jle nt!IopfCallDriver+0xa (8051c3e9)
804e3d5e 8b4260 mov eax,dword ptr [edx+60h]
804e3d61 83e824 sub eax,24h
804e3d64 56 push esi
804e3d65 894260 mov dword ptr [edx+60h],eax
Code:
lkd> !address 804e3d50
804d7000 - 00215000
Usage KernelSpaceUsageImage
ImageName ntoskrnl.exe
SYSENTER Hook - Manual
Code:
lkd> rdmsr 0x176
msr[176] = [COLOR=#0000ff]00000000`804def6f [/COLOR]
Code:
lkd> dc 804def6f
804def6f 000023b9 0f306a00 8ed98ea1 400d8bc1 .#...j0........@
804def7f 8bffdff0 236a0461 026a9c52 9d08c283 ....a.j#R.j.....
804def8f 01244c80 ff1b6a02 df030435 55006aff .L$..j..5....j.U
804def9f 8b575653 dff01c1d 8b3b6aff 000124b3 SVW......j;..$..
804defaf c733ff00 ffffff03 186e8bff ec83016a ..3.......n.j...
804defbf 9ced8148 c6000002 00014086 ec3b0100 H........@....;.
804defcf ff6e850f 6583ffff 46f6002c ae89ff2c ..n....e,..F,...
804defdf 00000134 fe37850f 5d8bffff 687d8b60 4.....7....]`.}h
SYSENTER Hook - Script
Code:
lkd> !!display_current_msrs
###################################
# Model-Specific Registers (MSRs) #
###################################
Processor 00
IA32_P5_MC_ADDR msr[00000000] = 0
IA32_P5_MC_TYPE msr[00000001] = 0
IA32_MONITOR_FILTER_LINE_SIZE msr[00000006] = 0
IA32_TIME_STAMP_COUNTER *msr[00000010] = 0000007f`ec25230f
IA32_PLATFORM_ID msr[00000017] = 0
IA32_APIC_BASE *msr[0000001B] = 00000000`fee00900
MSR_EBC_HARD_POWERON msr[0000002A] = 0
MSR_EBC_SOFT_POWERON msr[0000002B] = 0
MSR_EBC_FREQUENCY_ID msr[0000002C] = 0
IA32_BIOS_UPDT_TRIG msr[00000079] = 0
IA32_BIOS_SIGN_ID *msr[0000008B] = 00000028`00000000
IA32_MTRRCAP *msr[000000FE] = 00000000`00000508
IA32_SYSENTER_CS *msr[00000174] = 00000000`00000008
IA32_SYSENTER_ESP *msr[00000175] = 00000000`f8974000
IA32_SYSENTER_EIP *[COLOR=#0000ff]msr[00000176][/COLOR] = [COLOR=#008000]00000000`804def6f nt!KiFastCallEntry (804def6f)[/COLOR]
IDT Hook
Code:
lkd> !ms_idt
|-----|-----|--------------------|--------------------------------------------------------|---------|--------|
| Cre | Idx | Address | Name | Patched | [COLOR=#800080]Hooked [/COLOR]|
|-----|-----|--------------------|--------------------------------------------------------|---------|--------|
| 0 | 0 | 0xFFFFFFFF804DFBFF | nt!KiTrap00 | | No |
| 0 | 1 | 0xFFFFFFFF804DFD7C | nt!KiTrap01 | | No |
| 0 | 2 | 0x000000000000112E | *UNKNOWN* | | No |
| 0 | 3 | 0xFFFFFFFF804E015B | nt!KiTrap03 | | No |
| 0 | 4 | 0xFFFFFFFF804E02E0 | nt!KiTrap04 | | No |
| 0 | 5 | 0xFFFFFFFF804E0441 | nt!KiTrap05 | | No |
| 0 | 6 | 0xFFFFFFFF804E05BF | nt!KiTrap06 | | No |
| 0 | 7 | 0xFFFFFFFF804E0C33 | nt!KiTrap07 | | No |
| 0 | 8 | 0x0000000000001188 | *UNKNOWN* | | No |
| 0 | 9 | 0xFFFFFFFF804E1060 | nt!KiTrap09 | | No |
| 0 | 10 | 0xFFFFFFFF804E1185 | nt!KiTrap0A | | No |
| 0 | 11 | 0xFFFFFFFF804E12CA | nt!KiTrap0B | | No |
| 0 | 12 | 0xFFFFFFFF804E1530 | nt!KiTrap0C | | No |
| 0 | 13 | 0xFFFFFFFF804E1827 | nt!KiTrap0D | | No |
| 0 | 14 | 0xFFFFFFFF804E1F25 | nt!KiTrap0E | | No |
| 0 | 15 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 16 | 0xFFFFFFFF804E237F | nt!KiTrap10 | | No |
| 0 | 17 | 0xFFFFFFFF804E24BD | nt!KiTrap11 | | No |
| 0 | 18 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 19 | 0xFFFFFFFF804E262B | nt!KiTrap13 | | No |
| 0 | 20 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 21 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 22 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 23 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 24 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 25 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 26 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 27 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 28 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 29 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 30 | 0xFFFFFFFF804E225A | nt!KiTrap0F | | No |
| 0 | 31 | 0xFFFFFFFF806EDFD0 | *UNKNOWN* | | No |
| 0 | 42 | 0xFFFFFFFF804DF417 | nt!KiGetTickCount | | No |
| 0 | 43 | 0xFFFFFFFF804DF522 | nt!KiCallbackReturn | | No |
| 0 | 44 | 0xFFFFFFFF804DF6C7 | nt!KiSetLowWaitHighThread | | No |
| 0 | 45 | 0xFFFFFFFF804E0032 | nt!KiDebugService | | No |
| 0 | 46 | 0xFFFFFFFF804DEEA6 | [COLOR=#008000]nt!KiSystemService [/COLOR] | | [COLOR=#0000ff]No[/COLOR] |
INT 2Eh Hook
Code:
lkd> !idt 2e
Dumping IDT:
[COLOR=#800080]2e[/COLOR]: [COLOR=#0000ff]804deea6 nt!KiSystemService [/COLOR]
Thanks so much for reading, I hope you enjoyed!
References
BackdoorRustockB
On the Cutting Edge: Thwarting Virtual Machine Detection
Malware 101 - Viruses
Hunting rootkits with Windbg (as always for the great reference).