Log in

View Full Version : SEH Internals - Info Requested


Clandestiny
June 8th, 2002, 03:39
Hiya,

I am currently writing an tutorial on SEH as it relates to reversing and was curious about methods of circumventing SEH based protection tricks. I realize that protectors often deliberately invoke a SEH handler in response to tracing or sometimes by injecting an invalid opcode into the instruction stream. Hooking the SetThreadContext / GetThreadContext APIs might suffice in some cases to prevent manipulation of the machine state via the CONTEXT structure, but many protectors are smart enough to build and manipulate the SEH frame directly on the stack, rendering them impervious to these type of hooks. Being that SEH is intrinsically an OS provided service, it would seem that the way to defeat these types of tricks might be to hook into the Kernel's internal exception handler functions. "A Crash Course on the Depths of Structured Exception Handling" by Matt Pietrek provides some intersting information on the OS's SEH internals as it relates to WinNT:

************************************************
KiUserExceptionDispatcher is in NTDLL.DLL and is where execution begins after an exception occurs. To be 100 percent accurate, what I just said isn't exactly true. For instance, in the Intel architecture an exception causes control to vector to a ring 0 (kernel mode) handler. The handler is defined by the interrupt descriptor table entry that corresponds to an exception. I'm going to skip all that kernel mode code and pretend that the CPU goes straight to KiUserExceptionDispatcher upon an exception
The heart of KiUserExceptionDispatcher is its call to RtlDispatchException. This kicks off the search for any registered exception handlers. If a handler handles the exception and continues execution, the call to RtlDispatchException never returns. If RtlDispatchException returns, there are two possible paths: either NtContinue is called, which lets the process continues, or another exception is raised. This time, the exception isn't continuable, and the process must terminate.
Moving on to the RtlDispatchExceptionCode, this is where you'll find the exception frame walking code that I've referred to throughout this article. The function grabs a pointer to the linked list of EXCEPTION_REGISTRATIONs and iterates over every node, looking for a handler. Because of the possibility of stack corruption, the routine is very paranoid. Before calling the handler specified in each EXCEPTION_REGISTRATION, the code ensures that the EXCEPTION_REGISTRATION is DWORD-aligned, within the thread's stack, and higher on the stack than the previous EXCEPTION_REGISTRATION.
RtlDispatchException doesn't directly call the address specified in the EXCEPTION_REGISTRATION structure. Instead, it calls RtlpExecuteHandlerForException to do the dirty work. Depending on what happens inside RtlpExecuteHandlerForException, RtlDispatchException either continues walking the exception frames or raises another exception. This secondary exception indicates that something went wrong inside the exception callback and that execution can't continue.
The code for RtlpExecuteHandlerForException is closely related to another function, RtlpExecutehandlerForUnwind. You may recall that I mentioned this function earlier when I described unwinding. Both of these "functions" simply load the EDX register with different values before sending control to the ExecuteHandler function. Put another way, RtlpExecuteHandlerForException and RtlpExecutehandlerForUnwind are separate front ends to a common function, ExecuteHandler.
ExecuteHandler is where the handler field from the EXCEPTION_REGISTRATION is extracted and called. Strange as it may seem, the call to the exception callback is itself wrapped by a structured exception handler. Using SEH within itself seems a bit funky but it makes sense if you ponder it for a moment. If an exception callback causes another exception, the operating system needs to know about it. Depending on whether the exception occurred during the initial callback or during the unwind callback, ExecuteHandler returns either DISPOSITION_NESTED_ EXCEPTION or DISPOSITION_COLLIDED_UNWIND. Both are basically "Red Alert! Shut everything down now!" kind of codes.
**************************************************

From this it seems as though it might be possible to hook into the KiUserExceptionDispatcher to intercept exceptions for the process you are interested and retrieve the current CONTEXT structure before the app gets a chance to play dirty with the debug registers and such. I have done several searches regarding the possiblity of hooking into the Kernel Exception Handler and came back with mere breadcrumbs and little definitive information about the feasiblity / validity of this type of approach. I am likewise, interested in the internal implementation of the SEH mechanism in Win9X as the above doc by Pietrek seems only to apply to WinNT. So far, I have found next to nothing regarding SEH internals for Win9X. The Win9X NTDLL.dll appears not to export many of the Ki / Rtl Exception functions mentioned above.

Though I know it is possible for an app to replace the "default" handler, it would only be invoked if no other handlers in the chain agree to handle the exception. I guess I'm curious if it is possible be notified immediately after an exception occurs, *before* any of the registered handlers have a chance at it.

Is hooking into the Kernel SEH functions a valid approach to this type of problem? Or is there a better approach to generically protecting against these type of SEH tricks (ie both ring 3 and ring 0)? Indeed, I'm pretty much a newbie to SEH so I will be grateful for any insight.

Thanks,
Clandestiny

crUsAdEr
June 8th, 2002, 11:45
Hi Clandestiny,

I dont know much about internal working of seh except what you probably have known... fox3 might know somestuff...

I was just thinking of protection against such seh clearing bpm stuff... hooking API is one options, but you can always do it the way icedump protect IDT, by making fs:0 read only and hence notify us if any program is attempting to install its seh for this purpose...

Also, have you tried logging the instruction flow inside seh, that might help i guess... can start /tracex once seh occurs until it reaches the handler, and study the log file... you might be able to get some info out of it...

Just a thought :>... back to my revision... just a tiny weeny bit more then i am done

crUsAdEr

foxthree
June 8th, 2002, 12:14
Hi Clandestiny:

Interesting reading -- your post. You've echoed the exact thoughts I've been having these days after a few weeks of research myself. It all started with the SuperBPM for Win2K thread.

I did research a "permanent" solution to the SEH DRx clearing tricks. In fact, my research concluded in what you just summarized:

Hook KiUserExceptionDispatcher and NtContinue and you've all the control over SEH This method will work in both Win9x and WinNT/2K!

In fact, before I could even attempt to write an app to test this out our friend EliCZ has - as usual - beaten us. Check out his XcptLog application in the latest APIHooks package. Awesome ... and with full source code you can do whatever you want

In fact, the XcptLog exactly does the same by hooking the above two calls using APIHooks and prints out the thread context when entering KiUserExcpetionDispatcher and before NtContinue is called and all this at USER MODE ... Yes, you heard me right from USER MODE. No need for any more ring-0 stuff

As some "guru" mentioned, EliCZ is way ahead ...

Signed,
-- FoxThree

PS: There may be some pitfalls in hooking it the EliCZ way in ring-3. I believe writing a KMD to do the stuff would be the right way to go. However, there were some people that mentioned that this may not be the 100% solutions and we need to take care of stuff like KiSwapContext and things like that. I'm still not sure about this 'coz my home-brewn variant of XcptLog cures everything

Clandestiny
June 10th, 2002, 22:38
Wow! So I was on the right track... And hooking the kernel exception handler like that is possible (even though EliCZ beat me to it Thanks for the info! No doubt I will have a few more questions for the gurus when my exams are over and I get a chance to look at EliCZ's code in more detail

Cheers,
Clandestiny

Kayaker
June 11th, 2002, 06:48
Hiyas,

Interesting topic indeed. I was looking at Eliczs' code to see how he could have emulated hooking KiUserExceptionDispatcher in Win9x when this isn't exported by that version of ntdll.dll. In NT, he hooks the API directly with:

;------------------------
__EXPORT API_HOOK ApiHookChain[NHOOKS] = {
{"ntdll.dll", "KiUserExceptionDispatcher", HOOK_OVERWRITE, &OldKiUserExceptionDispatcher, &UnhookKiUserExceptionDispatcher, NewKiUserExceptionDispatcher}
;-------------------------

While in Win9x it uses:

;-------------------------
__EXPORT API_HOOK ApiHookChain[NHOOKS] = {
{NULL, 0, HOOK_RAW | HOOK_HARD, &OldKiUserExceptionDispatcher, &UnhookKiUserExceptionDispatcher, NewKiUserExceptionDispatcher}
;-------------------------


From the AH56 source it looks like in the case of NT, the ApiNameOrOrd field in the ApiHookChain API_HOOK structure will contain the string "KiUserExceptionDispatcher" (or "NtContinue", and the hook is established as such.

But in Win9x it's a HOOK_RAW | HOOK_HARD call, which according to the docs, if HOOK_RAW is set, ApiNameOrOrd must contain a 32bit virtual address (valid in Target).

I'm still working on digesting Eliczs' code, but this seems to say you need to know the RVA of the SEH you want to hook before you can even hook it in Win9x.


I'm trying to interpret the DllMain code in order to understand how the ApiNameOrOrd value is used in
\Ah56\Examples\F-Advanced\C\XcptLog\XcptLogW9X.cpp
My "C-interpretation" skills aren't too polished (meaning I suck at C ;-), does anyone familiar with Eliczs' code have a handle on what the BOOL APIENTRY DllMain code is doing during DLL_PROCESS_ATTACH?

[CODE]
BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
if(ul_reason_for_call == DLL_PROCESS_ATTACH) {
if(!Inited) {
++Inited;
if((hDLL = hModule) < (HINSTANCE)0x80000000)
return(FALSE);
PBYTE pb = (PBYTE)GetModuleHandle(_T("KERNEL32.DLL");
if((DWORD)pb < 0xBFF60000)
return(FALSE);
DWORD i;
__try {
for(i = 0x15000; i < 0x20000; i++)
if((*(pb+i) == 0xC7) && (*(pb+i+1) == 0x46) && (*(pb+i+2) == 0x24)
&& (*(PDWORD)(pb+i+3) > (DWORD)pb))
break;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
return(FALSE);
}
if(i < 0x20000) {
ApiHookChain[0].ApiNameOrOrd = (*(LPCSTR*)(pb+i+3));
}
else
return(FALSE);

__try {
for(i = 0x6500; i < 0x9000; i++)
if( (*(pb+i+6) == 0x53) && (*(pb+i+7) == 0x6A) && (*(pb+i+8) == 0x00)
&& (*(pb+i+9) == 0x68) && (*(pb+i+11) == 0x00) && (*(pb+i+12) == 0x2A)
&& (*(pb+i+13) == 0x00) && (*(pb+i+14) == 0xE8))// && (*(pb+i+19) == 0x55))
break;
}
__except(EXCEPTION_EXECUTE_HANDLER) {
return(FALSE);
}
if(i < 0x9000) {
ApiHookChain[1].ApiNameOrOrd = (LPCSTR)(pb+i);
}
else
return(FALSE);

}
else
return(FALSE);
}
return(TRUE);
}
/[CODE]


There still may be some other low level function call which is used early by the system in processing an exception which can be hooked in time to protect the current CONTEXT structure in Win9x, the trick is to find it. KiUserExceptionDispatcher seems to be the exception handler switching point in NT, but this hookable function is missing in Win9x. Making fs:0 read only and handling the page fault just might be the easiest direct approach after all.

btw, I found a good doc on the Native API ntdll.dll:
http://www.sysinternals.com/ntw2k/info/ntdll.shtml

Cheers,
Kayaker

Kayaker
June 11th, 2002, 07:25
Hmm, OK I see in Eliczs' forum that DL had problems running XcptLog on Win98, then got it working...

So what was the fix? ;-)

foxthree
June 11th, 2002, 08:07
Hello Kayaker:

The "fix" was to study the "Test.bat" that is in the same directory as the XcptLog source. Obviously, "DL" made a mistake in giving correct params to XcptLog ;D.

Signed,
-- FoxThree

PS: I'd like to write up a interpretation of EliCZ's C source code but alas I don't have the time right now . May be I'll post the dissection later today

Ni2
June 11th, 2002, 16:58
Hi fellas,

Interesting topic indeed. I'm a bit confused about how the following piece of code that kayaker copied from AH56 works and the code that it generates:

*************
While in Win9x it uses:

;-------------------------
__EXPORT API_HOOK ApiHookChain[NHOOKS] = {
{NULL, 0, HOOK_RAW | HOOK_HARD, &OldKiUserExceptionDispatcher, &UnhookKiUserExceptionDispatcher, NewKiUserExceptionDispatcher}
;-------------------------

****************

What is it hooked in this case for 9x? (no dll, no api...)

Regards,
Ni2

foxthree
June 11th, 2002, 20:29
Hi Ni2:

As Kayaker's posting above says, if there is no API and HOOK_RAW is present, the ApiNameOrOrd must contain a virtual address. In this case, EliCZ's seems to be hooking the addresses 0xBFF87ED3 and 0xBFF76702 for KiUserExceptionDispatcher and NtContinue equivalents! (The Corresponding i values in hex are 0x18320 and 0x6702)

Now the question is to find out why?

Signed,
-- FoxThree

Ni2
June 11th, 2002, 21:16
Hi foxtree!

It's true that 0xBFF87ED3 is called just before the SEH is invoked and 0xBFF76702 is called just after the SEH returns.

Whatda hell are those addresses? SICE says that 0xBFF87ED3 = Kernel32!VirtualQueryEx+2C30 and 0xBFF76702 = FindClose+242 (!!!)

How can he know that? ElicZ...you are COOL!

We have to find out that

Ni2

Kayaker
June 12th, 2002, 06:23
Very cool app. The code checks for the OS then calls the appropriate dll identified by its extension, i.e. XcptLog.w9x, then calls the functions in ApiHooks.dll and PrcWorks.dll to establish the hook.

In the DllMain loading code of XcptLog.w9x is a scan routine of Kernel32.dll for the signature bytes C7 46 24, which is where the first 'magic' address is retrieved:
:BFF88320 C74624D37EF8BF mov [esi+24], BFF87ED3

This address is a function which contains a pointer to an ExceptionInfo structure and must be the kernel code which emulates the functionality of KiUserExceptionDispatcher, or at least is where control returns to from the VMM after an exception.
:BFF87ED3 sub_0_BFF87ED3 proc near
...
:BFF87ED3 ExceptionInfo = _EXCEPTION_POINTERS ptr -1Ch


I decided to trace backwards from this address to see where it came from by setting a
BPM BFF87ED3 X
on it, then setting another BPM on the FromIP address which is returned in the command window of Softice, and so on in reverse until I hit a recognizable system call. About 4 or 5 breaks later I hit the RET in Cancel_Priority_VM_Event:

;---------------
Cancel_Priority_VM_Event+031F
...
0028:C000383E FF15B81001C0 CALL [Adjust_Thread_Exec_Priority]
0028:C0003844 FA CLI
0028:C0003845 C3 RET
;--------------
This is part of VMM code and eventually returns directly to the Kernel32.dll address BFF87ED3.


I wanted to see if instead of creating a HOOK_RAW | HOOK_HARD hook of this address in order to trap SEH exceptions as Eliczs' code does, I could do it from Ring0 by hooking the earlier VMM call first, and then have control over the CONTEXT registers. I've succeeded in hooking Cancel_Priority_VM_Event, and I'm trying to figure out the best way to use the parameters that are on the stack when the hook occurs.

I'm using the SystemHook example code I wrote for the 'Reversing "system tools" under w98' thread on the mini-project forum. I just changed the hook used to Cancel_Priority_VM_Event. The hook is established in a vxd with:
GetVxDServiceOrdinal eax, Cancel_Priority_VM_Event ; Service to hook - CHANGE THIS TO ANY OTHER
mov esi, OFFSET32 HookProc ; pointer to our hook procedure
VMMCall Hook_Device_Service ; address of the specified service returned in ESI
jc hookfailed

In the hook procedure which gets called for the hooked Service, I'm trying to use _VWIN32_Get_Thread_Context to get the debug registers and the rest of the CONTEXT structure.

;============= Begin HookProc =================
BeginProc HookProc, HOOK_PROC, OldServiceAddress, LOCKED

;----------------------------------------------------------------------
; Create a stack frame so we can access stack parameters at time of hook,
; parameters will vary with Service hooked.
;----------------------------------------------------------------------
push ebp
mov ebp, esp

From analyzing the stack at this point I am able to make out 4 parameters
which are passed in response to the Cancel_Priority_VM_Event hook:

; [ebp+04h] == Return address, points to immediately after the Cancel_Priority_VM_Event call
; [ebp+08h] == The Cancel_Priority_VM_Event call itself (it seems)
; [ebp+0Ch] == DDB Vxd_Desc_Block structure
; [ebp+10h] == Thread Block Control structure THCB (tcb_s)


The question now is how to best use this information? It looks like you have complete access to the DDB, from which you can access information on other system drivers, or detect for Softice for example. The Thread Block information I need to explore to see how it can be used. And you _should_ be able to get CONTEXT information with a few system calls as well.

It's this last part that's still giving me some troubles. There's some info on Eliczs' forum about getting the proper context for a _VWIN32_Get_Thread_Context call, but I'm still not sure exactly how it should be done. This is the description for it:

------------------------------------------
_VWIN32_Get_Thread_Context
Copies the registers for the specified thread to the pcontext buffer. Uses C calling conventions.

Returns non-zero value if successful; zero otherwise.
ptcb - Ring 0 thread handle. Indicates the thread to get the context for.
pcontext - Address of a CONTEXT structure that receives the appropriate context of the specified thread. The value of the ContextFlags member of this structure specifies which portions of a thread's context are retrieved
;------------------------------------------


The question is which thread handle is the proper one to get the CONTEXT_DEBUG_REGISTERS values returned after the call? I need the proper thread for the program which installed the SEH and generated the hook. Get_Sys_Thread_Handle gives the system thread handle and Get_Cur_Thread_Handle gets the "currently executing" thread, but I'm not sure if either of these are valid. This is without loading the target program at the moment.

The Owl suggested you have to access R0TCB and TDBX to get the context of a thread other than your own. I don't have enough information on the TCB structure to be able to interpret this suggestion, or even know what TDBX is as part of the tcb_s structure described in vmm.h. Documentation always sucks ;-)

The code seems stable, after handling the hook you chain to the previous hook with jmp [OldServiceAddress]. The only test app I've used so far is XcptLog itself, I figured between it and Etricks.exe setting up SEHs, there would be plenty of opportunity for Cancel_Priority_VM_Event to be called. The hook broke about 12 times, then the app would execute as normal. I don't know if all of them were because of being used in response to an SEH, or whether the system used Cancel_Priority_VM_Event in other cases as well.

But I could use a good test app, Foxthree, I'm not sure if you wrote a little test app which produces SEH code for your testing, but if you think it could be a good test target, could you send it to me or up your test app? I need something simple to try to sort out what I'm seeing here and would like to try it "blind" to see if this technique will hook any app using SEH. If not no problem. Thx..

Kayaker