Dot Net Deobfuscation – Generic String Decryption

Thanks to metadata, the object-oriented paradigm, leverageable framework libraries, and a well-documented assembly structure, Microsoft’s .Net Framework has gained a following among developers who don’t need the performance benefits of an unmanaged language like C++. However, for these same reasons, the resulting assembly is also much easier to decompile and reverse engineer. Obfuscation can be used to make an assembly harder to decompile by “muddying” the assembly to a human’s eye. Obfuscation has many parts, one of which is string encryption.

Strings can be crucial information for finding execution paths in an assembly, therefore it is always wise to encrypt them. String encryption comes in many flavors, some closer to “encoding” rather than “encryption”. Maybe I will do a case study on encryption some day, but for now, let’s have some fun and delve into removing the string encryption.

String Encryption in IL

One of the beauties of  programming is that there are many ways to implement an idea. Likewise, there are many ways of implementing string encryption, and I will focus on a method I found in a recent assembly I was researching.

L_0030: ldstr "<Cipher Text>"
L_0035: ldc.i4 <IntCode>
L_003a: call string <Namespace>.<Class>::<Method>(string, int32)

Of course I masked the specific parts for obvious reasons. As shown above, a cipher text string is pushed onto the stack followed by a integer code and a call to the decryption method. Important note: the arguments are constant. If they were changing, this sort of attack wouldn’t be possible, as we won’t be running the assembly from its entry point. It is also crucial for the decryption method to also not depend on any external variables, as those won’t be initialized when we use reflection.

The Attack

The basic idea behind this patching method is to load the assembly in question, run the decryption method to generate the plain text, repatch the assembly, and thus allow us to search the assembly for strings.

Using Mono

After loading the assembly with Mono, and iterating through the modules, types, and methods, we can access the actual instructions via MethodBody.Instructions. Now, we perform a linear search on the method body until we find the three instructions above: ldstr, ldc.i4, and call. Just preform a check on the call instruction to make sure we are calling the decrypt method by checking the namespace or the method name.

Now we need to collect the cipher text and integer code using Instruction.Operand. Now we have the full method name, and our arguments. Time to use reflection.


Instruction current = instructions[i];
Instruction next = instructions[i + 1];
Instruction last = instructions[i + 2];

if ((current.OpCode == OpCodes.Ldstr) &&
(next.OpCode == OpCodes.Ldc_I4) &&
(last.OpCode == OpCodes.Call))
{
	//Process
	string encrypted = current.Operand as string;
	int key = (int)next.Operand;
}

And Reflection

Next step is to give the assembly life and Load it using Assembly.LoadFrom. Now time to execute the assembly.

object result = decryptMethod.Invoke(null, new object[] { encrypted, key });
string decrypted = result as string;

We are using the power of Reflection to execute the decryption method on the assembly using its constant arguments. We have our plain text, and therefore don’t need the cipher text or integer code any more.

Patching the Assembly

Mono is pretty powerful, and can actually replace instructions. The three above instructions are useless and now should be replaced by just a single call to ldstr to push the plain text string on the stack.

//Now replace instructions
Instruction newInstruction = worker.Create(OpCodes.Ldstr, decrypted);
worker.Replace(current, newInstruction);
worker.Replace(next, worker.Create(OpCodes.Nop));
worker.Replace(last, worker.Create(OpCodes.Nop));

Dont forget to call AssemblyFactory.SaveAssembly to save the patched assembly to disk.

Final Thoughts

This type of attack is a simple and effective way of removing string encryption from assemblies as long as they use constant arguments to invoke a constant function. One final thing to note – depending on the target (a lot of viruses and malware is written in .Net as more can get done for less work), it might be best to remove the string encryption on a Virtual Machine to avoid running any of the assembly’s code.

This entry was posted in Reverse Engineering, Uncategorized. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>