This mini blog post sprouted out of my previous post: Leveraging Java Bytecode for Fun & Analysis and from preparation for the talk at BBWIC Foundation – A Tale of Two Platforms, which happened in July 2022. After a long hiatus from the blog, it is time to share this analysis trick for .NET malware with the world, or at least the minority of readers who still wander off onto this site. This article will also cover one of the Log4J malware samples just to bring it full circle from the previous post, however, this one unlike the last post is not written in Java.
It is not uncommon for people to use dnSpy to analyze .NET executables. As we discussed previously in the last post, at times we need to direct the control flow to obtain more insights into the techniques that the malware performs. Fortunately, dnSpy comes equipped with a debugger which is incredibly convenient. But the executable might be obfuscated and may pose a hindrance during analysis. Now, if you’re familiar with dnSpy, you might ask “Doesn’t it allow one to modify code as well?” And your observation would be spot-on, since we can change the code and print the required variables out to the console etc.
So, let’s try it out on the third stage of an AgentTesla sample. I’ll skip the details about how we got to extracting third stage of this malware (perhaps, content for some other post, based on popular demand).
If we attempt to edit this method, it throws a bunch of errors, just because of peculiarities of constructs in C#. It may also fail to compile if the variable names are Unicode characters, likely, due to obfuscation. Although we can easily add Common Intermediate Language (CIL) assembly instructions without any errors (see Fig 3).
To demonstrate this trick, we will take a look into a simple malware i.e., Khonsari. Analysis about this particular sample can be found here.
Looking at the sample we can make an educated guess that method ‘oymxyeRJ.CajLqoCk’ performs all the de-obfuscations for this malware. A glance at the method shows it is merely XORing the string with a key. We could rewrite this in a scripting language to de-obfuscate but would be missing out on all the fun.
By peeking into the CIL instructions in the epilogue of this method, following operations are observed when the control flow finishes with the loop:
- ldloc.0 – the value from local variable at index 0 is loaded onto the stack.
- ToString() – it is then converted to string.
- stloc.3 – stored into local variable at index 3.
A comprehensive guide for these instructions can be found in the references section.
After we add our instructions, the epilogue should look like this.
We are loading a string literal (absolute path of the output file) onto the stack, followed by the value at local variable 3, which is the resulting ‘stringBuilder’ string, and then a newline character. Then we call ‘String.Concat()’ on the latter 2 elements from top of the stack and then call ‘File.AppendAllText()’ on the result keeping the rest of the epilogue unchanged. Thus, we have successfully modified the method epilogue to write out the de-obfuscated strings to the file – C:\temp\out.txt.
We can detonate the sample to see the de-obfuscated strings written to the intended file.
Unsurprisingly, this also shows the striking similarity of Java bytecode and CIL. Nonetheless, we can certainly take advantage of the intermediate code generated by compilers of both of these languages for the sake of malware research.
- CIL Instructions