This is some text inside of a div block.
Glitch effect

Peeling Back the Layers of .NET Malware

Glitch effectGlitch effectGlitch effect
Glitch banner

Cyberattackers constantly develop creative ways to obfuscate their code to conceal the presence (and purpose) of their malware.

In this blog, we dive into an executable we found that compiles a DLL file on the fly, loads the DLL and then decodes an image embedded in the original executable that contains the true payload. Follow along as we step through each layer to uncover the real intent of this obfuscated .NET malware.

It all started the other day when we discovered a suspicious Internet Shortcut file within a user’s startup directory:


Instead of the typical behavior of launching a browser and displaying a website, this .URL file pointed to a local file C:\Users\<USER>\doc06421,pdf.exe.

As you can see, both file names are clearly suspicious. With no hits on the executable’s hash in VirusTotal, it was time for our SOC team to take a closer look.

We first used the file utility to identify the executable type, which was a .NET application. Next, we loaded it into dnSpy (a .NET debugger and assembly editor) and discovered three functions: Compile(), Main() and XorEncrypt().


The XorEncrypt() function immediately stood out to me. For those new to forensic analysis, malware often uses XOR to obfuscate malicious functionality. In a nutshell, this executable XOR “decrypts” the datastring variable (which contains C# source code), compiles the source code into a DLL and creates a thread and injects the DLL. This behavior screams malware.

By reimplementing the XorEncrypt() function in Python, we were able to inspect the deobfuscated source code. Interestingly, the new code (and subsequent DLL) doesn’t do much. Its Main() loads a resource, processes it in some fashion and then executes the processed resource in a new thread.

Main() from the deobfuscated DLL source code that loads and processes an embedded resource

Main() from the deobfuscated DLL source code that loads and processes an embedded resource

Now it was time to see if we could find the referenced resource. Using Resource Hacker, we examined the original executable. Sure enough, it has an HTML resource with the ID 130.

Original executable has an embedded HTML resource with a PNG header

Original executable has an embedded HTML resource with a PNG header

As highlighted in the above screenshot, resource 130 appears to be a .PNG file. However, we know from the source code that this resource was somehow being processed or leveraged by the malware in some way. Rather than reimplement the decoding routine this time, we decided to saved the resource to a file and simply modify the Main() to turn the malware against itself.

With the below modifications, the process will run just as it would have under normal conditions. However, instead of using FindResource() and ultimately executing the “payload,” we made the malware read in the resource from a file and save the “payload” that was extracted from the fake .PNG image.

We loaded the new “payload” file into dnSpy and were once again greeted with more obfuscation:

Fortunately, there are tools to deal with obfuscated .NET files. After running the binary through de4dot, we ended up with something a bit more comprehensible.

This new code uses GetExecutingAssembly() to get a copy of itself and then modifies itself during execution. Using dnSpy as a debugger, we stepped through the code to observe what was changing.

Near the end of the function there were two variables of interest (xnnXVZCo and xnnXVZCo2). These variables were initially used to store the original assembly (.NET PE file).

NET PE file

After saving the contents of the variable to a file, we once again used dnSpy to explore further. After jumping through a half-dozen layers of obfuscation and indirection, we finally hit pay dirt!

This malware had all sorts of capabilities that allowed an attacker to disable antivirus applications, steal passwords, log keystrokes and control a victim’s webcam—just to name a few.

Function names indicate capabilities of the malware

Function names indicate capabilities of the malware

Needless to say, .NET malware can pose a significant risk to Windows laptops, workstations, servers, etc. Although antivirus and other preventive security products do their best to stop every attack, this sample illustrates just how far hackers are willing to go to evade detection. Thankfully, our partners can rest easy knowing they’re protected by the Huntress SOC team.

Curious If You’re Compromised?

We offer a free trial of Huntress for an unlimited number of computers. Simply deploy our agent, set up a reporting integration into your ticketing system and we’ll deliver step-by-step remediation procedures for each compromised host we discover.

Sign Up for Blog Updates

Subscribe today and you’ll be the first to know when new content hits the blog.

Huntress at work