📜 ⬆️ ⬇️

How to trick NET.Reflector

Today I thought about how obfuscators hide the code of methods from deobfuscation utilities like NET.Reflector. Strangely enough, but I did not find any useful information on this issue on the Internet (I might have searched badly) and so I had to do a little research myself. Under the cut a brief note on the results. The sample code will again use Mono.Cecil and code generation, so be sure to read my first article .



Let's start with the theory. Each MSIL instruction code can be represented by one or two bytes. For example, the nop instruction is represented as 0x00. So we have 65,536 different options. To date, some of this space is occupied by valid MSIL instructions codes, but most are free. Switching the compiler's JIT to an instruction with an incorrect code will cause the application to crash. NET.Reflector, bumping into an incorrect instruction stops parsing the method code and displays the message "Invalid method body", which is what we need.
')
Thus, our goal is to insert incorrect instructions into the method, but so that the transition to it cannot occur under any circumstances. To do this, you can use the unconditional transition:

goto MethodCode; //    MethodCode: //    


Reflector will not analyze the reachability of the code and when analyzing bad instructions, it will stumble and the method will not be analyzed further. The application will work fine, since the transition to a bad instruction will never happen. The case is somewhat complicated by the fact that Mono.Cecil will not allow us to simply insert bad instructions - all valid codes are presented as an enumeration and you cannot add your standard means. Of course, you can always change the source code of Mono.Cecil, since the system is open source, but I wanted it to work on a standard build. After half an hour of analyzing the source code of Mono.Cecil, I found how to insert an incorrect instruction 0x0024 so that Mono.Cecil would skip it and not throw an exception. Let's look at the code:

 static void ProtectMethod(string path, string methodName) { var assembly = AssemblyDefinition.ReadAssembly(path); foreach (var typeDef in assembly.MainModule.Types) { foreach (var method in typeDef.Methods) { if (method.Name == methodName) { var ilProc = method.Body.GetILProcessor(); //   internal    OpCode var constructor = typeof(OpCode).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(int), typeof(int) }, null); //  Mono.Cecil     -     4-(int) ,       8 ,        MSIL . ,        8   2 .       OpCode,      .        ,       , Mono.Cecil        Exception,       . int x = 0xff << 0 | //   IL  0x24 << 8 | //   IL  0x00 << 16 | (byte) FlowControl.Next << 24; //       ,    ,  Mono.Cecil     int y = (byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24; var badOpCode = (OpCode) constructor.Invoke(new object[] {x, y}); //    Instruction badInstruction = Instruction.Create(badOpCode); Instruction firstInstruction = ilProc.Body.Instructions[0]; //         ilProc.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Br_S, firstInstruction)); //        ilProc.InsertBefore(firstInstruction, badInstruction); } } } assembly.Write(path); } 


Consider the result of this method on some method of our assembly, which we want to protect from viewing

Method before processing:
image
image

Method in the reflector after processing:
image
image

In this case, after processing the application runs without problems. The desired result is achieved.

Finally, I note that such obfuscation is very easy to manage (assembly changes will be required), but it is not well-versed by developers who can protect the code from being viewed. Also, in my opinion, the information itself, given in the note is very interesting.

Source: https://habr.com/ru/post/108955/


All Articles