Greetings, in this article, I explained how we can deobfuscate the “Crypto Obfuscator” string protection in Python using dnlib and Reflection.
If you need to ask questions contact to me from twitter or comments (sorry for my bad english I try to improve myself.)
Let’s open the file we encrypted with trial version crypto obfuscator on dnSpy
When we go to a random event, we will see encrypted strings and junk codes:
The process done here: sends the key value as parameter to the decryption method and gets the desired string.
Things To Do :
- Installing Python libraries (and including dnlib.dll)
- Defining string decryption method as name and MethodInfo
- Determining string encrypted places and getting decrypted value by using Invoke
- Placing the received decrypted value
Import and Load
First of all, i will need install pythonnet library
pip install pythonnet
Let’s import the reflection libraries.
In the main block, we will add our dnlib.dll file as a reference, then we will load the obfuscated application to our variables named module and assembly.
Let me give a small explanation of why we are loading two modules separately? assembly, was loaded from Reflection library for Invoke operation (this feature is not available in dnlib). module, for editing operations.
In this section, our script will only find decryption method then save to stringMethodName and invokeMethod variables.
Let’s reopen and analyze it with dnSpy, let’s see how the decryption method has distinctive features.
The decryption method is here:
Now let’s move on to the IL section, which the script can read. (Right Click > Edit IL Instructions)
The most distinctive features of the method:
- Lenght of IL Instructions
- 0th OpCode and 2th OpCode
IL Instructions lenght = 107 0th OpCode = LdcI4 2th OpCode = Ldsfld
Kodumuzu açıklayalım :
- 1) We define our two variables with global keywords so that other functions can access our variables.
- 2) https://docs.microsoft.com/en-US/dotnet/api/system.reflection.bindingflags?view=netcore-3.1
- 3) We navigate between the Types of the module we installed (ie obfuscated binary), that is, Classes. Just below, in the methods of types…
- 4) We call method.BODY.INSTRUCTIONS in the rest of the code. Therefore, if we do not have a body, we provide the skip operation to avoid errors.
- 5) Yes, here we have our first condition, if your IL instructions lenght is less than 107, pass that method.
- 6) We move between ILs to ensure our second condition
- 7) If your currently instruction is LdcI4 (LdcI4.1, LdcI4.2 […], we use the IsLdcI4() function to simplify it.) and after opCode is Ldsfld then…
- 8) We assign the classInstance assignment to find the decryption method later. In order to get the method to be invoked, ie the Decryption method, in the “MethodInfo” type, we are doing the procedures here.
After finding our decryption method, all we do is find the encrypted fields and send the invoking parameter to the decryption method and invoke it. We place the resulting result in its place.
The first thing we need to do is get to know the obfuscated regions. We open and look at dnSpy.
When we look at the IL instructions of an obfuscated place, we will only see that a numeric expression is sent to the method. But multiple methods can work like this. Here, the “stringMethodName” variable that we defined earlier in this section will be useful for us. In the Operand of the section with “call” OpCode, we can say that this is an obfuscated field if “stringMethodName” is contained.
The most distinctive features of this :
- If your current index is LdcI4 and the next instruction OpCode is call
- If the Operand of the instruction with call OpCode contains “stringMethodName”
What we now need to do is pull the value of what we are going to send as an argument and invoke and replace the incoming value:
Required files and full code: