• Uroburos Rootkit样本简单分析


     1.首先拿到样本,载入ida发现有一个类似于压缩壳解码的函数,跳过后dump出pe,修复对齐和section header。

     2.该样本会检查是否运行在wow64环境,检测名为Ultra3的服务和一系列事件来确保是第一次启动。

     

     

    3.随后该样本会内存加载资源段的一个pe文件尝试利用CVE-2009-1123执行特权代码

     

     4.如果该漏洞利用成功将直接内存加载驱动文件,然后退出该样本。否则将会尝试利用CVE-2010-0232进行权限提升,该样本通过尝试改写注册表currentversion字段来判断是否权限提升成功。如果提权失败则返回错误代码退出样本。如果该样本运行在vista以上版本并且是64位系统,则会尝试通过virtualbox的驱动漏洞关闭dse。然后打开加载驱动权限,手动填写注册表的服务,通过ZwLoadDriver加载驱动。

     

    5.该驱动会解密自身的代码,然后重新改写pe的入口,我们可以在解码完后用windbg dump出来然后修复。

     

     5.修复完后该样本就是安全客那篇文章最后dump出来的样本(https://www.anquanke.com/post/id/189549)

     6.该样本在驱动入口首先创建了一个全局的结构体储存一些信息,随后创建之前在应用层检测的全局事件。

    struct
    {
        int  unknown; //0xffffffff
        int* DriverStart;
        int  DriverSize;
        int* ntoskrl_base;
        int  ntoskrl_size;
        int  process_id;
    }

     7.然后把自身的整个pe映像复制到了一块新申请的内存并做了重定位后重新执行驱动入口,然后执行关键代码。

     1 unsigned int __stdcall fix_reloc(int a1, int a2)
     2 {
     3   unsigned int result; // eax
     4   int TypeOffset; // [esp+4h] [ebp-34h]
     5   _DWORD *i; // [esp+Ch] [ebp-2Ch]
     6   int v5; // [esp+14h] [ebp-24h]
     7   int v6; // [esp+1Ch] [ebp-1Ch]
     8   int v7; // [esp+20h] [ebp-18h]
     9   unsigned __int16 v8; // [esp+24h] [ebp-14h]
    10   unsigned int v9; // [esp+28h] [ebp-10h]
    11   int v10; // [esp+2Ch] [ebp-Ch]
    12   int offset; // [esp+34h] [ebp-4h]
    13 
    14   v6 = *(_DWORD *)(a1 + 0x3C) + a1;
    15   if ( *(_WORD *)(v6 + 0x18) == 0x10B )
    16   {
    17     v5 = *(_DWORD *)(v6 + 0xA0);                // image_directory_entry_basereloc.VirtualAddress
    18     v7 = a2 - *(_DWORD *)(v6 + 0x34);
    19     v10 = *(_DWORD *)(v6 + 0x70);
    20   }
    21   else
    22   {
    23     v5 = *(_DWORD *)(v6 + 0xB0);
    24     v7 = a2 - *(_DWORD *)(v6 + 0x30);
    25     v10 = *(_DWORD *)(v6 + 0x80);
    26   }
    27   if ( !v7 )
    28     return 0;
    29   if ( v5 )
    30   {
    31     for ( i = (_DWORD *)(v5 + a1); i[1]; i = (_DWORD *)(v5 + a1) )// 修复重定位
    32     {
    33       TypeOffset = v5 + a1 + 8;
    34       v8 = (unsigned int)(i[1] - 8) >> 1;       // (IMAGE_BASE_RELOCATION.SizeOfBlock 
    35                                                 // -sizeof(IMAGE_BASE_RELOCATION.SizeOfBlock))
    36                                                 // /sizeof(TypeOffset)
    37       v9 = 0;                                   // 计算重定位条目的个数
    38       while ( v9 < v8 )
    39       {
    40         offset = *i + (*(_WORD *)(TypeOffset + 2 * v9) & 0xFFF);// 待修复地址
    41         switch ( (signed int)*(unsigned __int16 *)(TypeOffset + 2 * v9) >> 0xC )// Based relocation types
    42         {
    43           case 0:
    44             goto LABEL_13;
    45           case 1:
    46             *(_WORD *)(offset + a1) += HIWORD(v7);
    47             goto LABEL_13;
    48           case 2:
    49             *(_WORD *)(offset + a1) += v7;
    50             goto LABEL_13;
    51           case 3:
    52             *(_DWORD *)(offset + a1) += v7;     // 重定位之后的值 = 需要进行重定位的地址值 - IMAGE_OPTINAL_HEADER中的基址 + 实际基址
    53             goto LABEL_13;
    54           case 4:
    55             return 0x21590064;
    56           case 5:
    57           case 6:
    58           case 7:
    59           case 8:
    60           case 9:
    61             return 0x21590064;
    62           case 0xA:
    63             *(_QWORD *)(offset + a1) += (unsigned int)v7;
    64 LABEL_13:
    65             ++v9;
    66             break;
    67         }
    68       }
    69       v5 += i[1];
    70     }
    71     result = 0;
    72   }
    73   else if ( v10 & 1 )
    74   {
    75     result = 0xFFFFFFFF;
    76   }
    77   else
    78   {
    79     result = 0;
    80   }
    81   return result;
    82 }

    8.内核重载,然后根据内核加载基址重定位全局变量。然后将自己加载内核中的ssdt内容复制到申请的内存池中确保后面hook api时取到的是正确的地址。

      1 int __stdcall memLoad_fixRelocate(int a1, wchar_t *ntoskrl_path, int ntoskrl_base, int a4, int a5)
      2 {
      3   unsigned int v6; // eax
      4   char v7[5]; // [esp+37h] [ebp-19h]
      5   HANDLE ProcessHandle; // [esp+3Ch] [ebp-14h]
      6   PVOID Buffer; // [esp+40h] [ebp-10h]
      7   ULONG Length; // [esp+44h] [ebp-Ch]
      8   HANDLE Handle; // [esp+48h] [ebp-8h]
      9   PVOID P; // [esp+4Ch] [ebp-4h]
     10 
     11   P = 0;
     12   ProcessHandle = 0;
     13   Buffer = 0;
     14   *(_DWORD *)a4 = 0;
     15   Handle = IoCreateFile_(ntoskrl_path, 0x8000); // open ntoskrnl
     16   if ( Handle != (HANDLE)0xFFFFFFFF )
     17     goto LABEL_15;
     18   P = alloc_pool(0x208u);
     19   if ( !P )
     20     return 0x21590004;
     21   *(_DWORD *)&v7[1] = sub_86911830((wchar_t *)P, 0x104u);
     22   if ( !*(_DWORD *)&v7[1] )
     23   {
     24     if ( a1 )
     25     {
     26       *(_DWORD *)&v7[1] = sub_8693A3E0(&ProcessHandle, 0x400u, a1);
     27       if ( *(_DWORD *)&v7[1] )
     28         goto LABEL_30;
     29       *(_DWORD *)&v7[1] = sub_8693A100((HANDLE)0xFFFFFFFF, (int)v7);
     30       if ( *(_DWORD *)&v7[1] )
     31         goto LABEL_30;
     32     }
     33     else
     34     {
     35       v7[0] = 0;
     36     }
     37     if ( v7[0] )
     38       wcsncat((wchar_t *)P, L"\SysWOW64\", 0x104 - wcslen((const unsigned __int16 *)P));
     39     else
     40       wcsncat((wchar_t *)P, L"\System32\", 0x104 - wcslen((const unsigned __int16 *)P));
     41     wcsncat((wchar_t *)P, ntoskrl_path, 0x104 - wcslen((const unsigned __int16 *)P));
     42     *((_WORD *)P + 0x103) = 0;
     43     Handle = IoCreateFile_((wchar_t *)P, 0);
     44     if ( Handle == (HANDLE)0xFFFFFFFF )
     45     {
     46       *(_DWORD *)&v7[1] = 0x21590005;
     47       goto LABEL_30;
     48     }
     49 LABEL_15:
     50     Length = sub_86912690(Handle);              // 查询文件长度
     51     if ( Length == 0xFFFFFFFF )
     52     {
     53       *(_DWORD *)&v7[1] = 0xFFFFFFFF;
     54     }
     55     else
     56     {
     57       Buffer = alloc_pool(Length);
     58       if ( Buffer )
     59       {
     60         if ( readfile(Handle, Buffer, Length) == 0xFFFFFFFF )
     61         {
     62           *(_DWORD *)&v7[1] = 0xFFFFFFFF;
     63         }
     64         else
     65         {
     66           Length = calc_pe_size_in_memory((int)Buffer);// 计算在内存中展开大小
     67           *(_DWORD *)a4 = alloc_pool(Length);
     68           if ( *(_DWORD *)a4 )
     69           {
     70             *(_DWORD *)&v7[1] = load_pe_in_memory(*(_DWORD *)a4, (int)Buffer);// 内存加载ntoskrnl
     71             if ( !*(_DWORD *)&v7[1] )
     72             {
     73               v6 = ntoskrl_base ? fix_reloc(*(_DWORD *)a4, ntoskrl_base) : fix_reloc(*(_DWORD *)a4, *(_DWORD *)a4);// 用nt内核文件的加载基址来进行重定位修复,应该是准备内核重载
     74               *(_DWORD *)&v7[1] = v6;           // 修正新加载内核中的全局变量
     75               if ( !v6 )
     76               {
     77                 if ( a5 )
     78                   *(_DWORD *)a5 = Length;
     79               }
     80             }
     81           }
     82           else
     83           {
     84             *(_DWORD *)&v7[1] = 0x21590004;
     85           }
     86         }
     87       }
     88       else
     89       {
     90         *(_DWORD *)&v7[1] = 0x21590004;
     91       }
     92     }
     93   }
     94 LABEL_30:
     95   if ( *(_DWORD *)&v7[1] )
     96   {
     97     free_pool(*(PVOID *)a4);
     98     *(_DWORD *)a4 = 0;
     99   }
    100   free_pool(Buffer);
    101   sub_8693A460(ProcessHandle);
    102   free_pool(P);
    103   if ( Handle != (HANDLE)0xFFFFFFFF )
    104     sub_86912590(Handle);
    105   return *(_DWORD *)&v7[1];
    106 }

    9.hook 0xC3号中断,后面inline hook 时用中断进行服务分发,利用dpc将线程跑在指定cpu上来读取多核idtr。

     

     

     

     

     9.接着有类似于修复无模块seh的处理,这块我不太懂,如果有大佬知道的,可以告诉我。

     

     0xA.接着保存了一个要hook函数信息的结构体,这块没有仔细阅读代码,不过猜测应该是利用了反汇编引擎动态分析的函数头部需要hook的字节数,同时生成了要hook代码。

    struct
    {
      void* api_info; --->
                        {
                         void*  new_api;
                         int    hook_length;
                         void*  original_api;
                        }
    }

     

    0xB.这些api具体hook的函数代码没有继续分析了,这个样本一直零零散散搞了这么久,感觉没必要在分析下去了。需要花点时间看看书了。

    这里附上这个样本的下载地址。https://files.cnblogs.com/files/DreamoneOnly/Uroburos.7z

  • 相关阅读:
    RabbitMQ消费端自定义监听器DefaultConsumer
    RabbitMQ exchange交换机类型
    RabbitMQ 快速入门
    chrome jsonView插件安装
    谈谈令人头大的prototype 和__proto__
    原生JS写一个淡入淡出轮播图
    模拟聊天对话框
    全选反选的小案例
    原生js做一个简单的进度条
    点击回到顶部的按钮
  • 原文地址:https://www.cnblogs.com/DreamoneOnly/p/11995391.html
Copyright © 2020-2023  润新知