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* orginal_api;
}
}


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