Skip to main content

Overview

The allocator::section policy allocates memory for syscall stubs using NtCreateSection with the SEC_NO_CHANGE flag. This creates an immutable memory region that cannot be modified after initial mapping, providing strong protection against runtime patching.

Method Signatures

allocate
static bool
Allocates an immutable section for syscall stubs
static bool allocate(
    size_t uRegionSize,
    const std::span<const uint8_t> vecBuffer,
    void*& pOutRegion,
    HANDLE& unused
)
return
bool
Returns true on successful allocation, false on failure
release
static void
Releases the section mapping
static void release(void* pRegion, HANDLE /*hHeapHandle*/)

Implementation Details

The allocation process involves multiple steps:
  1. Create a section object with SEC_NO_CHANGE | SEC_COMMIT attributes
  2. Map the section with PAGE_READWRITE permissions
  3. Copy the syscall stubs into the writable mapping
  4. Unmap the writable view
  5. Remap the section with PAGE_EXECUTE_READ permissions
  6. Close the section handle
The SEC_NO_CHANGE flag is the key security feature—it prevents any further modifications to the section, even from kernel mode.

Source Code

From syscall.hpp:115-165:
struct section
{
    static bool allocate(size_t uRegionSize, const std::span<const uint8_t> vecBuffer, 
                        void*& pOutRegion, HANDLE& /*unused*/)
    {
        HMODULE hNtDll = native::getModuleBase(hashing::calculateHash("ntdll.dll"));

        auto fNtCreateSection = reinterpret_cast<native::NtCreateSection_t>(
            native::getExportAddress(hNtDll, SYSCALL_ID("NtCreateSection")));
        auto fNtMapView = reinterpret_cast<native::NtMapViewOfSection_t>(
            native::getExportAddress(hNtDll, SYSCALL_ID("NtMapViewOfSection")));
        auto fNtUnmapView = reinterpret_cast<native::NtUnmapViewOfSection_t>(
            native::getExportAddress(hNtDll, SYSCALL_ID("NtUnmapViewOfSection")));
        auto fNtClose = reinterpret_cast<native::NtClose_t>(
            native::getExportAddress(hNtDll, SYSCALL_ID("NtClose")));
            
        if (!fNtCreateSection || !fNtMapView || !fNtUnmapView || !fNtClose)
            return false;

        HANDLE hSectionHandle = nullptr;
        LARGE_INTEGER sectionSize;
        sectionSize.QuadPart = uRegionSize;

        NTSTATUS status = fNtCreateSection(
            &hSectionHandle, SECTION_ALL_ACCESS, nullptr, &sectionSize,
            PAGE_EXECUTE_READWRITE, 
            SEC_COMMIT | static_cast<ULONG>(SECTION_NO_CHANGE), 
            nullptr
        );
        if (!NT_SUCCESS(status))
            return false;

        // Map as writable, copy data, remap as executable
        void* pTempView = nullptr;
        SIZE_T uViewSize = uRegionSize;
        status = fNtMapView(hSectionHandle, native::getCurrentProcess(), 
                           &pTempView, 0, 0, nullptr, &uViewSize, 
                           VIEW_SHARE, 0, PAGE_READWRITE);
        if (!NT_SUCCESS(status)) {
            fNtClose(hSectionHandle);
            return false;
        }

        std::copy_n(vecBuffer.data(), uRegionSize, static_cast<uint8_t*>(pTempView));
        fNtUnmapView(native::getCurrentProcess(), pTempView);
        
        uViewSize = uRegionSize;
        status = fNtMapView(hSectionHandle, native::getCurrentProcess(), 
                           &pOutRegion, 0, 0, nullptr, &uViewSize,
                           VIEW_SHARE, 0, PAGE_EXECUTE_READ);
        fNtClose(hSectionHandle);
        return NT_SUCCESS(status) && pOutRegion;
    }

    static void release(void* pRegion, HANDLE /*hHeapHandle*/)
    {
        if (pRegion) {
            HMODULE hNtDll = native::getModuleBase(hashing::calculateHash("ntdll.dll"));
            auto fNtUnmapView = reinterpret_cast<native::NtUnmapViewOfSection_t>(
                native::getExportAddress(hNtDll, SYSCALL_ID("NtUnmapViewOfSection")));
            if (fNtUnmapView)
                fNtUnmapView(native::getCurrentProcess(), pRegion);
        }
    }
};

Usage Example

#include <syscalls-cpp/syscall.hpp>

// Use the pre-defined type alias
SyscallSectionDirect syscallManager;
if (!syscallManager.initialize()) {
    std::cerr << "Failed to initialize with section allocator\n";
    return 1;
}

// Or compose manually
using CustomManager = syscall::Manager<
    syscall::policies::allocator::section,
    syscall::policies::generator::direct
>;

CustomManager manager;
manager.initialize();

Security Properties

Immutability

Once mapped as executable, the section cannot be modified—not even by kernel-mode code. This prevents runtime patching of syscall stubs.

Protection Demo

Attempts to patch syscalls in SEC_NO_CHANGE sections fail, as demonstrated in the library’s protection demo.

Trade-offs

AspectDescription
SecurityHighest—immutable after mapping
PerformanceSlightly slower allocation (multiple map operations)
ComplexityMost complex allocation process
CompatibilityRequires Windows NT 6.0+

See Also

allocator::heap

Heap-based allocation alternative

allocator::memory

Virtual memory allocation alternative

Policy Composition

Learn how to combine policies

Build docs developers (and LLMs) love