Thursday, October 30, 2014

BlackEnergy 2 (alias BlackEnergy Version 2) Live Debugging

Last night I took a quick look at BlackEnergy 2, a rootkit that surfaced in 2010. BlackEnergy 2 was essentially a rewrite of its predecessor as BlackEnergy 2 contains rootkit techniques, process-injection, and encryption. Surprisingly for being a now 'dated' rootkit, there's really not too much accessible (or not buried) reverse kernel-debugging documentation for the rootkit aside from when it was first surfacing. A lot of misc. information pops up throughout very few blogs/forums that are Russian, but that's about it.

There's a lot of additional lore behind the rootkit, but I really won't go into that. If you're interested about where the rootkit core came from before it was implemented into BlackEnergy 2, BlackReleaver is the answer!

NT Corruption

First off, we can view corruption regarding ntokskrnl:


 lkd> !chkimg -d -v nt  
 Searching for module with expression: nt  
 Will apply relocation fixups to file used for comparison  
 Will ignore NOP/LOCK errors  
 Will ignore patched instructions  
 Image specific ignores will be applied  
 Comparison image path: C:\Symbols\ntoskrnl.exe\41108004214780\ntoskrnl.exe  
 No range specified  

 Scanning section:  .text  
 Size: 466369  
 Range to scan: 804d7580-80549341  
   804ded5a-804ded5d 4 bytes - nt!KiBBTUnexpectedRange+8
      [ 00 ff 09 00:6b a0 c1 01 ]  
   804e59a1-804e59a5 5 bytes - nt!KeInsertQueueApc (+0x6c47)  // Not malicious -- Malwarebytes.
      [ 8b ff 55 8b ec:e9 e4 45 4e 77 ]  
 Total bytes compared: 466369(100%)  
 Number of errors: 9  

!chkimg compares the current loaded executable with the version within the symbol store. This is a helpful command to detect corruptions with images, and especially helpful when dealing with rootkits. The -d parameter displays a summary of all mismatched areas. The -v parameter makes the information verbose. In this case, the -v parameter is optional.

As noted above, we have two out-of-range values. We're interested in disassembling nt!KiBBTUnexpectedRange+8, but not nt!KeInsertQueueApc (+0x6c47). nt!KeInsertQueueApc (+0x6c47) as I commented above is in relation to the Chameleon technology from Malwarebytes. I had MWB ARK installed on this VM for testing purposes, so that is where it was spawning from.

nt!KiBBTUnexpectedRange+8 Disassembly - Healthy

If we disassemble nt!KiBBTUnexpectedRange+8 on a system not infected with BlackEnergy 2, we should expect similar results:

 lkd> u nt!KiBBTUnexpectedRange+8  
 nt!KiBBTUnexpectedRange+0x8:  
 804ded5a 00ff      add   bh,bh  
 804ded5c 0900      or   dword ptr [eax],eax  
 804ded5e 0bc0      or   eax,eax  
 804ded60 58       pop   eax  
 804ded61 5a       pop   edx  
 804ded62 8bec      mov   ebp,esp  
 804ded64 89ae34010000  mov   dword ptr [esi+134h],ebp  
 804ded6a 0f8490020000  je   nt!KiFastCallEntry+0x8d (804df000)  

nt!KiBBTUnexpectedRange+8 Disassembly - Corrupted

If we disassemble nt!KiBBTUnexpectedRange+8 on a system that has been infected with BlackEnergy 2, we should expect similar results:

 lkd> u nt!KiBBTUnexpectedRange+8  
 nt!KiBBTUnexpectedRange+0x8:  
 804ded5a 6ba0c1010bc058 imul  esp,dword ptr [eax-3FF4FE3Fh],58h  
 804ded61 5a       pop   edx  
 804ded62 8bec      mov   ebp,esp  
 804ded64 89ae34010000  mov   dword ptr [esi+134h],ebp  
 804ded6a 0f8490020000  je   nt!KiFastCallEntry+0x8d (804df000)  
 804ded70 8d15509b5580  lea   edx,[nt!KeServiceDescriptorTableShadow+0x10 (80559b50)]  
 804ded76 8b4a08     mov   ecx,dword ptr [edx+8]  
 804ded79 8b12      mov   edx,dword ptr [edx]  

So, why do we have corruptions in ntoskrnl and a corrupted nt!KiBBTUnexpectedRange+8 output? It's a side effect of the rootkit creating additional 'fake' service tables. It does this by patching the ETHREAD SystemTable pointer, which allows for things such as user threads to be patched, thread creation notification and service table pointer updating by using PsSetCreateThreadNotifyRoutine, etc.

The main use behind creating fake service tables is it gives anti-rootkit software a much harder time (harder back in 2010, at least) detecting its presence. It doesn't just 'hook' and/or modify the SSDT (which as we know would be a big red flag), it instead creates its own fake service tables, and then hooks (acquires?) the following functions:

 NtDeleteValueKey  
 NtEnumerateValueKey  
 NtEnumerateKey  
 NtOpenKey  
 NtOpenProcess  
 NtOpenThread  
 NtProtectVirtualMemory  
 NtQuerySystemInformation  
 NtReadVirtualMemory  
 NtSetContextThread  
 NtSetValueKey  
 NtSuspendThread  
 NtTerminateThread  
 NtWriteVirtualMemory
 etc...  

KTHREAD Structure
 
Given we're adding new/fake service tables, we need applications to be able to access them. This is done by using pointers as discussed above, which is accomplished in the KTHREAD Structure. Every single thread has a pointer to a ServiceTable which is ultimately set by KeInitThread. Additionally, if the thread requires GUI functions within the Shadow SSDT, PsConvertToGuiThread is called.

We can dump the KTHREAD Structure:

 lkd> dt -v nt!_KTHREAD  
 struct _KTHREAD, 73 elements, 0x1c0 bytes  
   +0x000 Header      : struct _DISPATCHER_HEADER, 6 elements, 0x10 bytes  
   +0x010 MutantListHead  : struct _LIST_ENTRY, 2 elements, 0x8 bytes  
   +0x018 InitialStack   : Ptr32 to Void  
   +0x01c StackLimit    : Ptr32 to Void  
   +0x020 Teb       : Ptr32 to Void  
   +0x0e0 ServiceTable   : Ptr32 to Void  

At this point if you'd like to see the tables, you can use the following command:

 !for_each_thread ".echo Thread: @#Thread; dt nt!_kthread ServiceTable @#Thread"   

If you see anything other than KeServiceDescriptorTable or KeServiceDescriptorTableShadow, it's a new/fake ServiceTable.

Registry Hiding

In order to survive reboots, etc, it hides its registry entry. If you're using Windows' Registry Editor, it won't find the hidden entry. For example, here's our hidden service:

 lkd> !ms_services  
 [205] | 0x01 | | qtcst | qtcst | SERVICE_RUNNING | \Driver\qtcst  

If we try and find qtcst with Registry Editor:


If we however use a 3rd party registry tool (any will probably work so long as it doesn't use Windows API calls):


We catch our culprit and the dropped driver red-handed. The driver renames after each reboot, so if you remove it and don't get the driver+registry entry at once, it'll just re-create with a different name.

Main.dll

 .exe SYS TMP cmd.exe /C b k e r n e l p l g _ d a t a getp v e r s i o n n a m e s l e e p f r e q c m d s p l u g i n s x%s_%X C:\ a d d r t y p e s e r v e r s i c m p _ a d d r b u i l d _ 
 i d str.sys \drivers\ \ \ . \ \ \ . \ G l o b a l \ %s%s { 9 D D 6 A F A 1 - 8 6 4 6 - 4 7 2 0 - 8 3 6 B - E D C B 1 0 8 5 8 6 4 A } main.dll .bdata {3D5A1694-CC2C-4ee7-A3D5-A879A9E3A623} 
 POST %.2X & = bid nt %d cn ln id ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ Content-Type: application/x-www-form-urlencoded _TEST_ .dll user32.dll advapi32.dll 
 wininet.dll ws2_32.dll DispatchCommand DispatchEvent GetLastError GetCurrentProcessId ExitThread CloseHandle KERNEL32.dll wsprintfA USER32.dll CoCreateInstance CoInitializeEx ole32.dll 
 OLEAUT32.dll WS2_32.dll RtlUnwind InterlockedExchange VirtualQuery main.dll ConfAllocGetTextByNameA ConfAllocGetTextByNameW ConfGetListNodeByName ConfGetNodeByName ConfGetNodeTextA 
 ConfGetNodeTextW ConfGetPlgNode ConfGetRootNode DownloadFile PlgSendEvent RkLoadKernelImage RkProtectObject SrvAddRequestBinaryData SrvAddRequestStringData  

Main.dll is the payload that is injected via trusted svchost. It contains as you can see a lot of readable stings, like str.sys for example. We can see str.sys in action here:


Overall, this rootkit was certainly a step up from most SSDT hooking/modification rootkits at the time. It can be a pain in the ass to remove if you don't kill everything properly : )

Thanks for reading.

References

Black Energy 2.1+
BlackEnergy Version 2 Analysis

Friday, October 10, 2014

Rustock.B Live Debugging - SwishDbgExt, SysecLabs script.

Here we are, part two! I thought rather than doing a live debugging of runtime2 as I discussed in my last rootkit debugging post, I'd debug a different rootkit. I chose Rustock.B (PE386) as it's a pretty notorious rootkit, and in my opinion is a lot of fun to debug. It's always a great learning experience to debug, reverse, and research things for yourself as well. I have a map of rootkits I want to debug and reverse as the weeks go by, so expect many more of these.

Let's get started!

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).



(thanks to MSIR for the image!)

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.



(thanks again to MSIR for the image!)

In addition to contacting a C&C server, the dropper 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

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.

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:


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:

 [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). 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:

 isolation.tools.setPtrLocation.disable = "TRUE"  
 isolation.tools.setVersion.disable = "TRUE"  
 isolation.tools.getVersion.disable = "TRUE"  // Thwarts backdoor I/O checks.
 monitor_control.disable_directexec = "TRUE"  // Thwarts descriptor table registers checks. VMware interprets each assembly instruction instead of the processor executing them.
 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:

 lkd> !ms_services  
 Implicit process is now 821ae9a0  
 Loading User Symbols  
 [209] | 0x01 | | pe386| Win23 lzx files loader | SERVICE_RUNNING | \Driver\pe386  

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:

 lkd> u poi(poi(iofcalldriver+2))  
 f6fb9dae 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.

 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:

 lkd> rdmsr 0x176  
 msr[176] = 00000000`806ccc3d  

The rdmsr command is used to view the state of a model-specific register (MSR).

 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.

 lkd> dc 806ccc3d  
 806ccc3d 8ee6c2e9 4c444e76 485f4445 5f445241 ....vNDLED_HARD_  
 806ccc4d 4f525245 000a0d52 1c000000 4e000000 ERROR..........N  
 806ccc5d 41505f4f 5f534547 49415641 4c42414c O_PAGES_AVAILABL  
 806ccc6d 000a0d45 18000000 50000000 4c5f4e46 E..........PFN_L  
 806ccc7d 5f545349 52524f43 0d545055 1c00000a IST_CORRUPT.....  
 806ccc8d 4e000000 5f534944 45544e49 4c414e52 ...NDIS_INTERNAL  
 806ccc9d 5252455f 0a0d524f 24000000 50000000 _ERROR.....$...P  
 806cccad 5f454741 4c554146 4e495f54 4e4f4e5f AGE_FAULT_IN_NON  

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:

 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       *msr[00000176] = -># HOOK #<- 00000000`806ccc3d nt!_NULL_IMPORT_DESCRIPTOR <PERF> (nt+0x1f5c3d) (806ccc3d) => Original : nt!KiFastCallEntry (804def6f)  

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:

 lkd> !ms_idt  
   |-----|-----|--------------------|--------------------------------------------------------|---------|--------|  
   | Cre | Idx | Address      | Name                          | Patched | Hooked |  
   |-----|-----|--------------------|--------------------------------------------------------|---------|--------|  
   |  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 | nt!_NULL_IMPORT_DESCRIPTOR (nt+0x1f5c38)      |     | Yes  |  

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:

 lkd> !idt 2e  
 Dumping IDT:  
 2e:     806ccc38 nt!_NULL_IMPORT_DESCRIPTOR <PERF> (nt+0x1f5c38)  

On a healthy x86 system, if you go ahead and dump the IDT, the only thing that should show is nt!KiSystemService. For example:

 kd> !idt 2e  
 Dumping IDT: 823f7400  
 18b78dea0000002e:     8185c77e nt!KiSystemService  

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

 lkd> u poi(poi(iofcalldriver+2))  
 nt!IopfCallDriver:  
 804e3d50 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  

 lkd> !address 804e3d50   
  804d7000 - 00215000                
      Usage    KernelSpaceUsageImage  
      ImageName  ntoskrnl.exe  

SYSENTER Hook - Manual

 lkd> rdmsr 0x176  
 msr[176] = 00000000`804def6f  

 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

 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       *msr[00000176] = 00000000`804def6f nt!KiFastCallEntry (804def6f)  

IDT Hook

 lkd> !ms_idt  
   |-----|-----|--------------------|--------------------------------------------------------|---------|--------|  
   | Cre | Idx | Address      | Name                          | Patched | Hooked |  
   |-----|-----|--------------------|--------------------------------------------------------|---------|--------|  
   |  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 | nt!KiSystemService               |     | No   |  

INT 2Eh Hook

 lkd> !idt 2e  
 Dumping IDT:  
 2e:     804deea6 nt!KiSystemService  

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).

Tuesday, September 30, 2014

Registers (x86)

As I discussed towards the end of my last post regarding stacks, my next post was likely going to be about registers. Well, here we are! I had originally planned on discussing both x86 and x64 registers in a single post, but this posed two main problems. The first problem was this would have been a very long post! The second problem, which is a considerably larger one, is I don't know x64 assembly/architecture as much as I'd like to feel confident in making a post about it. The good news is whenever I am brushed up enough in regards to x64's architecture to write a detailed post, I can simply jump right in as I did all the dirty work right here in this post! Happy days.

Memory Hierarchy

First off, it's important to discuss and understand what a register is. Before we get into that however, let's have a look at my favorite image of the memory hierarchy:


(thanks to COMPUTER SCIENCE E-1 for this great image)

It's safe to say there are probably thousands of images regarding the memory hierarchy throughout CS books, documents, and presentations, but this one takes the cake for me! It's about as good as it visually gets for a hierarchy image, and although it doesn't display a few key points, I can do that myself right here in this post.

What are the key points I'm talking about that are missing from this image? Well, from the bottom>top, we're going from slowest to fastest. If we're coming from the top>bottom, we're going from the fastest to the slowest (in regards to read and write access time). It's absolutely imperative to also understand that the faster we get, the more expensive we get, and the slower we get, the less expensive we get (in regards to USD). With that now known, you can imagine that the read/write from a removal media device (such as USB) is slower than the read/write from your hard drive, but is less expensive.

As this is a post strictly about registers, I won't go into the complexities and intricacies of each part of the hierarchy, and will instead focus on the registers themselves. As far as access time goes, let's compare registers and the hard drive as an example:

Registers - 1-2ns (nanoseconds)

Hard Drive - 5-20ms (milliseconds)

-- It's all dependent on the architecture of the processor, really. These are rough #'s.

Cue the amazing Grace Hopper!


Why are registers so fast? Registers are actually circuits which are built/wired (literally) into the Arithmetic logic unit (ALU), which is also widely considered the fundamental building block of a CPU. With that said, we really can't get any closer, which means there's also no data transfer overhead as there are barely any clock cycles required. Also, a CPU's instruction set tends to work with registers more than it does with actual memory locations.

Speaking of clock cycles, here's a chart displaying the cycles regarding the memory hierarchy:


(thanks to HLNAND for this great image)

As we can see, a register only takes one clock cycle.

What is a register?
 
Now that we understand the basic fundamentals behind the memory hierarchy and where the register resides on the hierarchy, we can discuss what a register actually is! In its most basic definition, a register is used to store small pieces of data that the processor is actively working on. There are many different registers and categories of registers, all of which essentially do something different, however you can generally break registers down into two types. For example, regarding the first type, we have the General purpose register (GPR), which essentially stores data and performs arithmetic based on an instruction (addition, subtraction, multiplication, etc). Once the arithmetic is finished (or the manipulation of data/memory is finished), it's entirely up to what the instruction is set to do. It can either store it back into memory with the same instruction, a different one, etc.

Regarding the second type, we have the Special purpose register (SPR) which as the name implies has a special meaning and specific purpose. For example, the SP (Stack Pointer) register is a SPR in addition to being a GPR regarding the IA-32 architecture. This register is used to have the CPU store the address of the last program request in a stack. Among other things, as new requests are coming in, they push down the older ones in the stack, with the most recent request always residing at the top of the stack.

It's important to note that at every clock tick, there are specific values regarding registers. The values stored in a specific register may have been updated on a tick, so the values may not be the same as they were prior to the tick. For example, when an interrupt fires, register values are copied to a stack and stay on that stack while an Interrupt Service Routine (ISR) is being executed by the CPU. Once the interrupt is properly handled, the original register values are loaded back from the stack so they can continue to service the instruction they were previously working with.

What I described above is known as context switching, which is essentially the jump of instructions from CPU > ISR. Although unrelated yet interesting to note, in some special cases regarding 0x101 bug checks depending on what actually caused the bug check, you may need to have knowledge of context switching to properly debug.

With all of the above said, there's about a dozen different ways I could go at this point. I could go on to talk about the register file, the many different and various categories of registers, etc. However, let's jump ahead to register renaming as that's a pretty important topic.

Register Renaming

Register renaming is essentially a form of pipelining that deals with data dependencies between instructions by renaming their register operands. The way renaming works is, it will go ahead and replace the architectural register (user-accessible registers, or more easily just known to us as 'the registers') names (value names) with a new value name for each instruction destination operand.

Thanks to register renaming, we can also successfully perform what is known as Out-of-order execution (OOE). How exactly does it allow OOE to be performed? Register renaming entirely eliminates name dependencies between instructions, and recognizes true dependencies. True dependencies occur when an instruction depends on the result of a subsequent instruction.

Given the above is now explained, here's a good time to explain data hazards. Data hazards occur when instructions that exhibit data dependence modify data in different stages of a pipeline. Ignoring a data hazard can lead to what is known as a race condition, which is when the order of the data that was outputted was not the intended order. We have three main data hazards:

  • Read-after-write (RAW), also known as a true dependency.
  • Write-after-read (WAR), also known as an anti-dependency.
  • Write-after-write (WAW), also known as an output dependency.

1. What is RAW? Let's take two instruction locations (l1, l2). A prime RAW example is when l2 tries to read a source before l1 writes to it. l2 is attempting to refer to a result that hasn't been calculated or retrieved yet by l1.

2. What is WAR? Let's once again take l1 & l2. l2 tries to write a destination before it is read by l1. This is a problem in concurrent execution, which notes of course they must work concurrently and not sequentially. If they do work sequentially, then we have a data hazard like so.

3. What is WAW? Taking l1 & l2 one last time, l2 tries to write an operand before it is written by l1.

With register renaming, since we're ultimately maintaining a status bit for each value that indicates whether or not it has been completed, it allows the execution of two instruction operations to be performed out of order when there are no true data dependencies between them. This removes WAR/WAW, and of course leaves RAW intact as discussed above.


(Excerpt from the following .pdf)

x86 Registers

In WinDbg, by using the r command we can go ahead and dump the registers from the context of the thread that caused the crash. For example:

 kd> r  
 eax=818f4920 ebx=86664d90 ecx=818fb9c0 edx=000002d0 esi=818f493c edi=00000000  
 eip=818c07dd esp=a1e72cb0 ebp=a1e72ccc iopl=0     nv up ei pl nz na pe nc  
 cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000       efl=00000206  

x86 (IA-32) has eight GPRs, which are:

  • EAX
  • EBX
  • ECX
  • EDX
  • ESI
  • EDI
  • EBP
  • ESP

Great, so there's our eight GPRs. Now we can go ahead and break them down:

  • EAX - The 'A' in the EAX register implies it's the accumulator register for operands and results data.
  • EBX - The 'B' in the EBX register implies it's the pointer to the data in the DS segment. DS is the current data segment. It also means 'base register'.
  • ECX - The 'C' in the ECX register implies it's the counter for storing loop and string operations.
  • EDX - The 'D' in the EDX register implies it's the I/O pointer.
  • ESI - The 'SI' in the ESI register implies it's the Source Index, which is a pointer to data in the segment pointed to by the DS register.
  • EDI - The 'DI' in the EDI register implies it's the Destination Index, which is a pointer to data (or a destination) in the segment pointed to by the ES register. It's essentially the counterpart to the ESI register, for lack of a better word.
  • EBP - The 'BP' in the EBP register implies it's the Base Pointer, which is the pointer to data on the stack.
  • ESP - The 'SP' in the ESP register implies it's the Stack Pointer, which is used to detect the location of the last item put on the stack.

Whew, alright! So there's just a few things I'd like to quickly explain as well as we haven't covered all of the bases:

  • EAX - The 'AX' in the EAX register is used to address only the lower 16 bits of the register. If we were to reference all 32 bits, we'd use all of EAX, and not just AX.
  • EIP - I didn't forget this guy, don't worry! The 'IP' in the EIP register implies it's the Instruction Pointer, which can also actually be called the 'program counter'. It contains the offset in the current code segment for the next instruction that will be executed. It's also interesting to note that EIP cannot be accessed by software, and is explicitly controlled by control-transfer instructions such as JMP, CALL, JC, and RET. Aside from control-transfer instructions, interrupts and exceptions can also access EIP directly.

Okay, so I can't mention control-transfer instructions and then not explain them. I mean, I could... but I wouldn't be happy. Control-transfer instructions specifically control the flow of program execution. There are quite a few control-transfer instructions, but I will discuss the ones I mentioned that can directly access EIP:

  • JMP - Jump. JMP transfers program control to a different point in the instruction stream without recording any return information. The destination operand specifies the address of the instruction being jumped to. This operand can be an immediate value, a GPR, or a memory location. The JMP instruction can be used to actually execute four different types of jumps:
1. Near jump - A jump to an instruction within the segment currently pointed to by the CS register. It can also at times be referred to as an intrasegment jump.

2. Short jump - A type of near jump in that the jump range is limited to -128 to +127 from the current EIP value.

3. Far jump - A jump to an instruction that's located in a different segment than the current code segment, but at the same privilege level. It can also at times be referred to as an intersegment jump.

4. Task switch - A jump to an instruction that's located in a different task. Note that a task switch can only be accomplished in protected-mode, which not to fly too off the handle here, but it's necessary to explain. Rather than explaining it here though as it's a bit large, I'll explain it below in a short while.
  • CALL - Call procedure. CALL pushes the current code location onto the hardware supported stack in memory, and then performs an unconditional jump to the code location indicated by the label operand. Unlike the simple jump instructions I listed above, the call instruction saves the location to return to when the subroutine completes. 
  • JC - Jump if carry flag is set. 
  • Ret - Return. This instruction transfers control to the return address located on the stack, which is usually placed on the stack by a call instruction that we discussed above. It then performs an unconditional jump to the retrieved code location. For example:

 call <label>  
 ret  
I mentioned intersegment/intrasegment jumps above. Intersegment jumps can transfer control to a statement in a different code segment, while intrasegment jumps are always between statements in the same code segment.

Now that we have the above control-transfer instructions explained, let's discuss protected-mode as I mentioned above. Before further discussing protected-mode and the similarly named but very different real-mode, we'll need to do a bit of a history lesson.

Way before my time, back in the late 70's (76-78), Intel's 16-bit 8086 processor was released. It was 16-bit because its internal registers + internal/external data buses were 16 bits wide. A 20-bit external address bus meant this beast could address a whopping 1 MB's of memory! 1 MB may not seem like anything these days, but it was actually considered more than overkill around this time. Due to this being the case, the max linear address space was limited to a mere 64 KB. Aside from 1 MB being overkill, this was also because the internal registers were only 16 bits wide.

There were two problems here:

1. Programming over 64 KB boundaries meant adjusting segment registers.

2. As time went on, applications were being released that made this mere 64 KB seem like the measly number it is today.

What was the saving grace? Intel's 80286 processor was released in 1982! Well, what's so great about this processor that solve the above two problems? The 80286 processor had two operating modes, as opposed to the 8086 which only had one. The operating modes were:

1. Real-Mode (backwards compatible 8086 mode).

2. Protected-Mode.

The 80286 processor had a 24-bit address bus, which could address up to 16 MB! That's 15 more MB than its predecessor. There's too much good stuff here, so let's discuss the problems. The problems were certainly problems, and they were considerably big ones:

- DOS apps couldn't easily be ported to protected-mode granted that most/if not all DOS apps were developed in a way that made them incompatible.

- The 80286 processor couldn't successfully revert back to the backwards compatible real-mode without a CPU reset. Later on however in 1984, IBM added circuitry that allowed a special series of instructions to successfully cause a revert without initiating a CPU reset. This method while certainly being a great feat, posed quite the performance penalty. Later on it was discovered that initiating a triple fault was a much faster and cleaner way, but there was still yet to be a 'painless' method of transition.

With the above said, the successor was released which is the 80386. The 80386 had a 32-bit address bus, which allowed for 4 GB of memory access. 1 MB > 16 MB > 4 GB, quite the increase! In addition, the segment size was increased to 32-bits, which meant there wasn't a need to switch between multiple segments to access the full address space of 4 GB.

Whew! With all of this known, what is protected-mode actually so great for at this point? Protected-mode allows for virtual memory, paging, ability to finally painlessly switch back to real-mode without a CPU reset, etc.

How do we actually get into protected-mode? The Global Descriptor Table (GDT) needs to be created with a minimum of three entries. These three entries are a null descriptor, a code segment descriptor, and a data segment descriptor. Afterwards, the PE bit needs to be set in the CR0 register and a far JMP needs to be made to clear the prefect input queue (PIQ).

 // Set PE bit  
 mov eax, cr0  
 or eax, 1  
 mov cr0, eax  
 // Far JMP (Remember CS = Code Segment)  
 jmp cs:@pm  
 @pm:  
 // Now in protected-mode :)  

So we got to most of the registers from the x86 register dump excerpt, but we're missing these ones:


 nv up ei pl nz na pe nc  

These are the current contents of the FLAGS register (there are 20[?] different flags), which is the status register for x86.

  • nv - No overflow.
  • up - Up.
  • ei - Enable interrupt.
  • pl - Plus (I believe).
  • nz - Not zero.
  • na - Not auxiliary carry.
  • pe - Parity even.
  • nc - No carry.

Disassembly Example

Let's now dump a stack from a random x86 crash dump to show an example of some of the registers we talked about, some assembly code, and instructions:

 ChildEBP RetAddr   
 a1e72ccc 81a8eec4 nt!KeBugCheckEx+0x1e  
 a1e72cf0 81a1e85f nt!PspCatchCriticalBreak+0x73  
 a1e72d20 81a1e806 nt!PspTerminateAllThreads+0x2c  
 a1e72d54 8185c986 nt!NtTerminateProcess+0x1c1  
 a1e72d54 77725d14 nt!KiSystemServicePostCall  
 0028f0d0 00000000 0x77725d14  

Let's go ahead and disassemble the nt!PspTerminateAllThreads+0x2c kernel function:

 nt!PspTerminateAllThreads+0x2c:  
 81a1e85f 8b450c     mov   eax,dword ptr [ebp+0Ch]  // Move value stored at memory address in the ebp+0Ch register to the eax register.
 81a1e862 8b4048     mov   eax,dword ptr [eax+48h]  // Move value stored at memory address in the eax+48h register to the eax register.
 81a1e865 8945f0     mov   dword ptr [ebp-10h],eax  // Move contents of eax register to the ebp-10h register.
 81a1e868 6a00      push  0  // Push a 32-bit zero on the stack.
 81a1e86a 8bc7      mov   eax,edi  // Move contents of the edi register into the eax register.
 81a1e86c c745fc22010000 mov   dword ptr [ebp-4],122h  // Store the 32-bit value 122h to the ebp-4 register.
 81a1e873 e8bf010000   call  nt!PspGetPreviousProcessThread (81a1ea37) // Call function name nt!PspGetPreviousProcessThread.
 81a1e878 8b5d14     mov   ebx,dword ptr [ebp+14h]  // Moving value stored at memory address in the ebp+14h register to the ebx register.

Hope you enjoyed reading!

References

Intel 80386 Reference Programmer's Manual
X86 Assembly/High-Level Languages
A Guide to Programming Intel IA32 PC Architecture
Segment Registers: Real mode vs. Protected mode

Saturday, September 20, 2014

Stacks, stacks, and more stacks!

If you've ever debugged a crash dump before, user-mode or kernel, you've very likely seen a stack. If you debug crash dumps quite often, you've seen and traversed thousands of stacks, maybe more! For a long time I knew the basics behind a stack and what I was looking at/through for the most part, but really beyond that I didn't know much for quite awhile until I dug deep into dozens of documents and books as time went on. With that said, let's discuss stacks and their mechanics!

The stack excerpt below is from an x86 0xF4 crash dump. The cause of the bug check and such is irrelevant, and this stack is purely for explanatory purposes. With that said, turn off your tingly BSOD senses for a moment and look at the functions and such for what they are, and not what they could have done to play a role in the crash : )

 kd> k  
 ChildEBP RetAddr   
 a1e72ccc 81a8eec4 nt!KeBugCheckEx+0x1e  
 a1e72cf0 81a1e85f nt!PspCatchCriticalBreak+0x73  
 a1e72d20 81a1e806 nt!PspTerminateAllThreads+0x2c  
 a1e72d54 8185c986 nt!NtTerminateProcess+0x1c1  
 a1e72d54 77725d14 nt!KiSystemServicePostCall  
 0028f0d0 00000000 0x77725d14  

k* will perform a stack unwind, or more specifically will display the stack backtrace. * denotes the placeholder for the various possible parameters that we can use, such as - kb, kv, kc, kd, the list goes on. Each of those all does something different, but for now we'll stick to plain old k for example purposes.

Let's break this down one at a time!

ChildEBP

ChildEBP - First we have our ChildEBP, otherwise known as the 'base pointer', which is essentially nothing more than a pointer to a stack frame that has been set up by a piece of code (a reference to a stack of memory). As an example, let's take a look at the following command:

 dd a1e72cf0 l1  

dd - This is a variation of plain old d, which simply means dump. If we however use dd, this will display/dump dword(s).

a1e72cf0 - This is a pointer address from our stack, specifically from nt!PspCatchCriticalBreak+0x73.

L1 - I've always been under the impression that this tells WinDbg to print a line based on what we're looking to do as far as the command goes. I've searched the WinDbg help doc far and wide for a conclusive answer, but I haven't found one yet. There are billions of people out there far smarter than I, and hopefully one of them reads my blog to tell me what it really does!

EDIT: Hilariously enough, someone smarter than me did happen to come along. Thanks to Blair Foster, I now understand what L1 really does.
its a size/range specifier. In the case of d* its used as size. So, dd L1 displays 1 dword, db L1 is 1 byte, etc.
Thanks, Blair!

If we go ahead and run this command, here's what we get:

 kd> dd a1e72cf0 l1  
 a1e72cf0 a1e72d20  

Look familiar? a1e72d20 is another pointer address from our stack, but more specifically the pointer from the previous function in the stack - nt!PspTerminateAllThreads+0x2c. With this known, we can conclude that the ChildEBP also stores the previous function address.

 a1e72d20 81a1e806 nt!PspTerminateAllThreads+0x2c  

In this specific example, we don't have a good stack to show us what would happen if we went all the way back to what function spawned/began the stack. Taking a quick look through some x64 dumps (didn't have any good x86 ones), I dug up a quick stack. Try not to get overwhelmed with the differences between x86 and x64 as far as stack goes (even though there aren't really any). It's just more bits and bobs!

 3: kd> k  
 Child-SP          RetAddr             
 fffff880`033dfa58 fffff800`0302da3b nt!KeBugCheckEx  
 fffff880`033dfa60 fffff800`031f0463 hal!HalBugCheckSystem+0x1e3  
 fffff880`033dfaa0 fffff800`0302d700 nt!WheaReportHwError+0x263  
 fffff880`033dfb00 fffff800`0302d052 hal!HalpMcaReportError+0x4c  
 fffff880`033dfc50 fffff800`0302cf0d hal!HalpMceHandler+0x9e  
 fffff880`033dfc90 fffff800`03020e88 hal!HalpMceHandlerWithRendezvous+0x55  
 fffff880`033dfcc0 fffff800`030d84ac hal!HalHandleMcheck+0x40  
 fffff880`033dfcf0 fffff800`030d8313 nt!KxMcheckAbort+0x6c  
 fffff880`033dfe30 fffff800`030d07b8 nt!KiMcheckAbort+0x153  
 fffff880`09e9f638 00000000`00000000 nt!memcpy+0x208  

Just by looking at the stack we can see it begins right at nt!memcpy+0x208, but let's show it the way we've been demonstrating:

 3: kd> dd fffff880`033dfe30 l1  
 fffff880`033dfe30 00000000  

We can see at address fffff880`033dfe30, the return value was zero. This tells us it wasn't called from another prior function, etc, and is the very beginning of the stack. Given the function itself is nt!memcpy, this is understandable.

RetAddr

RetAddr - Now that we understand our base pointer, we have our RetAddr, which implies return address. This is done because quite simply the CPU without being told what to do is pretty useless. We need to let it know what address to return to after it's done successfully executing the code, because all it's concerned about is instructions and executing their code. It has no idea how to handle them without this stuff set in place, or what to do after it's done. With that said, this is why we have our return address.

If we take another look at our x64 stack excerpt:

 3: kd> k  
 Child-SP          RetAddr             
 fffff880`033dfa58 fffff800`0302da3b nt!KeBugCheckEx  
 fffff880`033dfa60 fffff800`031f0463 hal!HalBugCheckSystem+0x1e3  
 fffff880`033dfaa0 fffff800`0302d700 nt!WheaReportHwError+0x263  
 fffff880`033dfb00 fffff800`0302d052 hal!HalpMcaReportError+0x4c  
 fffff880`033dfc50 fffff800`0302cf0d hal!HalpMceHandler+0x9e  
 fffff880`033dfc90 fffff800`03020e88 hal!HalpMceHandlerWithRendezvous+0x55  
 fffff880`033dfcc0 fffff800`030d84ac hal!HalHandleMcheck+0x40  
 fffff880`033dfcf0 fffff800`030d8313 nt!KxMcheckAbort+0x6c  
 fffff880`033dfe30 fffff800`030d07b8 nt!KiMcheckAbort+0x153  
 fffff880`09e9f638 00000000`00000000 nt!memcpy+0x208  

According to the stack, HalpMceHandlerWithRendezvous was called by HalHandleMcheck. We know that when the work HalpMceHandlerWithRendezvous was doing was finished, it would return back to HalHandleMcheck. We can confirm this by taking a look at the return memory addresses and using the ln command:

 3: kd> ln fffff800`03020e88  
 (fffff800`03020e48)  hal!HalHandleMcheck+0x40  

ln - This command will display the function/routine at or near the given address.

Bingo! Execution will resume afterwards in HalHandleMcheck, specifically 64 bytes from the start of the function.

Functions/Call Site

Functions/Call Site - Last but not least, we have our third column (it's not labeled above in any of the stack excerpts). Interestingly enough, x64 stacks do label their function columns, specifically as Call Site. In the above stack x64 excerpts however, I removed them to avoid confusion! : )

Here is what a non-edited x64 stack looks like:

 3: kd> k  
 Child-SP          RetAddr           Call Site  
 fffff880`033dfa58 fffff800`0302da3b nt!KeBugCheckEx  
 fffff880`033dfa60 fffff800`031f0463 hal!HalBugCheckSystem+0x1e3  
 fffff880`033dfaa0 fffff800`0302d700 nt!WheaReportHwError+0x263  
 fffff880`033dfb00 fffff800`0302d052 hal!HalpMcaReportError+0x4c  
 fffff880`033dfc50 fffff800`0302cf0d hal!HalpMceHandler+0x9e  
 fffff880`033dfc90 fffff800`03020e88 hal!HalpMceHandlerWithRendezvous+0x55  
 fffff880`033dfcc0 fffff800`030d84ac hal!HalHandleMcheck+0x40  
 fffff880`033dfcf0 fffff800`030d8313 nt!KxMcheckAbort+0x6c  
 fffff880`033dfe30 fffff800`030d07b8 nt!KiMcheckAbort+0x153  
 fffff880`09e9f638 00000000`00000000 nt!memcpy+0x208  

As we can see, there's Call Site! In x86 crash dumps however, it's not labeled. I don't need to paste the stack excerpt we used from above as I didn't edit it, so you can just scroll back up and take a quick look.

It should be no mystery that this third and final column displays the function (and routine) names throughout the stack. Do note that they are in direct correlation with the current loaded symbols. If you don't have proper symbols, you're going to get an unresolved function (or routine), and it's going to look like junk.

--------------------

Thanks for reading! In my next post I plan on going into the x86 registers (maybe x64 although it'll be a lot of work -- may save that for a future post).