先放代码:https://github.com/coNgY1/global-seh-hook
之前看到过VEH HOOK,就一直想能不能用SEH进行HOOK,这里就写了一个:
VEH HOOK主要好处是进程相关,而SEH是每个线程相关,每个线程的SEH chain是不同的,所以要弄SEH HOOK就必须将每个线程都添加一个seh handle,如果有新线程要创建,就必须在新线程创建后执行前进行添加我们自己的seh handle。
那么有两个地方需要处理:
1、新线程创建后执行前添加seh handle。
2、seh handle不通过push handle; push fs:[0]; mov fs:[0],esp这种来创建。
针对1问题:
windows线程在创建后到达R3层上首先是LdrpInitialize这个位置,我最开始HOOK这个地方进行添加SEH handle,但是发现线程执行时候会有一个系统的seh handle在我添加的handle之前。也就是在LdrpInitialize后会进行一次添加,经过分析发现LdrpInitialize最后会调用ZwContinue
而ZwContinue的第一个参数是CONTEXT结构体,ZwContinue就是跳转到这个线程上下文去执行,+0xB8是EIP指向的地址是NTDLL.RtlUserThreadStart
分析RtlUserThreadStart发现它会调用RtlInitializeExceptionChain这个api对线程添加一个系统SEH handle,这个handle就是出现异常时候弹一个程序崩溃,终止程序。继续跟踪发现会调用BaseThreadInitThunk
这个函数作用就是初始化_tiddata然后call ThreadProc,并在线程函数返回后进行ExitThread操作。
所以我选择HOOK BaseThreadInitThunk这个API,因为此时系统的SEH已经被安装,我们可以将自己的SEH Handle添加到链表头部了。需要注意的是BaseThreadInitThunk函数的__fastcall调用约定。
针对问题2:
以前分析后SEH的调用部分,SEH调用前会判断handle是否在堆栈上,如果不是则不会处理,这里我将seh handle放在了tib->StackLimit处,这里是线程堆栈的底部,这样就不用通过push handle,push fs:[0],mov fs:[0],esp来处理了。