今天看到 http://www.rootkit.com/newsread.php?newsid=885   Ruining the Rustock.C rumors and myths and Kaspersky Lab role 这篇文章,于是把它的大体意思翻译了过来,,因为本人英语确实很烂,所以大家就凑合着,很多术语都只是直译过来(懒得去查),一些细节也有可能翻译错误,,,大家见笑了.

WRITE BY :hljleo
MY BLOG:http://hi.baidu.com/hljleo
MY QQ:554920269



1 序言
2 Rustock 的新特点,集合的更多信息,制作检测工具。
3 Rootkits roadmap(略)
4 Alexander Gostev 扮演的游戏角色(略)
5 结束语(略)

1序言
Rustock。。。。邪恶的心脏。

它真的无法检测到吗?当然不是的。我们有没有可能在Rustock 运行的时候将它删除掉吗?这是肯定的。在2006年,Rustock不能被检测到这只是一个谣言而已。因为在那个时候,只有一些不大成熟的rootkit检测软件。那么我们怎么去检测它呢?我们需要用到驱动程序吗?它HOOK了那么多的内核函数,是个狡猾的角色。。。。。
现在让我们立刻构筑一个用户态的检测器(小巧和简单)来对抗这只“不可检测”的Rustock。
整个故事是从2005年末开始的,这一年末对于反病毒技术人员来说是毫无希望的,但现在依然记忆犹新。也就是说,

rootkits以极快的速度摧毁了反病毒工具。Hacker Defender 为了对付这种情形带来了很多新的素材资料。这是一个技术战争新时代的开始。当然我们也不能说这是一场战争,因为双方只是为了保护自己和只要双方的任何一方不存在另一方也就不存在。(被使用来隐藏的东西也能用来检测)。

这些真的很难说,就像AV当时为什么在很长时间内不发起一场合适的反抗一样。这是一个想法与实现的决定性时间。当时已经70%的计算机被感染了,你知道吗?在不考虑其他反病毒公司时这个数目一直在稳定的增长。这是被他们绝对接受的事实。但是他们一点都不反抗,因为他们想要的只是----你口袋里的钱。你认为他们会真的是帮助一些人吗?其实在他们眼里---你最好是给他们钱然后闭嘴。这就是他们的政策,这就是商业。
这就是他们不能保护和揭示新的威胁的时候就有其他的公司出来代替他们,这就是最近3年很多反病毒软件的崛起的原因。。。。。。。


AV公司已经敌对rootkits 和Rustock 特别是。。。。
这种赌注叫---“主动防御”,它的主要目标就是让有害的软件不能正确的运行,工作,因为主动防御能分析出这些软件的行为是否具有有害性(这是很容易使系统不稳定的)。 Kaspersky 实际上有创新的地方,它在有名的反病毒监控器中构建了coded HIPS 和结合了一些相关工作原理。任何事件都是无法离开操作系统的管理的。实现这些功能有很多方法,很多HIPS 和AV 内置 HIPS 模块选中SSDT,HOOK 了很多的函数。。不信,看Kaspersky的驱动文件klif.sys 。

下面所有函数列表都是从 DriverEntry 原始调用的,位于某处的偏移量是0003CXXX ,依据 klif 版本而定。


InitializePhase0();
  InitializeResourcesList();
  v15 = FltRegisterFilter(P, &v24, &dword_3A1A0);
  String2.Buffer = (PWSTR)v15;
  if ( v15 < 0 )
  {
    ShutdownWhileInitializationError();
    return (unsigned int)String2.Buffer;
  }
  v7 = Drink_KLINSKOE();

….stripped not interested info….

  v18 = InitializeFsFilterCommunication(&String2);
  v11 = v18;
  if ( v18 >= 0 )
  {
    v12 = CreateAndRegisterDevicesAndLinks((PDRIVER_OBJECT)P, &String2);
    if ( v12 >= 0 )
    {
      RtlInitUnicodeString((LSA_UNICODE_STRING *)&v39, &word_3C65A);
      stru_3A27C.Length = 0;
      v19 = (_WORD)String2.Length + v39 + v10->Length + 2;
      stru_3A27C.MaximumLength = (_WORD)String2.Length + v39 + v10->Length + 2;
      v13 = (WCHAR *)ExAllocatePoolWithTag(PagedPool, v19, 0x387A4C4Bu);
      stru_3A27C.Buffer = v13;
      if ( v13 )
      {

… stripped uninterested info….
            if ( v22 >= 0 )
            {
              InitializePhase1();
              Kakayata_hyinya1();
              v23 = Kakayata_hyinya2();
              HIBYTE(P->Size) = v23;
              if ( v23 )
                HIBYTE(P->Size) = sub_14F0C();
              Kakayata_hyinya3();
              if ( HIBYTE(P->Size) )
              {
                OwnAndFuckTheSystem();
                SetRegistryCallback();
                v11 = FltStartFiltering(dword_3A1A0);
                
We interested in the following entries

INIT:0003C4BD                 call    OwnAndFuckTheSystem
INIT:0003C4C2                 call    SetRegistryCallback

  v3 = PsSetCreateProcessNotifyRoutine(NotifyRoutine, 0);
  v2 = v3;
  if ( v3 >= 0 )
  {
    byte_38A76 = 1;
    v4 = PsSetCreateThreadNotifyRoutine(sub_1CE78);
    v2 = v4;
    if ( v4 >= 0 )
    {
      byte_38A77 = 1;
      v5 = PsSetLoadImageNotifyRoutine(nullsub_1);
      v2 = v5;
      if ( v5 >= 0 )
      {
        byte_38B14 = 1;
        HookSSDTEntry(&dword_38EFC, sub_1DBD6, "NtOpenProcess");
        HookSSDTEntry(&dword_39004, sub_1DAB2, "NtAdjustPrivilegesToken");
        HookSSDTEntry(&dword_39014, sub_1DC7E, "NtTerminateProcess");
        HookSSDTEntry(&dword_38EEC, sub_1DCF0, "NtWriteVirtualMemory");
        HookSSDTEntry(&dword_38F14, sub_1DDAE, "NtCreateThread");
        HookSSDTEntry(&dword_38FF8, sub_1D010, "NtSaveKey");
        HookSSDTEntry(&dword_38EE8, sub_1D320, "NtRestoreKey");
        HookSSDTEntry(&dword_38F0C, sub_1D1BE, "NtReplaceKey");
        if ( IsWindowsXP || IsWindows2000 )
        {
          HookSSDTEntry(&dword_38F10, sub_1D4EE, "NtCreateKey");
          HookSSDTEntry(&dword_38EF8, sub_1D60C, "NtSetValueKey");
          HookSSDTEntry(&dword_38EE0, sub_1D72A, "NtQueryValueKey");
          HookSSDTEntry(&dword_38F18, sub_1D82A, "NtDeleteKey");
          HookSSDTEntry(&dword_38EF4, sub_1D8EA, "NtQueryMultipleValueKey");
          HookSSDTEntry(&dword_38F04, sub_1D9C8, "NtDeleteValueKey");
        }
        GetProcedureAddressEx(&dword_38A84, "PsGetThreadId");
        GetProcedureAddressEx(&dword_38A88, "PsGetThreadProcessId");
        GetProcedureAddressEx(&dword_38A8C, "PsGetProcessId");
        if ( dword_38A84 )
        {
          if ( dword_38A88 )
          {
            HookSSDTEntry(&dword_38F08, sub_1DEAC, "NtSetContextThread");
            if ( (unsigned __int8)HookSSDTEntry(&dword_38F00, sub_2115A, "NtResumeThread") )
              HookSSDTEntry(&dword_39018, sub_21038, "NtSuspendThread");
            if ( IsWindowsXP )
            {
              if ( dword_38A8C )
                HookSSDTEntry(&dword_39010, sub_20F54, "NtSuspendProcess");
            }
          }
        }
        HookSSDTEntry(&dword_38AFC, sub_1F71E, "NtConnectPort");
        HookSSDTEntry(&dword_38B00, sub_1FEB0, "NtRequestWaitReplyPort");
        HookSSDTEntry(&dword_38AF8, sub_1F5EE, "NtSecureConnectPort");
        HookSSDTEntry(&dword_38AE0, sub_1EFC6, "NtOpenFile");
        HookSSDTEntry(&dword_38AE4, sub_1F16A, "NtCreateFile");
        HookSSDTEntry(&dword_38AE8, sub_1F32C, "NtFsControlFile");
        HookSSDTEntry(&dword_38AEC, sub_1F438, "NtDeviceIoControlFile");
        dword_38A98 = SpecialHookSSDTEntry("NtQueryInformationThread");
        if ( IsWindowsXP || IsWindows2003 )
        {
          if ( dword_3A264 & 0x40 )
            sub_31978();
        }
        HookSSDTEntry(&dword_38FFC, sub_20936, "NtSystemDebugControl");
        HookSSDTEntry(&dword_3900C, sub_209C4, "NtLoadDriver");
        HookSSDTEntry(&dword_38FA0, sub_20A8C, "NtSetSecurityObject");
        HookSSDTEntry(&dword_38A94, sub_1DF84, "NtClose");
        HookSSDTEntry(&dword_39000, sub_20E52, "NtCreateSymbolicLinkObject");
        HookSSDTEntry(&dword_38FB0, sub_20EA6, "NtSetSystemInformation");
        HookSSDTEntry(&dword_38EF0, sub_20E7C, "NtOpenSection");
        if ( dword_38A88 )
        {
          if ( dword_38A84 )
            HookSSDTEntry(&dword_39008, sub_20BFA, "NtQueueApcThread");
        }
        HookSSDTEntry(&dword_38EE4, sub_2127C, "NtDuplicateObject");
      }
    }
  }
  Zamuta_c_CSRSS_chto_prosto_pizdec_delat_nehuy_bulo_vidat_ili_deti_dorvalis_do_ddk();
  return v2;
}


正如你所看到的,现代反病毒软件使用了SSDT HOOING和大量的白痴行为。这些都有用吗?当然不一定。因为HIPS的概念在很多地方很容易受到攻击的。
第一:如果ROOTKIT已经进入内核安装在操作系统中,那么主动防御还能做什么呢?

第二:任何主动防御都必须要用户的参与,最终如果用户出现一个小小的错误的决定,整个HOOKING的杂技游戏就会受到威胁,
第三:安全性是一直存在的一个问题,因为它并不是操作系统的一部分。他们就是侵略者(行为上像某些 rootkits ),只是他们比某些流氓软件获得操作系统更多的承认而已。这也就是为什么大部分antimalware卖主很担心Vista 的变化。
第四:任何主动防御的执行,仅仅因为更多的必须的操作和更多细微的处理的出现。
看一个来自AV公司的极端的buggy soft 。难道你认为处理HOOKS在世界级的编程中是一件漂亮的事情吗?Notification 回调被广泛的运用于antimalware 解决方案中,当然也是很容易受到攻击的。Malware 能够在几步中就不预先通知的拜访和移除notifications 或者修补他们,或者调用他们或者修补在 antivirus驱动中的真正函数。相信这些通报简直就是没意义的。Configuration manager 回调在一些地方会受到愚弄。更进一步的说,他们更是被malware  使用而不是合法软件。哈哈,这些在Vista中都改变了。
你相信 flt filters吗?就让我们这样认为吧,在那里存在了可使用的file system parsers(在用户模式与内核模式中)。

2 Rustock 的新特点,集合的更多信息,制作检测工具

我们不会深入到细节中,但是现在就讲一些rootkit的新知识等等。
在单处理器与多处理器中Rustock的行为是不一样的。
它tcpip.sys中设置了更多的HOOKS,就像下面的方式一样,很多在tcpip.sys 的内部调用看起来都是这种形式:
call [F6633220] (例子)
或者
call [83882200] (例子)
Rustock 以下面的方法修补这些指令:用rootkit 处理函数的地址代替tcpip.sys 的地址。这个不是单纯的INLINE 

HOOK,但是地址却改变钩住了。这真的是个好想法,因为这仅仅是四字节的改变,同时大部分antirootkit 都不能够跟踪到它的修改,因为它的实现比较特殊。它从修改的地方开始分析并且以一条指令转换地址,当然就是跳到自己的HOOKS中。

HOOKS的观察
其中一个检测这些狡猾的修改的方法是----从我们开始实时反汇编与分析的地方做一个映射。因为静态分析对当前的难题没有什么用。那么我们从IDA哪里得到所要映射的地方去反汇编呢?答案是简单的。首先是从驱动的入口。第二:导出表。我们也可以从驱动入口得到原始IRP处理函数。(他们大部分被分派在那里)。我们能够使用导入表和发现在代码中所有涉及到的函数。最根本的但不大容易完成的方法是:在代码内部搜索调用和分析所有种类的跳转指令。在做映射的时候我们经常会跟踪代码,当发现与原代码错配时----我们会搜索最接近映射的那个点和开始反汇编程序(我们会动态分析代码和跟踪钩子)。因为rootkits 的制作者能够做到跳回原来被调用的函数地址。理论上我们依靠跟踪钩子能够发现被隐藏的内核模块,但是这些都不是必须的,我们仅仅只要发现HOOS就可以了。最重要的一件事情是----我们必须选择UNHOOK的原文件,最好的方法就是有一个system components的数据库。但是数据库将会很大,操作又随系统而定的,因而我们简单的读原内容从我们的低层IO派遣驱动程序(这个不会使用到WINDOWS API和FS drivers 队列)。但是这个会给我们带来很多的兼容性问题(在不同的机子上和动态磁盘)。最后完成了,就像antirootkit 所称的“Resurrector”---分析HOOKS和找到真正的处理函数。

Rustock当然也有可能以同样的方式HOOK  pIofCallDriver和设置两个在IAT拦截wanarp.sys - 

NdisOpenAdapter/NdisCloseAdapter ,就像Rustock.B hooks. 使用自己实现的通过 IofCallDriver hook 。

IofCallDriver(
    IN PDEVICE_OBJECT  DeviceObject,
    IN OUT PIRP  Irp
    )
{    
//….variables stack location checking etc  use your brains
//….Blah blah blah blah ….

Proc1 = (PHANDLER_PROC)DeviceObject->DriverObject->MajorFunction[v3->MajorFunction];
return Proc1(DeviceObject, Irp);
}

Rustock 也HOOKS了几个IDT入口(除了KiSystemService),但是这个没有得到公认,所以这部分就不检测了。
看看 pe386 的版本 buggy(先前的Rustock.B行为真的让人有点困惑,他依靠不同的硬件认证),它用INLINEHOOK对ntfs.sys,, fastfat.sys 过滤FSD 请求。
但是这里有一个BUG,在单处理器系统中它不能过滤一些信息,所以我们得到相等的文件大小在未被处理过的模式下和通过API的枚举。这很疯狂吗?假如你对null.sys (infected driver)调用GetFileSize ,它会返回一个假的文件大小。假如你调用NtQueryInformationFile,以FILE_STANDARD_INFORMATION 为参数,同样也会得到假的文件大小,但是这个 AllocationSize 域能得到真实的感染文件的大小。在FAT32 systems,Rustock过滤有更多的影响。 GMER v1.14 不能看到任何的HOOKS----因为它从磁盘中加载了假的映像。但是我们的文件请求试验被展现变态的过滤行为----它返回了AllocationSize= INFINITE或者为0。这是由于疯狂的广告信息,我们认为----它有必要创建一个特别的程序去检查驱动目录中的文件,因为以不同的检测重新得到所有文件卷的大小将会动态减少执行和增加错误的检测器数目。你能够在用户态中检测出错配的文件大小。
Rustock 在winlogon.exe 隐藏两个线程和在相同的winlogon.exe 中有两个句柄,两个在csrss.exe ,这些是在NtQuerySystemInformation过滤帮助下实现的。
这是非常容易的去绕过KiSystemService hook 。你可能知道内核模式的Zw* 函数的调用过程,给个例子:



.text:0042997C ; NTSTATUS __stdcall ZwQueryKey(HANDLE KeyHandle, KEY_INFORMATION_CLASS 

KeyInformationClass, PVOID KeyInformation, ULONG KeyInformationLength, PULONG ResultLength)
.text:0042997C                 public ZwQueryKey
.text:0042997C ZwQueryKey      proc near               ; CODE XREF: sub_4A5CFC+13 p
.text:0042997C                                         ; sub_4A5CFC+51p ...
.text:0042997C
.text:0042997C KeyHandle       = dword ptr  4
.text:0042997C KeyInformationClass= dword ptr  8
.text:0042997C KeyInformation  = dword ptr  0Ch
.text:0042997C KeyInformationLength= dword ptr  10h
.text:0042997C ResultLength    = dword ptr  14h
.text:0042997C
.text:0042997C                 mov     eax, 0A0h
.text:00429981                 lea     edx, [esp+KeyHandle]
.text:00429985                 pushf
.text:00429986                 push    8
.text:00429988                 call    KiSystemService
.text:0042998D                 retn    14h
.text:0042998D ZwQueryKey      endp


所以Rustock能够过滤从用户态来的NT,内核态的ZW函数。因为 KiSystemService 被HOOK了。要绕过它必须从磁盘加载干净的ntoskrnl ,去做这些未处理的模式只是为了不在重复 Gmerek 的错误,重新认证它和使用原始函数。这些是相同的能够用在绕过所有的 HIPS hooks 和其他内核模式的HOOKING,极端的方法----在新加载的核心中在分配你的代码(依靠修补调用函数的地址)。这需要很多的工作,但是这是可能的。
毕竟我们能发现所有的HOOKS,发现一些感染的文件和在用户态中做这些事。我们能够做一个纯用户态的检测器来发现

这些HOOKS等等。。。。

 Rustock.C中有名的保护(略)

3 Rootkit roadmap
将来还会出现什么?Rustock.D?。。。。。。