异常处理真的是个好复杂的东西,网上有不少牛文,大家参考,这里只说其中的一点,关于SHE不工作的问题及如何解决的一点思路。
... {
Xor eax,eax
Mov [eax],ebx
}
__except (EXCEPTION_EXECUTE_HANDLER)
... {
}
上面的代码稳蓝。
疑惑了N久,WINDBG了下,发现了一个叫做RtlIsValidHandler的函数,顾名思义是一个判断异常处理过程是否合法的函数。原型应该是
BOOLEAN RtlIsValidHandler(PVOID Handler);
如果返回0那么这个异常处理过程就不会被调用,就是我们看到的失效状况。查了下这个函数据说是xp sp2 跟 2003 sp1 才出现的一个新的安全检测函数。防止数据运行,溢出攻击等等。在2000下尝试被加载的驱动,果然工作良好。
于是最简单的解决方法诞生了,想办法找到RtlIsValidHandler的地址,hook了直接返回1就OK。这样相当于屏蔽了系统的安全检测机制,商业产品的话被同行说成给病毒木马开后门就不好了。还是着手分析下RtlIsValidHandler的流程构造一个符合规则的异常处理函数是正道。
还好RtlIsValidHandler不大,开始就是对一个叫做RtlLookupFunctionTable函数的调用,这次不是太容易顾名思义了。单从返回参数来看也很诡异。RtlLookupFunctionTable也非常不大^_^,而且提示明显,有一个对_PsLoadedModuleList的遍历动作,有点安全方面经验的朋友基本可以很快推测个八九不离十。这个函数的原型跟伪代码如下:
... {
if 当前irql不为DISPATCH_LEVEL
KeRaiseIrqlToDpcLevel()
遍历_PsLoadedModuleList
...{
if 异常处理程序地址 >= 当前模块基址
and 异常处理程序地址 <= 当前模块基址+当前模块大小
...{
返回值1=当前模块->InMemoryOrderLinks->Flink
返回值3=当前模块-> InMemoryOrderLinks->Blink
返回值2=当前模块->DllBase
降低irql
Return ;
}
}
遍历完成没有找到
返回值1=传入的异常处理地址
返回值3=传入的异常处理地址
返回值2=遍历到的最后一个模块的基址
降低irql
Return;
}
大致如此,随后RtlIsValidHandler对返回值做了相关检测没有仔细分析。已知的如下。
if 返回值2 <= 0 (也就是大于0x80000000没错吧?)确定这个异常处理是非法的。
补充:PsLoadedModuleList是一个内核变量,指向一个加载模块的链表,具体结构是
+0x000 InLoadOrderLinks : _LIST_ENTRY
+0x008 InMemoryOrderLinks : _LIST_ENTRY
+0x010 InInitializationOrderLinks : _LIST_ENTRY
+0x018 DllBase : Ptr32 Void
+0x01c EntryPoint : Ptr32 Void
+0x020 SizeOfImage : Uint4B
+0x024 FullDllName : _UNICODE_STRING
+0x02c BaseDllName : _UNICODE_STRING
+0x034 Flags : Uint4B
+0x038 LoadCount : Uint2B
+0x03a TlsIndex : Uint2B
+0x03c HashLinks : _LIST_ENTRY
+0x03c SectionPointer : Ptr32 Void
+0x040 CheckSum : Uint4B
+0x044 TimeDateStamp : Uint4B
+0x044 LoadedImports : Ptr32 Void
+0x048 EntryPointActivationContext : Ptr32 Void
+0x04c PatchInformation : Ptr32 Void
实际发现InMemoryOrderLinks InInitializationOrderLinks这两个链表貌似在做一些跟它的名称很不相符的工作。其内容经常为0或者未知内容,总之不像一个双向链表。从前面的比较也可以看出不应该是链表,自己太笨不能理解。希望有明白的牛人可以指教。
SEH用起来简单,理解起来相当复杂,Ring3跟Ring0的检测貌似不完全相同。总之有一天你发现异常捕获也异常了^_^,就考虑下新加入的这个RtlIsValidHandler。