Loading Capabilities from Memory: Open Sourcing SCYTHE's Windows C In-memory Module Loader
This is Jonathan Lim and Ateeq Sharfuddin, and in this blog we share a project we are open sourcing: SCYTHE's standard Windows C In-memory Module Loader. We provide a brief overview of loaders and share how we load our capabilities with the community.
There are three well-known mechanisms a program can choose to use other software : static linking, dynamic linking, and dynamic loading. In Windows, dynamic linking and dynamic loading are handled by the Windows loader, and are done at load time and runtime, respectively.
For dynamic loading, loaders take a shared library and import it into a program’s memory, allowing the program to invoke capabilities available inside the library. When a program is done using these capabilities, the loader can be asked to unload the library from memory.
We’re most interested in dynamic loading, which allows a program to load and use a shared library at run time because it leaves a smaller footprint.
Why do we care?
SCYTHE emulates different classes of threat-actors. Different classes of threat-actors have different motivations, and therefore, produce and utilize different types of malware to complete their goals. For example, Nation-states are the most sophisticated threat-actors and go through great lengths to ensure the end user is not alerted to their intrusions. As a consequence, whereas cyber-criminals and less sophisticated threat-actors might not be so concerned about leaving artifacts of compromise behind, malware produced by nation-states aim to leave no artifacts behind and have built-in security that hinders forensic analysis. One method nation-state threat-actors use is to keep much of the running malware only in volatile memory, for example, a system’s RAM. This way, when the system shuts down or is restarted, there is no disk artifact of the intrusion as state is lost in volatile memory when power is removed from the system. Dynamic loading capabilities aid adversaries in this scenario.
Dynamically loading shared libraries
Shared libraries such as Dynamic-Link Libraries (.dll files) on Windows or Shared Object Libraries (.so files) on Linux can be dynamically loaded at runtime. As a simplification, Windows API provides a C function LoadLibrary(FilePath) to load a shared library into a process’s address space. The C function GetProcAddress(ModuleHandle, FunctionName) can then be used to retrieve the address of an exported function (or variable) in the shared library. If the function is found, the application code can now call this function. Finally, the application can call FreeLibrary(ModuleHandle) to unload the shared library from the process’s address space when no longer in use . In the POSIX standard API, the equivalent C functions are dlopen, dlsym, and dlclose , respectively.
Loading Dynamic-Link Libraries from memory
Although official documentation exposes APIs where the shared libraries must be files on disk, research has been published  that demonstrate that shared libraries can in fact be dynamically loaded directly from memory and reference implementations exist. These research provide details necessary to reimplement the operating system’s loader and offer three replacement C functions: LoadLibraryFromMemory(ModuleBytes), GetMemoryProcAddress(Handle, FunctionName), and FreeInMemoryLibrary(Handle). With such a reimplementation of the operating system’s loader we can load the shared library directly into a process’s address space.
In an adversarial scenario, the shared library would be transmitted over the wire into memory and a loader similar to what we are sharing would load this shared library from memory without any need to write it to disk first.
In-Memory Loader Overview
The in-memory loader goes through a series of steps that emulates a Windows loader except it allows libraries from the network:
- Open a network socket to read the shared library into a previously allocated memory address space
- Allocate memory based on information available in the headers of the shared library.
- Perform relocations
- Load the Dynamic-Link Libraries this shared library depends on with the Windows loader.
- Update the section protections
- Perform TLS callbacks
- Call DllMain of the shared library with DLL_PROCESS_ATTACH
Source code is available in GitHub under: https://github.com/scythe-io/memory-module-loader.
Notable features of this loader:
- This loader can directly load any Dynamic-link Library (DLL) from memory for both x86 and x64. The DLL does not need to follow any specific pattern: For example, the DLL does not need to be a Reflective DLL nor does the DLL need to only contain position-independent code.
- The loader performs TLS callbacks prior to calling DllMain.
- You can also retrieve resources from the DLL.
This loader is derived from John Levine's Loaders and Linkers (ISBN: 1558604960).
This particular loader is a fork from https://github.com/fancycode/MemoryModule circa summer 2016.
The solution contains source code for the loader. The source code is compiled into a static library (.lib). A sample DLL is provided, as well as an executable, which when invoked reads this sample DLL into memory and then loads it and calls a function inside it.
Loading is a key concept in allowing programs to use other software as shared libraries. We explored how loading capabilities dynamically can be done from memory alone, without leaving behind disk artifacts. SCYTHE emulates various types of threat-actors and some such as nation-state actors require having a small footprint to avoid traceability. There are many other methods that are used by threat-actors to hide their presence but hopefully this gives you an idea of how dynamic loading can be used for unintended purposes.
SCYTHE provides an advanced attack emulation platform for the enterprise and cybersecurity consulting market. The SCYTHE platform enables Red, Blue, and Purple teams to build and emulate real-world adversarial campaigns in a matter of minutes. Customers are in turn enabled to validate the risk posture and exposure of their business and employees and the performance of enterprise security teams and existing security solutions. Based in Arlington, VA, the company is privately held and is funded by Gula Tech Adventures, Paladin Capital, Evolution Equity, and private industry investors.