首页 > 安全资讯 >

SSDT HOOK拦截远线程的创建(上)

09-04-13

 注:本文已发表在2009年第3期《黑客防线》,转载请注明来源。      在ring3的API HOOK中,怎样迫使目标进程调用我们的傀儡DLL是我们非常重视的一个问题。在多数情况下,我们都喜欢使用CreateRemoteThread

 注:本文已发表在2009年第3期《黑客防线》,转载请注明来源。
      在ring3的API HOOK中,怎样迫使目标进程调用我们的傀儡DLL是我们非常重视的一个问题。在多数情况下,我们都喜欢使用CreateRemoteThread在目标进程中创建一个远程线程来迫使它加载我们的DLL。
 因为CreateRemoteThread的使用方法并不复杂,而且与其他方式相比,它可以称得上是一种相当“优雅”的做法。各种因素的汇集就导致了这种方法的泛滥,致使很多具备主动防御或行为监控的安全软件都加强了对这个函数的照顾。
      最近在自己的毕业设计中也要用到这个功能,阻止一些简单地调用CreateRemoteThread注入到我们要保护的进程中,就对这个问题稍微学习了下。
      根据我们的思维惯性,很显然我们应该HOOK CreateRemoteProcess函数,这在ring3下非常容易实现,但是如果这样的话我们的保护也太没有强度可言了。正好最近正在学习SSDT HOOK,就使用这个方法吧,算是在实战中磨炼自己,吼吼~~

第一部分:SSDT HOOK的使用方法

      关于什么是SSDT等基本概念我就不再赘述了,网上有很多文章,而且黑防以前的杂志上也有不少详细介绍,这里我推荐一篇李马的《城里城外看SSDT》,确实不错。我一开始是修改《Rootkits——Windows内核的安全防护》关于SSDT的示例代码,它给出了一个挂钩ZwQuerySystemInformation来隐藏进程的示例。
      很不幸的是,当我照本宣科地把ZwQuerySystemInformation修改成ZwCreateThread(第二部分会介绍,这里只要知道CreateRemoteThread是最终调用ZwCreateThread)后,编译器报错:“error LNK2019: unresolved external symbol __imp__ZwCreateThread@32 referenced in function _DriverEntry@8”。这个错误令我诧异了很久,在尝试解决问题的过程中,偶然发现了出现问题的原因,原来ZwCreateThread函数没有被ntoskrnl.exe导出(使用depends看看就知道了)。
      与之同时,我们在函数中用到了几个宏,它们的详细定义如下:

#define SYSTEMSERVICE(_Function)
     KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_Function+1)]

#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)

#define HOOK_SYSCALL(_Function, _Hook, _Orig )
          _Orig = (PVOID) InterlockedExchange( (PLONG) &MappedSystemCallTable
          [SYSCALL_INDEX(_Function)], (LONG) _Hook)

#define UNHOOK_SYSCALL(_Function, _Hook, _Orig)  InterlockedExchange((PLONG)
         &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)

      而SYSTEMSERVICE宏是采用由ntoskrnl.exe导出的Zw*函数地址,并返回对应的Nt*函数在SSDT中的地址,SYSCALL_INDEX采用Zw*函数地址并返回它在SSDT中相应的索引号。
      由于ZwCreateThread没有被ntoskrnl.exe导出,所以这时我们就无法直接使用上述的宏,只能另想他法。
      这时候最常规的方法就是从ntdll中动态获取地址,不过我不想那么麻烦。昨天在群里问了下,iceboy大牛给出了一种猥琐的方法,因为ZwCreateThread在SSDT中的前后两个函数都被导出了,分别是ZwCreateSymbolicLinkObject和ZwCreateTimer。
      这时候我们就可以分别获取这两个函数的索引,判断它们的索引之差是否为2,如果是,则它们中间的函数就是ZwCreateThread,代码如下所示:

a = SYSCALL_INDEX(ZwCreateSymbolicLinkObject);
b = SYSCALL_INDEX(ZwCreateTimer);
c = b - a;

KdPrint(("[nokyo] FunctionIndex = %d -> %d", a, c));
if (c != 2)
{
    c = 0;
    return STATUS_UNSUCCESSFUL;
}
......
HOOK_ON(c, New_ZwCreateThread);

当然,这时候开启和关闭HOOK的宏也要进行细微的修改,如下所示:

#define HOOK_ON(_FuncIndex, _New) (PVOID)InterlockedExchange
            ((PLONG)&MappedSystemCallTable[_FuncIndex], (LONG)_New)
#define HOOK_OFF(_FuncIndex, _Old) InterlockedExchange
            ((PLONG)&MappedSystemCallTable[_FuncIndex], (LONG)_Old)

      不过我用起这个方法还是有问题,主要是不会获取原函数的地址并保存起来,这样虽然能够开启HOOK,但驱动卸载的时候却没法还原(事实上是可以的,不过当时我没有想到什么简单的方法,其实就是直接读取SSDT中的内容,把它当成指针就行了);而且我获取到ZwCreateTimer的索引号总是错误的,很不解。
      问题放的时间久了还没解决,心里难免有点急躁,到处找资料(偶可是在硬盘上一个文档一个文档地翻,不能上网真苦啊)总算找到了一点信息。
      硬盘的某个角落里静静地躺着adly以前给的一个SSDT HOOK ZwCreateFile的例子,正好符合我的要求,赶紧拿来用用。
      然后就是劳力活儿,经过将近一个小时的忙活,总算成功挂钩了ZwCreateThread函数,效果如图1所示:

 






 整体代码如下所示:

#define CREATETHREAD_INDEX    0x35        // 硬编码,不保证在其他地方也相同
// 安装 HOOK
// 关闭写保护
__asm
{
    cli    ;
    mov    eax, cr0
    and    eax, ~0x10000
    mov    cr0, eax
}
   
// 保存原始值
(ULONG)Old_ZwCreateThread = *((PULONG)(KeServiceDescriptorTable.
ServiceTableBase) + (ULONG)CREATETHREAD_INDEX);
// 指向新函数
*((PULONG)(KeServiceDescriptorTable.ServiceTableBase) +
     (ULONG)CREATETHREAD_INDEX) = (ULONG)New_ZwCreateThread;
   
// 恢复写保护
__asm
{
    mov eax, cr0
    or    eax, 0x10000
    mov cr0, eax
    sti    ;
}

      在卸载的时候使用以下的代码恢复,这里限于篇幅我省略了关闭/恢复写保护的代码,在程序中可不能省略,否则会BSOD滴:

// 恢复原函数
*((PULONG)(KeServiceDescriptorTable.ServiceTableBase) + 
                (ULONG)CREATETHREAD_INDEX) = (ULONG)Old_ZwCreateThread;

第二部分:挂钩CreateRemoteThread的几个问题

      在内核中如何挂钩CreateRemoteThread函数呢,因为我们必须知道它没有与之向对应的ZwCreateRemoteThread函数,我搜遍了硬盘上所有的资料,终于在《w2k native》里面搜到了有用的东西,原来它和CreateThread一样最终都是调用ZwCreateThread实现的。
      这样我们的问题就转化成了如何使用SSDT HOOK来挂钩ZwCreateThread函数,这个函数的原型如下所示:

NTSYSAPI
NTSTATUS
NTAPI
ZwCreateThread(
    OUT     PHANDLE                 ThreadHandle,
    IN      ACCESS_MASK             DesiredAccess,
    IN      POBJECT_ATTRIBUTES      ObjectAttributes,
    IN      HANDLE                  ProcessHandle,
    OUT     PCLIENT_ID              ClientId,
    IN      PCONTEXT                ThreadContext,
    IN      PUSER_STACK             UserStack,

热点推荐