A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #1928  by Dreg
 Fri Aug 13, 2010 2:49 pm
What about SwapContext Hook trick? (I am developing a security tool POC based on this stuff and works fine, look the coments of the post for the last release):

Detecting Hidden Processes by Hooking the SwapContext Function
By: kimmo

http://www.rootkit.com/newsread.php?newsid=170

We have the tool Klister created by Joanna Rutkowska which can detect hidden processes by examining the contents of the following three linked lists maintained by the kernel: KiWaitInListHead, KiWaitOutListHead and KiDispatcherReadyListHead[32]. However, Klister only works with Windows 2000 and the porting of the code for Windows XP/2003 is not trivial. The problem is that the scheduler has changed between these versions and all the necessary lists are not present. For example, Windows XP/2003 have only two lists: KiWaitListHead and KiDispatcherReadyListHead[32], and they do not contain all the threads present in the system. I spent quite a lot of time trying to figure out how to circumvent this without any success. So I decided to go into other direction.

Jamie Butler mentioned in his presentation about Direct Kernel Object Manipulation (DKOM) at Black Hat Europe 2004 that in theory one way to detect hidden processes is to hook the SwapContext function in ntoskrnl.exe. This function does context switching between threads. The pointers to the _KTHREAD structures of the two threads are passed in ESI and EDI registers. If we hook this function we can get every thread that is executed in the system. I decided to try if this solution was feasible. I made a prototype driver that hooks the SwapContext function using the Detours method and collects the thread ID, process ID and the image filename. The source code is in the swapcontext_hook.zip file which is available from my vault. I have only created the driver which will print the collected data through the DbgPrint function, so you have to install the driver manually (I use INSTDRV.EXE available from Hoglund's vault) and attach a debugger or use the DbgView by Sysinternals to catch the output. I have tested the driver with Windows XP SP1/SP1A and Windows 2003 Server, and it has been very stable and the performance has been good. However, since we are reading data directly from internal kernel structures it is possible that this code will bluescreen your machine, so be carefull.

The code should be quite well documented so if you want details you had better read through it. Here is the basic idea how the driver works:

At first the driver has to know the address of the SwapContext function. This is accomplished by scanning some well known memory locations for a signature which consists of the first 20 bytes of the function. If the signature is not found it will scan the whole address space of the ntoskrnl module. This is performed by the FindSwapContextAddress function.

When we have the address of the SwapContext function we will hook it. We use the detours method which is explained in detail by Hunt and Brubacher in their paper "Detours: Binary Interception of Win32 Functions". This is done by the InstallSwapContextHook function. The generic idea is to batch at least the first five bytes of the hooked function with a JMP rel32 instruction which will direct the flow of control to our detour function. How many bytes we have to batch depends on the length of the first few instructions. Since the instructions and their length are different between XP and 2003 I have to do the batching dynamically. For example in XP we have to replace the first seven bytes and in 2003 the first six bytes. I use the XDE v1.01 disassembler created by Z0MBie to get the length of the instructions. The first five bytes consists of the JMP rel32 instruction and the rest are replaced with NOPs. InstallSwapContextHook function also creates the trampoline which contains the instructions we have replaced and a jump to the rest of the original SwapContext function. My detour function looks like this:

void __declspec(naked) DetourFunction()
{
__asm {
// Save parameters we will overwrite.
pushad
pushfd
// Disable interrupts. Assume single processor machine.
cli
// EDI holds the thread whose context we will switch out.
push edi
call ProcessData
// ESI holds the thread whose context we will switch in.
push esi
call ProcessData
// Enable interrupts.
sti
// Restore the saved state.
popfd
popad

// Jump to the trampoline function.
jmp dword ptr pTrampoline
}
}

ProcessData is the function that gets the required data from the _KTHREAD, _ETHREAD and _EPROCESS structures and stores the data in a separate chaining hash table. I am using the threads virtual memory address as the key to the hash table (first I used the thread ID, however in theory one could modify a malicious thread to have the same ID as some non-malicious thread) and a thread is only inserted once during its lifetime. Because I use the threads memory address as the key I have to make sure that the entry is removed from the table when the thread is terminated since a new thread can be allocated to the same memory address. When a thread is terminating it signals this by setting the Terminated flag in the CrossThreadFlags entry which is part of the _ETHREAD structure. The ProcessData function looks like this:

void __stdcall ProcessData(DWORD *pEthread)
{
// NOTICE: WinDbg gives offsets in BYTEs, we use DWORDS
DWORD *pEprocess = (DWORD *)*(pEthread + offsets.threadsProcess);
DWORD *pCid = (DWORD *)(pEthread+offsets.CID);
DWORD key;
DATA data;

// FIXME: A thread could be hidden by setting threadsProcess or CID
// field as NULL!
if (pEprocess != NULL && pCid != NULL)
{
key = (DWORD)pEthread;
data.processID = *pCid;
data.threadID = *(pCid + 0x1);
data.imageName = (BYTE *)(pEprocess+offsets.imageFilename);

// The thread is terminated so remove it from the
// hashtable.
if (*(pEthread + offsets.crossThreadFlags) & 1)
{
Remove(key, pHashTable);
}
else
{
Insert(key, &data, pHashTable);
}
}
}

The hash table is stored in the nonpaged pool since we are operating at IRQL = DISPATCH_LEVEL.

And finally when the driver is stopped it will remove the hook, dump the contents of the hash table through the DbgPrint call and release any used resources. So this is it, quite simple :).

I have performed minor testing and this method is able to find all processes hidden by any of the current rootkits and the performance impact is not noticeable on normal desktop usage. Since I remove the threads from the hash table when they are terminated this driver will only display those processes that still have active threads. However, if we do not remove them and use unique keys we will get full trace of every process that has been run on the system.

Is there a way to circumvent this method? Yes, you can always batch the kernel, remove the hook etc.

-Kimmo
 #1929  by Alex
 Fri Aug 13, 2010 3:29 pm
Hooking SwapContext and other kernel functions is really bad idea in my opinion. Hooking is dangerous and can be defeated easily (Invisible Process 1.0). There are other and better methods like thread scheduler lists analysis or pool scanning...
 #1930  by EP_X0FF
 Fri Aug 13, 2010 3:29 pm
The given example cannot detect phide_ex.
Also if you interested in KiSwapContext hook bypassing you can look for PHIDE2 source.
You should avoid dangerous and unstable hooking at all cost.
 #1934  by Dreg
 Fri Aug 13, 2010 4:32 pm
Yes, I am interesting in the KiSwapContext hook bypassing, My POC needs check an EPROCESS field to evade a new kind of attack, well then, what is the best way to check an EPROCESS field and DENY or ALLOW the execution? Are there any solution better than KiSwapContext hook?

Sincerely, Dreg.
 #1936  by Dreg
 Fri Aug 13, 2010 5:04 pm
The scenario is:

1) While the process is runing, it allocs a LDT, well this LDT is valid and not evil.
2) The process exploits a driver bug for example, modifying the LDT entry of the EPROCESS.
3) The process force the switching to force the new GDT[LDT_HARDCODE_VALUE_WIN32] evil.

My POCs it is easy, it hooks the switching and it checks the LDT entry, if the LDT entry is evil (call gates inside, or LDT forward to user space attack), the POCs DENY the execution.

Then, are there a best way than the mine to do this?

Thx for your response EP_X0FF :-)

Sincerely, Dreg.
 #1938  by EP_X0FF
 Fri Aug 13, 2010 5:29 pm
Sure, this is interesting :)
I was thinking you speaking about monitoring of processes startup.
 #1943  by EP_X0FF
 Fri Aug 13, 2010 5:45 pm
Dedicated thread created :)