这个故事是发生在内核,驱动A开始工作的很好,后来老大说改成由驱动B模拟系统加载驱动B吧。也就是自己完成PE文件映射,重定位处理,导入表处理等等。改完后唯一的问题就是,SHE不工作了。
__try
{
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的遍历动作,有点安全方面经验的朋友基本可以很快推测个八九不离十。这个函数的原型跟伪代码如下:
返回值1 RtlLookupFunctionTable(异常处理程序地址,返回值2,返回值3 )
{
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 返回值1 ==0 或者 返回值3 == 0直接确定这个异常处理是合法的。
if 返回值2 <=0(也就是大于0x80000000没错吧?)确定这个异常处理是非法的。
回到SHE不能捕获异常的问题,原因是因为在驱动加载时为了隐蔽没有把模块插入到PsLoadedModuleList链表中,上面RtlLookupFunctionTable会发现异常处理过程没有落在任何Module之内,之后的返回值肯定导致RtlIsValidHandler返回0,于是异常处理过程就不被调用了。尝试把驱动加入LoadedModuleList后,问题已经解决。其他的检测也就不仔细研究了。
补充:PsLoadedModuleList是一个内核变量,指向一个加载模块的链表,具体结构是
lkd> dt nt!_LDR_DATA_TABLE_ENTRY
+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。