Map file to process memory utilizing NtCreateSection and NtMapViewOfSection

In order to bypass Endpoint Detection and Response (EDR) as well as Anti-Virus (AV) software that hook Windows APIs on the user-mode level, a technique commonly utilized is to go a level deeper and make system calls (syscalls) directly. This post is an attempt to document some of the steps required to dynamically resolve system calls from ntdll.dll.

Act 1

The following code maps a file - that exists on the system - into the memory of the calling process. The file is mapped in a shareable memory section utilizing NtCreateSection and NtMapViewOfSection Windows APIs. That means that any process that maps this memory region into its address space will get access to this memory region.

#include <windows.h>

typedef struct _UNICODE_STRING {
	USHORT Length;
	USHORT MaximumLength;
	PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
	ULONG           Length;
	HANDLE          RootDirectory;
	PUNICODE_STRING ObjectName;
	ULONG           Attributes;
	PVOID           SecurityDescriptor;
	PVOID           SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef enum _SECTION_INHERIT {
	ViewShare = 1,
	ViewUnmap = 2
} SECTION_INHERIT, *PSECTION_INHERIT;

typedef NTSTATUS (*fnNtCreateSection)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PLARGE_INTEGER, ULONG, ULONG, HANDLE);
typedef NTSTATUS (*fnNtMapViewOfSection)(HANDLE, HANDLE, PVOID*, ULONG_PTR, SIZE_T, PLARGE_INTEGER, PSIZE_T, SECTION_INHERIT, ULONG, ULONG);

INT ModuleMapper(CHAR* modpath)
{
	OFSTRUCT fstruct = { 0 };
	HFILE hFile = ::OpenFile(modpath, &fstruct, OF_READ);
	if (hFile == 0)
	{
		::wprintf(L"[-] OpenFile has failed: %d\n", ::GetLastError());
		return 0;
	}

	DWORD FileSize = ::GetFileSize((HANDLE)hFile, NULL);
	if (FileSize == 0)
	{
		::wprintf(L"[-] GetFileSize has failed: %x\n", FileSize);
		return 0;
	}

	HMODULE hModule = GetModuleHandleWrapper(modpath);
	fnNtCreateSection NtCreateSection = (fnNtCreateSection)GetProcAddress(hModule, "NtCreateSection");
	if (hModule == NULL)
	{
		::wprintf(L"[-] GetProcAddress for NtCreateSection has failed\n");
		return 0;
	}

	HANDLE SectionHandle = 0;
	LARGE_INTEGER MaxSize = { 0, 0 };
	MaxSize.LowPart = FileSize;
	NTSTATUS CreateSectionStatus = NtCreateSection(&SectionHandle, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL, &MaxSize, PAGE_READWRITE, SEC_COMMIT, NULL);
	if (CreateSectionStatus != 0)
	{
		::wprintf(L"[-] NtCreateSection has failed: %x\n", CreateSectionStatus);
		return 0;
	}

	fnNtMapViewOfSection NtMapViewOfSection = (fnNtMapViewOfSection)GetProcAddress(hModule, "NtMapViewOfSection");
	if (hModule == NULL)
	{
		::wprintf(L"[-] GetProcAddress for NtMapViewOfSection has failed\n");
		return 0;
	}

	PVOID Address = NULL;
	SIZE_T SectionSize = 0;
	NTSTATUS MapViewOfSectionStatus = NtMapViewOfSection(
								SectionHandle,
								GetCurrentProcess(),
								&Address,
								NULL,
								NULL,
								NULL,
								&SectionSize,
								ViewUnmap,
								NULL,
								PAGE_READWRITE
							    );

	if (MapViewOfSectionStatus != 0)
	{
		::wprintf(L"[-] NtMapViewOfSection has failed: %x\n", MapViewOfSectionStatus);
		return 0;
	}
	
	// copy file to the allocated shared memory
	BOOL ReadStatus = ::ReadFile((HANDLE)hFile, Address, FileSize, NULL, NULL);
	if (ReadStatus == FALSE) {
		::wprintf(L"[-] ReadFile has failed: %d\n", ::GetLastError());
		return 0;
	}

	return 1;
}

tags: #Windows API