[EN] ConfuserEx Deep Analysis ~ AntiTamper
Hi everyone, rhotav here. I’ll be analyzing the AntiTamper feature that belongs to ConfuserEx protection. Why I am analyzing ConfuserEx specifically is to not break the chain of this course (AntiDump Analysis). Plus, it bothers the reversers much more when there is a newly edited open-source protection.
The basic structure of this post is as:
What is AntiTamper?
AntiTamper according to ConfuserEx wiki:
This protection ensures the integrity of application and encrypts the methods with the checksum of the whole module, to ensure that the module will load only if no modification has been made to it.
In accordance with this definition, we can assume that AntiTamper protection messes up with the whole code structure to re-organize it at the beginning, in spite of ensuring the integrity of the application (except module constructor). I’m sure you’ll comprehend this with few sample cases.
Detection of AntiTamper in a Code
Let’s start observing with dnSpy. I mentioned that it disarranges functions’ bodies. We can scroll over Entry-Point to check that out.
We can see that there is not a single line at Main. Though it could be like this:
Why this happens is: ConfuserEx confuses the functions’ structure with random values while injecting protection to a file. Not stumbling upon a decompiler error at Main doesn’t mean that we won’t see them in a different function. Because it shuffles Bytes with totally arbitrary values, sometimes we confront decompiler error’s when there are significant bytes of the CIL Instruction List instead of normal ones.
Doing Right Click > Edit Method Body on Form1 CCTOR function will let us see the gibberish OpCodes:
Now let’s do it with the Main function:
Not a single byte appears here…
At this point, I have to touch on a blurriness of concepts. What AntiTamper protection does for ensuring the integrity is actually disarranging functions and rearranging them to run properly while working. Thus, we can say that it’s purpose is not making a code unreadable. We can call it Anti-Decompiler or Anti-Disassemble…
Removing of AntiTamper Dynamically
So, this is the cool part here. Before barging into Removing, I will explain the theory of this method.
While running, a .NET application is always in touch with JIT Compiler. When you don’t put the application to pre-jit, every function gets commented as total runtimes. That is to say, that when you run a function, the function is sent to clrjit (could be mscorjit, depends on the version). You can see the post I analyze the JIT Compiler here.
Preview of My JIT Killer Project, exclusively for PwnLab.
Consequently, for an AntiTamper applied code to run properly, first it has to regulate the function structures that it messed up with earlier. This regulating process has to run on a not de-arranged function before all de-arranged functions. Where I am referring to is the Module Constructor point, otherwise the .cctor point of the module. We can Right Click > Go to .cctor on module for this.
static <Module>() ( .... )
Where it brings you, you will see a call of a function that got protected with “Symbol Renaming”. To properly see and read the functions with broken names, I change them via dnSpy here:
It became something like this. It’s just a little more clear now.
Now just after the Initialize function runs and it’s over, we’ll get the dump from the “Modules” tab that dnSpy offers us. Add a breakpoint to the location. When breakpoint triggers, continue with “StepOver” once and open the dump via memory on dnSpy:
Deep AntiTamper Analysis
Right, now we can dig into the AntiTamper theory. If you check from ConfuserEx GitHub page, you’ll see there are 2 AntiTamper modes present (Normal and JIT). JIT Mode is a method that the maker of ConfuserEx didn’t finish making so we can’t see that. If it was done, it probably was going to be like the method that .NET Reactor uses.
Everything is crystal clear here already. Aim is obvious, hooking the compileMethod that is in a virtual class of ICorJitCompiler that getJit runs and switching the function structure, though it discontinued…
We can get into the detail about that in another analysis text. Now let’s go through the AntiTamper protection that we see in every ConfuserEx mod and classic ConfuserEx.
Because I think it’s unnesseccary to go through, I will skip the process where AntiTamper structure is injected.
As you can see from the GitHub page here too, it Injects the class into the target software. So, the function that runs at constructor that I renamed afterwards is actually this one. We can proceed to go through this on dnSpy
To interpret PE structure like which offset has which value, I take here as a reference.
When we examine the Initialize function, we encounter the classic, dynamic computation codes that I mentioned in my AntiDump post:
Module module = typeof(<Module>).Module; string fullyQualifiedName = module.FullyQualifiedName; bool flag = fullyQualifiedName.Length > 0 && fullyQualifiedName == '<'; byte* ptr = (byte*)((void*)Marshal.GetHINSTANCE(module)); // Fonksiyonun hafıza üzerindeki adresi alındı. byte* ptr2 = ptr + *(uint*)(ptr + 0x3C); // Hafıza adresi + 0x3C = e_lfanew (PE Header adresi) ushort num = *(ushort*)(ptr2 + 6); // e_lfanew + 0x6 = Number of Sections ushort num2 = *(ushort*)(ptr2 + 0x14); // e_lfanew + 0x14 = Size of Optional Header uint* ptr3 = null; uint num3 = 0U; uint* ptr4 = (uint*)(ptr2 + 0x18 + num2); // e_lfanew + 0x18 + (e_lfanew + 0x14) = First Section Header
You can use HxD to see these computational processes on memory. Open Hxd, click “Open Main Memory” and choose the application from Process list that comes out.
You have to do this while the application is running. Plus, it’s easier to confirm which variable holds which value if you run debug on dnSpy.
The software, I use CTRL-G, paste the address copied from dnSpy and locate to confirm e_lfanew for instance.
We can see that the address I copied “005D0080” (base address + 3C) = (005D0000 + 3C) choose PE, or “50 45” directly.
The aim for these calculations was to spot the areas to be deleted in AntiDump protection and delete. Here, it is to calculate the password to decrypt the function structures and decrypt.
Thanks to dnSpy once again, we can check on the method part in Stream structure of the software. The list of every function that is used in the code appears here. .NET files’ arrangement has this feature. RVA points to the method body data that stores ILHeader ILCode LocalVar EH. ConfuserEx switches the RVA and embeds it to “Section #0: BLABLABLA”(That is marked with green). That is why the function called in Constructor goes to the first section header. This section keeps the method body, ConfuserEx encrypts it and that call activity running in Module Constructor actually calls a structure to fix it.
What it does afterward is basically nothing but detecting the key location to put it in XOR with bytes. So no need to go into that, it’ll do the same process that we have done up there.
There are two versions of AntiTamper as mentioned. Why JIT wasn’t done is because there would be integrity problems and it’d require updates over and over. Usually JIT structure renews with every .NET update. New features and structures gets added so there can be integrity problems in applications like these. .NET Reactor does a successful job there, as it has updates repeatedly😀