Beating a .NET Anti-Analysis Technique With as Little Work as Possible

A Brief Writeup on the Bypass for an Anti-Analysis Technique for .NET Software

Background

A close friend of mine found a file that seemed to be untouchable by any existing .NET tooling. No matter what program was used to attempt to study the file, the binary would invariably trigger a crash. Worse yet, the file was accompanied with an intentionally smug video gloating over the supposed power of this software protection technique. I couldn’t resist taking a peek.

Using ildasm

The video shows that the protection completely crashes ildasm; however, the video uses the ildasm GUI, which does far more work than I want when peeking into a file. I ran ildasm /METADATA Cursed-VRChat.dll /OUT=metadata.txt to check for any anomalies. The error that ildasm produced gave my first clue: ERROR: method 'Open_Source_Obfuscator_Edited_By_Kronik' has no signature.

Fortunately, ildasm did produce metadata and dump it. The error told me everything that I needed, before even peeking into metadata, though: that I will either need to modify the binary to correct malformed information, that I will need to modify tooling to handle it better, or both. Now that I had an idea of what to do, it was time to debug.

Breaking the Protection

Identifying the Issue

The first thing I attempted to do, in the spirit of laziness, was to run the file through an obfuscator (Confuser 1.9) again, in the hope that the metadata obfuscations done by Confuser would be enough to undo the changes to the file. This failed, and Confuser simply crashed while doing obfuscation. This meant two things: one, that I actually had to think and do work, and two, that I had a program to debug.

For those unaware, Confuser (and most software that works directly on .NET assemblies, including programs like ilspy) use Mono’s Cecil library (or a variant) for inspecting .NET assemblies. If I fixed Cecil, then, in an ideal world, I would also have fixed most .NET RE tooling.

I immediately set to work using Confuser 1.9’s command line frontend and dnspy to work on a fix. I quickly narrowed down the error to Cecil’s ReadArrayTypeSignature method. In this method, an array is created of enormous size. A memory error is thrown–and if it isn’t (which happens in the case of dnspy), then the program simply hangs for an eternity. The size of the array is returned by ReadCompressedUInt32(). The uint is stored in the binary as 0xDFDFDFDF, which is a very large number, and this is what triggers the pathological behavior.

Crafting the Solution

I simply hex-edited 0xDFDFDFDF out of the binary and replaced it with 0x01010101. An alternative approach is to read the reader’s buffer.position at the time ReadCompressedUInt32() is called in order to find out which bytes to replace.

Results

After patching out this byte sequence, the file opened just fine in dnspy. While the final file wasn’t entirely .NET tooling friendly afterward, this is to be expected from any obfuscated assembly. The fact that it opened in dnspy was more than enough to allow further analysis.

You can contact me at my email address (see the about page) for the files if you would like them.

Notes

I am in the middle of midterms as I wrote this. I shouldn’t have distracted myself with this project, and I shouldn’t be writing this post. I’m not even at the same computer that I did the reversing on, so I’m unable to attach any resources, like pictures. Feel free to email me here if there are any questions.