DllShimmer: The Stealth Tool for Weaponizing DLL Hijacking without Detection
DllShimmer
How it works
DllShimmer parses the original DLL and extracts information about exported functions (name, ordinal number, and forwarder info). Based on this information, DllShimmer creates a boilerplate C++ file (.cpp). The generated file allows you to add your own code to each function exported from the original DLL without disrupting the normal operation of the program. No reverse engineering or instrumentation is required, because DllShimmer does not rely on function signatures (see more in “Limitations”).
The second file generated is a .def file, which ensures that all DLLs exported from the proxy after compilation will have the same names and ordinal numbers as in the original DLL.
After compilation, the EAT in the proxy DLL is an exact copy of the EAT in the original DLL. All names and ordinal numbers of exported functions match, and forwarded functions are forwarded as well. DllShimmer does not explicitly forward all functions (like most tools), creating a completely new and suspicious EAT structure.
Limitations
- Only x86-64 / AMD64 architecture is supported.
- Most likely, the generic proxy code will not work for functions with floating-point parameters, as they use different registers than integer ones utilized by DllShimmer. If you know the function signature, you can manually adjust it in the generated file.
- Functions with more than 12 arguments will not work because this number has been hardcoded into DllShimmer templates.
- There are some huge obfuscated DLLs with weird name mangling, calling conventions and tricks (e.g. compiled Qt framework DLL). I don’t recommend to use them as a proxy DLL. DllShimmer most probably will generate some garbage in this case.
Usage
Parameters:
-i / --input <path> [required]
The original DLL that you want to backdoor.
-o / --output <path> [required]
The path to the directory where DllShimmer will save all generated files.
-x / --original <path> [required]
In case of dynamic linking (default) provide the path where the proxy DLL will find the original DLL on the target system.
In the case of static linking (--static), specify only the name of the original DLL. It will be searched for according to the default loading order on Windows.
-m / --mutex [optional]
Enabling this option will add a mutex to the source file, which prevents your backdoor from being executed more than once during a single program run. All original functions will continue to work normally.
--static [optional]
Enable static linking between the proxy DLL (IAT) and the original DLL (EAT). This generates an additional .lib file in the output directory, which acts as the original DLL for static compilation.
This technique has some serious limitations compared to dynamic linking:
- You cannot define a full or relative path to the original DLL. The system loader only uses the DLL name form proxy IAT and searches in the default paths.
- Limited debugging information. If the original DLL fails to load, the program will usually crash without additional information.
However, static linking may be more stealthy and natural in some scenarios.
Default: DllShimmer always uses dynamic linking with the LoadLibraryA() and GetProcAddress() functions.
--debug-file <path> [optional]
Save debug logs to a file. Logs are written to a file on an ongoing basis while the program is running. If selected, logs are not printed to STDOUT.
Default: DllShimmer always writes debug logs to STDOUT.
Download
Support Our Threat Intelligence
If you find our technology report and cybersecurity news helpful, consider supporting our work.