
Reflektierender PE-Mapper und evasionsgehärtete Module-Stomping-Engine für Windows x64
Ein minimaler reflektiver PE-Mapper und Module-Stomping-Engine für Windows x64, aufgebaut um eine saubere IPlugin-ABI. Mappt eine DLL aus einem Byte-Buffer im Speicher, löst Exporte auf und ruft Plugin-Funktionen auf — entweder aus anonymem Speicher (klassisches reflektives Laden) oder aus dem Inneren einer legitim geladenen Opfer-DLL (Module Stomping).
Der Module-Stomping-Pfad ist evasion-gehärtet: Abschnittsweise Schutzmechanismen (kein RWX), Bereinigung privater Mappings, Wiederherstellung der Originalbytes und Heap-Bereinigung. Post-Cleanup-Scans mit pe-sieve, Moneta, hollows_hunter, BeaconEye und YARA liefern null Funde im gestompften Modul.
Erfordert CMake 3.15+ und entweder MSVC, Clang oder MinGW unter Windows.
# Configure (defaults to Release)
cmake -B build -G Ninja
# Build
cmake --build build
# The build produces:
# build/Modules/CmdPlugin/cmdplugin.dll (example plugin)
# build/Testing/harness.exe (test harness)
Für einen Debug-Build mit vollständiger Diagnoseausgabe:
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build
.\harness.exe cmdplugin.dll whoami
Mappt das Plugin in VirtualAlloc'ten Speicher. Kein PEB-Eintrag, keine Loader-Benachrichtigung.
.\harness.exe cmdplugin.dll whoami --stomp xpsservices.dll
.\harness.exe cmdplugin.dll "dir C:\" --stomp mscms.dll
Lädt die Opfer-DLL legitim über LoadLibrary und überschreibt anschließend deren .text mit dem Plugin als zusammenhängendes Abbild. Die Ausführung erfolgt aus dem Adressraum der Opfer-DLL — Call Stacks und Modullisten ordnen den Code dem Opfermodul zu.
Die Harness pausiert nach der Bereinigung, damit Sie Speicherscanner gegen den Prozess ausführen können.
1. MapImage(plugin) private reflective mapping
2. LoadLibrary(sacrificial) legitimate load, PEB entry
3. Save original .text bytes for later restoration
4. Copy plugin image -> .text contiguous image preserves RIP-relative offsets
5. Relocate stomped code fix addresses for new base
6. Patch entry point mov eax,1; ret (safe FreeLibrary)
7. Per-section protections RX / R / RW per section (never RWX)
8. Resolve exports from private mapping, return stomped addresses
9. Free private mapping eliminates floating PE artifact
10. Execute plugin code runs inside "sacrificial.dll"
11. Restore original .text bytes sacrificial code back in place
12. FreeLibrary(sacrificial) unloads the module
13. LoadLibrary(sacrificial) fresh load = clean file-backed pages
14. Scrub heap buffers zero saved bytes + raw DLL data
Nach der Bereinigung ist die Opfer-DLL nicht mehr von einem normal geladenen Modul zu unterscheiden. Die Seiten sind dateigestützt (nicht privat), der Inhalt stimmt mit der Datei auf der Festplatte überein, und es verbleiben keine PE-Artefakte auf dem Heap.
| IOC | Gegenmaßnahme |
|---|---|
| RWX-Speicherbereiche | Schutz pro Abschnitt: .text RX, .rdata R, .data RW |
| Privater ausführbarer Speicher (schwebende PE) | VirtualFree des privaten Mappings nach der Exportauflösung |
| Modifizierter Modulcode | Ursprüngliche .text-Bytes wiederherstellen, dann FreeLibrary + LoadLibrary für saubere dateigestützte Seiten |
| PE-Header auf dem Heap | memset(0) des rohen DLL-Puffers und der gespeicherten Bytes vor der Freigabe |
| DllMain-Absturz beim Entladen | Einstiegspunkt vor dem Überschreiben auf mov eax,1; ret gepatcht |
ReflectivePluginLoader/
├── CMakeLists.txt
├── Include/
│ ├── IPlugin.h Plugin ABI (TaskApi, IPlugin)
│ ├── ReflectiveLoaderEngine.h PE mapper + export resolver
│ └── StompedLoaderEngine.h Module stomping engine
├── Modules/
│ └── CmdPlugin/
│ ├── cmdplugin.cpp Command execution plugin
│ └── CMakeLists.txt
├── Testing/
│ ├── main.cpp Test harness
│ └── CMakeLists.txt
└── Tools/
└── file2hex.py DLL to C byte array converter
Modules/ und binde IPlugin.h ein.IPlugin-Interface:#include "IPlugin.h"
class MyPlugin : public IPlugin {
public:
void init() const override { /* setup */ }
void execute(TaskApi* task) const override { /* work */ }
void cleanup() const override { /* tear */ }
};
Exportieren Sie die fünf Einstiegspunkte (create_plugin, destroy_plugin, plugin_init, plugin_exec, plugin_cleanup). Verwenden Sie HeapAlloc + Placement new für CRT-sichere Allokation.
Fügen Sie eine CMakeLists.txt hinzu und registrieren Sie sie im Root mit add_subdirectory().
Der .text-Abschnitt der Opfer-DLL muss größer sein als die virtuelle Image-Größe des Plugins. Sie sollte nicht aktiv vom Prozess verwendet oder zur Laufzeit auf Integrität geprüft werden.
Gute Kandidaten: xpsservices.dll (1,8 MB .text), mscms.dll, colorui.dll, npmproxy.dll
Schlechte Kandidaten: ntdll.dll, kernel32.dll (aktiv verwendet), clr.dll (integritätsgeprüft)
.text-Abschnitt passenDieses Release ist eine komplette Neufassung des ursprünglichen Proof-of-Concept:
StompedLoaderEngine.h) — zusammenhängendes Image-Stomping mit vollständiger Evasion-Bereinigung.text RX, .rdata R, .data RW; keine RWX-Regionen mehrFreeLibrary + LoadLibrary-Zyklus ersetzt privatisierte Seiten durch saubere dateigestützte.text werden vor dem Scan-Fenster genulltmov eax,1; ret verhindert DllMain-Absturz während FreeLibraryVirtualFree des reflektiven Mappings nach Exportauflösung (eliminiert schwebende PE)ReflectiveLoaderEngine.h) — direktes Mapping aus dem Eingabepuffer, keine zwischengeschaltete VirtualAlloc-KopieIPlugin.h) — tote Hilfsfunktionen entfernt (BeaconCout, CStrLenA, PluginAlloc, PluginFree)main.cpp) — bedingte Kompilierung über NDEBUG; Release-Builds zeigen nur Befehlsausgabe und Scan-Befehle-nostartfiles für Plugin-DLLs (kein CRT in reflektiv geladenem Code)