typedef struct _EXCEPTION_REGISTRATION_RECORD
{
struct _EXCEPTION_REGISTRATION_RECORD * pNext;
FARPROC pfnHandler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
FS:[0]中的指针是指向_EXCEPTION_REGISTRATION_RECORD首部的指针。对应地,每个结
构体在pNext域中包含着指向下一个结构体的指针和指向回调函数pfnHandler的指针。不难猜到,这就是异常处理的处理程序。函数的原型如下:
EXCEPTION_DISPOSITION __cdecl _except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
);
我们来分析函数的参数。第一个参数是指向下面结构体的指针。
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
ExceptionCode是Windows NT的异常代号。异常在NTSTATUS.H文件中被描述为STATUX_xx
xxxx
: ExceptionAddres - 发生异常的地址。
第三个参数是指向CONTEXT结构体的指针。
typedef struct _CONTEXT
{
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
:.
DWORD Esp;
DWORD SegSs;
} CONTEXT;
这个结构体定义于WINNT.H文件。其意义是不言而喻的,这里就不全写了。函数返回下面
枚举类型值中的一个:
typedef enum _EXCEPTION_DISPOSITION {
ExceptionContinueExecution,
ExceptionContinueSearch,
ExceptionNestedException,
ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;
ExceptionFlags定义了下面的位标志:
#define EH_NONCONTINUABLE 1
#define EH_UNWINDING 2
#define EH_EXIT_UNWIND 4
#define EH_STACK_INVALID 8
#define EH_NESTED_CALL 0x10
在发生异常时,控制传递到ntoskrnl.exe中相应的处理程序。例如,如果试图下面这段
代码:
mov eax,80100000h
mov dword ptr [eax],0
这会引发异常0e,控制传递向处理程序KiTrap0E,堆栈中错误号为07(试图在用户模式
下向内核写入。该页位于内存中,因为线性地址80100000h是内核加载的起始地址)。之
后,控制传递到ntdll.dll,在这里解析线程的TEB并顺次执行链表中所有的处理程序,
直到某个函数的返回代号不是ExceptionContinueSearch。在此之后,再次调用链表中的
所有处理函数,直到找到某个函数,但这次用的是另外一个代号ExceptionCode(STATU
S_UNWIND)并在ExceptionFlags设置位EH_UNWINDINGS。这个标志用于异常处理,进行堆
栈的清除和其它必要的工作。如果没有一个处理程序能处理,则就来到链表中最后一个
处理程序,由Win32子系统建立的处理程序。这个处理程序是如何被建立的以及建立在哪
里在以后研究CreateProcessW函数时会讲到。关于异常处理的完整介绍可以从MSDN获得
,因为在反汇编源代码中所得到的是扩大化了的异常处理机制(更高层次的机制)。