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.
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.
Why care about passing an invalid handle instead of a handle to a file? because we will use this API later on to create a mapping object that points to a memory region (shellcode) instead of a file on disk.
Setting INVALID_HANDLE_VALUE
flag allows the function to perform its task without using a file from disk, and instead the file mapping object is created in memory with a size specified by the dwMaximumSizeHigh
or dwMaximumSizeLow
parameters.
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.
This flag doesn't allocate a RWX section, it just specifies that an RWX page can be created later on. if we use PAGE_READWRITE
instead of PAGE_EXECUTE_READWRITE
, the content wont be executable later on.
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.
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.
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
MapViewOfFile
returns a pointer that points to the start address of mapped memory, so we can use multiple execution techniques like passing this address to a new local thread or using callback functions.
Code:
Code Samples
Code snippets are available on GitHub:
Last updated
Was this helpful?