3LayersPersistence
Demonstrating 3 persistence layers from a single EXE, that converts itself into proxy DLLs at runtime.
Quick Links
Malware Development Course Syllabus
Offensive Phishing Operations Course Syllabus
Ransomware Internals, Simulation and Detection Course Syllabus
Persistence Layer 1
Using a permanent WMI subscription (under ROOT\subscription), a RegistryValueChangeEvent WQL query watches SOFTWARE\Microsoft\Windows Defender\Signature Updates\SignatureUpdateLastAttempted, which is a registry value updated every time Windows Defender performs a signature update.
The EXE copies itself to %SystemRoot%\System32\wbem\SgrmBroker.exe and registers an ActiveScriptEventConsumer running a VBScript that launches it via Win32_Process.Create.
The dropped EXE's filesystem timestamps are cloned from sihost.exe.
Note
Requires administrator privileges.
Persistence Layer 2
The second persistence layer is a COM hijacked CLSID under HKCU named {c53e07ec-25f3-4093-aa39-fc67ea22e99d}. The real system DLL is Windows.StateRepositoryPS.dll (fetched from the HKLM CLSID), gets loaded by svchost.exe, OpenWith.exe, ms-teams.exe, notepad.exe, SnippingTool.exe, and others.
The EXE patches itself into a proxy DLL (named MsComHost.dll) whose export table mirrors Windows.StateRepositoryPS.dll. MsComHost.dll (our DLL) forwards all function calls to Common.StateRepositoryRM.dll, which is a renamed copy of the real system DLL (Windows.StateRepositoryPS.dll) dropped together to %APPDATA%\Microsoft\Common\.
A set of decoy Ms*.dll files copied from System32 are dropped into the same directory to make it look like a legitimate directory. All PE timestamps and filesystem timestamps are cloned from the original system DLL.
Note
Requires no elevation.
Persistence Layer 3
Spotify loads dsound.dll from its own application directory before falling back to System32, making it vulnerable to DLL search order hijacking.
The EXE patches itself into a proxy DLL (named dsound.dll) whose export table mirrors the real dsound.dll. Our DLL forwards all exports to dspatial.dll, which is a renamed copy of the real dsound.dll DLL. Both DLLs are dropped inside Spotify's %APPDATA%\Spotify\ directory.
PE timestamps and filesystem timestamps are cloned from the real System32 dsound.dll DLL.
Note
Requires no elevation.
Installation Check
A DWORD registry value (HKCU\Software\MaldevAcademy\XXXX\AppIdentifier) is written on first run. On subsequent executions (e.g., when triggered by WMI), the EXE detects this flag and skips re-installation, jumping directly to the payload execution.
Mutex Guard
A machine-unique global mutex prevents the payload from executing concurrently across multiple processes. This is important for the DLL layers, since MsComHost.dll and dsound.dll get loaded into multiple processes simultaneously, without a guard, the payload would fire once per host process.
The mutex name is derived at compile time from the __TIME__ macro mixed with the volume serial number of the system drive at runtime, producing a name that is unique per machine.
EXE-to-DLL Patching
The patching process reads the EXE from disk into memory, then applies the following patches from within the ConvertExecutableToDll function:
IMAGE_FILE_DLLis set inFileHeader.CharacteristicsAddressOfEntryPointis redirected to the RVA of DllMain.Subsystemis set toIMAGE_SUBSYSTEM_WINDOWS_GUI- The target system DLL's exports are read and a forwarded export table is built, with every export pointing to the renamed copy of the real system DLL
- The export table is appended as a new
.edatasection - Stomps all PE timestamps (NT header, export directory, debug directory) to 30 days before the original DLL's timestamp.
The resulting binary is a functional proxy DLL that mirrors the hijacked system DLL's exports and forwards all calls to the legitimate DLL.
Verification & Cleanup
After execution, one can run VerifyPersistence.ps1 to inspect all dropped files, registry keys, exports, and timestamps. Additionaly, for cleanup, CleanupScript.ps1 should be executed.
Compilation Modes
| Configuration | Logging | Logging Strings |
|---|---|---|
Debug |
DbgView + Console Window (In DLLs Also) | Present |
Release |
DbgView only | Present |
Stripped |
None | Removed |
In Stripped mode, all logging macros compile to nothing and strings are removed, producing a clean binary with no debug artifacts. It also replaces the default entry point with a custom EntryPoint that calls main and exits via ExitProcess, avoiding CRT linking.