Obfuscating Reflective DLL Memory Regions with Timers
Applying Memory Region obfuscation to Reflective DLLs specifically ⌛
Last updated
Applying Memory Region obfuscation to Reflective DLLs specifically ⌛
Last updated
In this blog, I want to quickly document some bugs I squashed whilst playing with Ekko (from 5pider). After looking into the technique, I figured it would be a cool addition to Vulpes and that's what I did. However, the purpose of this blog is to discuss the issues I had with:
Reflective DLL Region Permissions
Reflective DLL Region Tracking for Ekko to protect
Threading in DLLMain
General Cleanup
in Maelstrom: Writing a C2 Implant, specifically, Safe Sleeping, we document the background to this technique. Below is that excerpt:
On May 5th 2022, Austin Hudson posted a tweet with a blog: Studying “Next Generation Malware” - NightHawk’s Attempt At Obfuscate and Sleep
This blog went through how Austin was able to identify a sample of Nighthawk which is a proprietary C2 from a UK-based Cyber Security Consultancy, MDSec. In this post, Austin discusses how the technique uses thread contexts and callbacks to flip the memory regions permissions (which we will discuss further in later posts).
For clarity, the research efforts for this technique, on behalf of MDSec, was Peter Winter-Smith and modexp.
I won't be detailing the technique, this blog is focusing on the aforementioned objective.
In the following screenshot, Vulpes can be seen hanging out in memory in a RX
region:
For people who are familiar with the original Reflective Loader, it allocates memory as RWX:
This is something that Paranoid Ninja demonstrates in PE Reflection: The King is Dead, Long Live the King and in his course: Malware on Steroids. From the blog, the following code is shown:
To quote the blog:
The below screenshot shows the newly rebased PE section which does not have any RWX regions anymore, and the RX section only contains the executable code i.e. the
.text
section since all other remaining sections are allocated to other regions now.
This allows the Reflective DLL's .text
section to be converted to RX, and that is what the screenshot earlier on was showing.
For the eagle-eyed, there was only one memory region. As this was demonstrated in Malware on Steroids and I cannot find any reference online showing how to determine which region to free. However, PE Reflection: The King is Dead, Long Live the King does show how to free it:
This is not something I will be showing, though.
At this point, the Reflective DLL looks okay in memory. It has one region cleaned up and freed, and then the other operating out of RX
.
Again, we discussed Ekko in Maelstrom: Writing a C2 Implant:
Once the proof-of-concept was made public by Austin, C5pider then built it out into an open-source tool called Ekko. However, this proof-of-concept uses the base address of the entire image as the region to protect, this only works when the malware is the entire EXE on disk, or loaded as a proper DLL. This can be seen on line 36:
In the event that malware wants to load in the implant entirely through memory, so something like a Reflective DLL, this technique will not work as the
GetModuleHandleA
call will get the base address of the image the DLL is being loaded into. For example, say the DLL is being reflectively loaded intocalc.exe
, then theGetModuleHandleA
will be the base ofcalc.exe
.
For this to work with a proper Reflective DLL, the code needs to be changed slightly. The easiest way to redefine the function is as such:
Whilst also removing the call to GetModuleHandleA
:
The next thing is to figure out which region. Well, the region we have is the RX
one. I spent some time debugging and Paranoid Ninja pointed out that it should be the rebased .text
section, which is obvious in hindsight:
So, in my Reflective Loader:
Where Caller
is:
The struct is then passed to DLLMain
as seen in PE Reflection: The King is Dead, Long Live the King:
Where DLLMain
is:
At this point, I had the correct region. But this then led to a few days of debugging.
For the longest time, my DLLMain
had created a thread on DLL_PROCESS_ATTACH
:
But this only caused issued because:
The Reflective Loader creates a thread pointing to the export function
The export function does some stuff and then calls DLLMain
. So, that call will remain in the context of the thread from the Loader.
DLLMain
is called and a subsequent thread is created pointing to the implants core function, then it breaks.
The DLLMain
returns and the NtWaitForSingleObject
call returns, and the implant exits with ERROR_SUCCESS
.
TL;DR:
DLLMain
shouldn't create a thread because the loader will do the thread creation.
Also, don't be like me and use a Parent Process Id spoof in the loader which injects into a suspended process because the process hasn't finished setting up. This left the thread created by the loader with a base address of 0x0
, crashing within Ekko
.
By simply removing the thread creation in DLLmain
, and just calling the function, the timers work:
All in all, this took a few days of my life. The Timers technique is a interesting and is a cool way to hide malicious memory regions. With that said, Patriot is a tool put together by Joe Desimone to detect this method by searching memory for timers which point to NtContinue
!
Thanks to:
Peter Winter-Smith and modexp: Original authors of the technique
Austin Hudson: For identifying a sample and Reverse Engineering the technique 👀
5pider: For proof-of-concepting the research
Paranoid Ninja: For helping me understand Reflective DLLs properly and debugging the memory region setup