Skip to content

cstom4994/MemoryModuleNativeAOT

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

MemoryModuleNativeAOT

MemoryModuleNativeAOT is an in-memory loader for .NET NativeAOT PE binaries on Windows based on NativeAOT-RunPE.

It targets NativeAOT executables and DLLs that cannot be loaded with Assembly.Load, and that typically crash when used with traditional PE memory loaders such as MemoryModule, Donut, sRDI, or pe_to_shellcode.

Features

  • Load .NET NativeAOT EXE and DLL binaries directly from memory
  • Expose a MemoryModule-style C API
  • Handle loader structures required by the NativeAOT runtime
  • Register static TLS and unwind metadata
  • Resolve exports from in-memory NativeAOT DLL images
  • Support unloading with cleanup logic, including tracked TLS and FLS slot release

Why This Exists

NativeAOT binaries include their own runtime and rely on Windows loader internals that ordinary manual mapping implementations do not reproduce.

In practice, the runtime expects:

  • a valid loader entry in the PEB loader state
  • membership in the loader's BaseAddressIndex tree
  • proper static TLS registration
  • valid exception/unwind metadata registration

Without those pieces, NativeAOT startup typically ends in STATUS_FAIL_FAST_EXCEPTION (0xc0000602).

This project implements the missing loader-side behavior needed to make NativeAOT images initialize successfully from memory.

How It Works

At a high level, the loader:

  1. Maps PE sections, applies relocations, and resolves imports
  2. Creates and links a synthetic LDR_DATA_TABLE_ENTRY
  3. Inserts the module into the loader's BaseAddressIndex red-black tree
  4. Registers TLS using LdrpHandleTlsData
  5. Hooks TlsAlloc/TlsFree and FlsAlloc/FlsFree imports so runtime-owned slots can be tracked
  6. Registers exception handling tables with RtlAddFunctionTable
  7. Applies final section protections
  8. Calls the EXE entry point or DllMain
  9. Resolves exports from the in-memory module

On unload, it releases tracked TLS and FLS slots, detaches TLS, unregisters unwind metadata, calls DllMain(DLL_PROCESS_DETACH) when needed, and removes the synthetic loader entry.

Build

cmake -S . -B build
cmake --build build --config Release

Usage

test.exe /path/to/test.dll
test.exe /path/to/test.dll --get-proc Add
test.exe /path/to/test.dll --call-int32 GetTlsValue
test.exe /path/to/test.dll --call-int32x2 Add 7 5
test.exe /path/to/test.dll --free

API

The public API is declared in src/memory_nativeaot.h:

  • MemoryNativeAOTLoadLibrary(const void* data, size_t size)
  • MemoryNativeAOTLoadLibraryEx(const void* data, size_t size, const char* module_name_hint)
  • MemoryNativeAOTLoadLibraryFromFileA(const char* path)
  • MemoryNativeAOTGetProcAddress(MemoryModuleNativeAOT* module, const char* export_name)
  • MemoryNativeAOTGetModuleBase(MemoryModuleNativeAOT* module)
  • MemoryNativeAOTFreeLibrary(MemoryModuleNativeAOT* module)

If you unload a NativeAOT DLL after calling managed exports, ensure those exports ran on worker threads that have already exited before MemoryNativeAOTFreeLibrary. Otherwise the current thread may still hold runtime or TLS state during teardown.

Test Payloads

Build the sample NativeAOT payloads with the .NET 8+ SDK:

cd src
dotnet publish -c Release -r win-x64

The sample DLL exports:

  • Add(int, int)
  • GetTlsValue()
  • TouchRuntime()

Compatibility

  • Windows 10 1903+ / Windows 11
  • x64 only
  • C++17 with a recent MSVC-compatible toolchain
  • Target binaries: .NET 7+ NativeAOT (PublishAot=true)

References

About

In-memory loader for .NET NativeAOT PE binaries on Windows

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors