
ReflectivePluginLoader is a minimal C++ PE mapper for Windows x64 that loads DLLs from an in-memory byte buffer and dispatches work through a clean IPlugin interface, intended for authorized research and red-team education.
| Tool | racoten/ReflectivePluginLoader — minimal reflective PE mapper with a hot-swappable IPlugin ABI for Windows x64 |
| Category | Windows internals / memory loading library (C++, CMake) |
| Primary Use | Learning and prototyping modular plugin architectures where DLLs are mapped from a byte buffer via a custom PE mapper instead of LoadLibrary |
| Safe Use | Educational companion code for red-team development study; use in isolated labs, your own test harnesses, and authorized engagement research only |
| Telemetry Note | Because modules never transit the loader lock or the standard loader's module list, reflective mapping evades LoadLibrary-based inventory; defenders should watch for VirtualAlloc/section-protection patterns, unbacked executable memory, and modules absent from the PEB module list |
ReflectivePluginLoader is a compact C++ project by racoten that answers a narrow, well-defined question: what is the smallest honest implementation of a reflective PE mapper, paired with a disciplined plugin ABI, that a developer can actually read end to end? The repository presents itself as companion code for a blog post on replacing LoadLibrary with hot-swappable modules, and that pedagogical framing shapes everything about it. There is no obfuscation, no injection scaffolding, and no network code — just a mapper, an interface header, one example plugin, and a test harness. At roughly 44 stars and released under MIT (the README badge says MIT while the repo metadata lists Unlicense, a minor inconsistency worth noting), it sits firmly in the educational corner of the reflective-loading ecosystem.
The core value proposition is architectural. On Windows, the canonical way to load a DLL is LoadLibrary, which makes the module visible to the OS loader: it appears in the loaded-modules list, participates in DllMain initialization under loader lock, and becomes enumerable by process and security tooling. A reflective mapper bypasses that path entirely. ReflectivePluginLoader takes a raw byte buffer containing a PE image, manually maps its sections into memory, applies base relocations, walks and resolves the import table, sets per-section memory protections, and then resolves the image's export table — all without a single call into LoadLibrary or GetProcAddress. The README describes the engine as ReflectiveLoaderEngine.h, split conceptually into the PE mapper and the export resolver.
What distinguishes this project from older references like stephenfewer/ReflectiveDLLInjection — which the README explicitly credits as the original reflective loader — is the plugin contract layered on top. Instead of calling an arbitrary export and hoping for the best, modules implement an IPlugin interface declared in Include/IPlugin.h. The host maps the module, resolves five well-known exports (create_plugin, destroy_plugin, plugin_init, plugin_exec, plugin_cleanup), and drives a strict lifecycle: init -> execute -> cleanup. The README's phrasing is telling: the host doesn't need to know what the module does internally; it maps, resolves, dispatches, and moves on. That separation of concerns is exactly what makes a hot-swappable architecture viable.
The ABI design shows attention to cross-module memory safety, which is where naive plugin systems usually fall apart. The README instructs module authors to implement allocation using HeapAlloc and placement new, a deliberate choice to avoid CRT heap mismatches when the host and plugin were built against different runtimes or settings. Freeing a pointer allocated by one CRT with another CRT's delete is a classic cross-module crash, and pushing allocation down to the process heap sidesteps it. The five-export surface (create_plugin/destroy_plugin for lifetime, the three plugin_* functions for behavior) also keeps the boundary symmetrical — the host creates and destroys, the module initializes and cleans up after itself.
The example module, CmdPlugin under Modules/, is intentionally mundane: it takes a command string and executes it via the TaskApi passed to execute. The harness demonstrates mapping it from disk and dispatching a string. In an authorized lab context, this is a teaching vehicle — it shows how the plumbing of mapping, resolution, and dispatch composes, without bundling anything that resembles an operational capability. The interesting part for a defender reading the code is not the command execution but the plumbing around it: any functionality could sit behind the same IPlugin interface, from a screenshot taker to a crypto routine, and the host would be identical.
Build and usage are straightforward and conventional. Configuration and compilation go through CMake — cmake -B build followed by cmake --build build --config Release — producing cmdplugin.dll and harness.exe in the Release output directory. The harness then maps the plugin and dispatches through the interface, for example harness.exe cmdplugin.dll "dir C:\", or with no arguments it falls back to the bundled default plugin and command. The repository also ships a small Python utility, Tools/file2hex.py, that converts a DLL into a C byte array — the standard trick for embedding an image inside a host binary, which is exactly what you would need if the byte buffer were compiled in rather than read from disk.
The most refreshing section of the README is the known-limitations list, because it is unusually candid about what the mapper does not do. Supported features are base relocations, import resolution, and per-section memory protections. Not supported: TLS callbacks, delay-load imports, forwarded exports, CFG metadata, and SEH table registration. These omissions are not trivia — they are exactly the PE features that production loaders and AV/EDR emulation engines care about. A DLL with TLS callbacks will silently skip initialization; a module relying on exception handling will misbehave without a registered SEH table; a binary built with Control Flow Guard expects its LOAD_CONFIG metadata to be honored. The README's engineering position on this is explicit and worth quoting in spirit: reject the module early with a clear error rather than silently half-supporting it, because silent half-support is the worst failure mode.
From a defensive research perspective, ReflectivePluginLoader is a clean specimen for studying loader-detection heuristics. Manually mapped images never enter the PEB module list, never trigger LdrLoadDll notifications, and never create the loader-lock side effects that some instrumentation relies on. Conversely, they do produce distinctive lower-level signals: VirtualAlloc calls with PAGE_EXECUTE_READWRITE followed by re-protection to per-section permissions, relocation writes into freshly allocated regions, and executable memory with no corresponding file backing. The README's explicit mention of per-section memory protections indicates the author is aware that flattening a whole image to RWX is itself a detection smell — a detail many toy loaders get wrong.
Where this fits in an authorized workflow is primarily education and prototyping. Security engineers building detection content for memory-scanning tools can use the harness as a controlled generator of manually mapped images in their own test processes, without the noise of a full offensive framework. Developers studying modular architecture can read the entire codebase in an afternoon — IPlugin.h for the ABI, the engine header for the mapping logic, one module, one harness. Red-team developers preparing for authorized engagements can use it as a starting skeleton to understand what production-grade loaders like MemoryModule (also cited in the references) add on top, particularly around the unsupported PE features listed above.
The lineage the README draws is helpful for orienting the tool. ReflectiveDLLInjection established the technique; MemoryModule is described as the full-featured load-DLL-from-memory library; ReflectivePluginLoader deliberately positions itself as the minimal, readable middle ground with a modern plugin ABI. It is not trying to compete on feature coverage, and the limitations section makes that contract honest. For anyone who wants to understand reflective loading at the code level rather than the blog-post level — or who needs a small, correct testbed for defender-side research into manually mapped modules — this repository is a well-scoped, well-documented starting point, provided its gaps are respected and unsupported modules are rejected rather than forced through.
racoten/ReflectivePluginLoader.Educational analysis for authorized security professionals. Use only in controlled, authorized environments.
0 comentários:
Post a Comment
Note: Only a member of this blog may post a comment.