SEH in ASM 研究(二) (6千字)
PART II 继续深入三、传递给异常处理句柄的参数
要想明白seh处理例程如何得到感兴趣的信息,首先要明白SEH的作用机制.事实上,当异常
发生时,系统给了我们一个处理异常的机会,他首先会调用我们自定义的seh处理例程,当然也包括
了相关信息,在调用之前,系统把包含这些信息结构的指针压入stack,供我们的异常处理例程调用,
传递给例程的参数通常是四个,其中只有三个有明确意义,另一个到现在为止还没有发现有什么作用,
这些参数是:pExcept:DWORD,pErr:DWORD,pContext:DWORD,pDispatch意义如下:
pExcept: --- EXCEPTION_RECORD结构的指针
pErr: --- 前面ERR结构的指针
pContext: --- CONTEXT结构的指针
pDispatch:---没有发现有啥意义
ERR结构是前面介绍的_EXCEPTION_REGISTRATION结构,往前翻翻,Dispatch省略,下面介绍
EXCEPTION_RECORD和CONTEXT结构的定义:
;//================================以下是两个成员的详细结构========================================
EXCEPTION_RECORD STRUCT
ExceptionCode DWORD ? ;//异常码
ExceptionFlags DWORD ? ;//异常标志
pExceptionRecord DWORD ? ;//指向另外一个EXCEPTION_RECORD的指针
ExceptionAddress DWORD ? ;//异常发生的地址
NumberParameters DWORD ? ;//下面ExceptionInformation所含有的dword数目
ExceptionInformation DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?)
EXCEPTION_RECORD ENDS ;//EXCEPTION_MAXIMUM_PARAMETERS ==15
;//================================具体解释========================================
ExceptionCode 异常类型,SDK里面有很多类型,你可以在windows.inc里查找STATUS_来找到更多
的异常类型,下面只给出hex值,具体标识定义请查阅windows.inc,你最可能遇到的几种类型如下:
C0000005h----读写内存冲突
C0000094h----非法除0
C00000FDh----堆栈溢出或者说越界
80000001h----由Virtual Alloc建立起来的属性页冲突
C0000025h----不可持续异常,程序无法恢复执行,异常处理例程不应处理这个异常
C0000026h----在异常处理过程中系统使用的代码,如果系统从某个例程莫名奇妙的返回,则出现此代码,
如果RtlUnwind时没有Exception Record参数也同样会填入这个代码
80000003h----调试时因代码中int3中断
80000004h----处于被单步调试状态
注:也可以自己定义异常代码,遵循如下规则:
_____________________________________________________________________+
位: 31~30 29~28 27~16 15~0
_____________________________________________________________________+
含义: 严重程度 29位 功能代码 异常代码
0==成功 0==Mcrosoft MICROSOFT定义 用户定义
1==通知 1==客户
2==警告 28位
3==错误 被保留必须为0
ExceptionFlags 异常标志
0----可修复异常
1----不可修复异常
2----正在展开,不要试图修复什么,需要的话,释放必要的资源
pExceptionRecord 如果程序本身导致异常,指向那个异常结构
ExceptionAddress 发生异常的eip地址
ExceptionInformation 附加消息,在调用RaiseException可指定或者在异常号为C0000005h即内存异常时含义如下
第一个dword 0==读冲突 1==写冲突
第二个dword 读写冲突地址
;//================================解释结束========================================
off.
CONTEXT STRUCT ; _
ContextFlags DWORD ? ; | +0
iDr0 DWORD ? ; | +4
iDr1 DWORD ? ; | +8
iDr2 DWORD ? ; >调试寄存器 +C
iDr3 DWORD ? ; | +10
iDr6 DWORD ? ; | +14
iDr7 DWORD ? ; _| +18
FloatSave FLOATING_SAVE_AREA <> ;浮点寄存器区 +1C~~~88h
regGs DWORD ? ;--| +8C
regFs DWORD ? ; |\段寄存器 +90
regEs DWORD ? ; |/ +94
regDs DWORD ? ;--| +98
regEdi DWORD ? ;____________ +9C
regEsi DWORD ? ; | 通用 +A0
regEbx DWORD ? ; | 寄 +A4
regEdx DWORD ? ; | 存 +A8
regEcx DWORD ? ; | 器 +AC
regEax DWORD ? ;_______|___组_ +B0
regEbp DWORD ? ;++++++++++++++++ +B4
regEip DWORD ? ; |控制 +B8
regCs DWORD ? ; |寄存 +BC
regFlag DWORD ? ; |器组 +C0
regEsp DWORD ? ; | +C4
regSs DWORD ? ;++++++++++++++++ +C8
ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?)
CONTEXT ENDS
;//================================以上是两个成员的详细结构========================================
I、传递给final句柄的参数,只有两个可描述为EXCEPTION_POINTERS结构,定义如下:
EXCEPTION_POINTERS STRUCT
pExceptionRecord DWORD ?
ContextRecord DWORD ?
EXCEPTION_POINTERS ENDS
在call xHandler之前,堆栈结构如下:
esp -> *EXCEPTION_RECORD
esp+4 -> *CONTEXT record ;//具体结构见下面
然后执行call _Final_Handler,这样在程序里要调用什么不轻而易举了吗?
II、 传递给per_thread句柄的参数,如下:
在call xHandler之前,在堆栈中形成如下结构
esp -> *EXCEPTION_RECORD
esp+4 -> *ERR ;//注意这也就是fs:[0]的指向
esp -> *CONTEXT record ;//point to registers
esp -> *Param ;//呵呵,没有啥意义
然后执行 call _Per_Thread_Handler
调用handler的原型是这样
invoke HANDLER,*EXCEPTION_RECORD,*_EXCEPTION_REGISTRATION,*CONTEXT record,*Param
即编译代码如下:
PUSH *Param ;//通常不重要,没有什么意义
push *CONTEXT record ;//上面的结构
push *ERR ;//the struc above
push *EXCEPTION_RECORD ;//see above
CALL HANDLER
ADD ESP,10h
好现在你明白了应该如何访问具体有关系统信息的细节了吧,下一部分,让我们来看看如何应用...(待续)
