So yesterday I talked a about using CCI to remove attributes from .Net binaries. Specifically the SupressIldasm attribute. I promised I’d put up some more code highlighting the framework’s benefits. So some more detail on the binary I’m working with. It has been ran through Babel -> Netz -> Babel again. My goals have been to reverse Debabel-> Unpack Netz -> Rebuild the .exe -> debabel again, although the first stage of babel could be skipped, but why not analyze it.
Babel uses a couple of simple techniques to prevent programs like reflector from analyzing protected binaries. These techniques are also found in other protections, so it’s useful to understand why the work and how they work, they are really very simple.
Today I’ll cover a simple but annoying technique being employed; inserting junk bytes. Babel inserts junk bytes into the IL stream of each method. When reflected it causes the disassembler to fail as it does not recognize the byte sequences it can’t continue.
Below is an example of a method ildasm’ed after removing the “suppressIldasm” attribute from the previous post.
.class public auto ansi beforefieldinit netz.NetzStarter extends [mscorlib]System.Object { .field private static initonly string Property0 .field private static initonly string Property1 .field private static initonly string Property2 .field private static class [System]System.Collections.Specialized.HybridDictionary Property3 .field private static class [mscorlib]System.Resources.ResourceManager Property4 .field private static class [mscorlib]System.Collections.ArrayList Property5 .field private static bool Property6 .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 14 (0xe) .maxstack 8 IL_0000: br IL_0007 IL_0005: unused IL_0006: unused IL_0007: ldarg.0 IL_0008: call instance void [mscorlib]System.Object::.ctor() IL_000d: ret } // end of method NetzStarter::.ctor |
As you can see it does an absolute jump over some “unused” bytes which are really invalid bytes. This way the logic of the program is maintained while confusing the disassembler. One technique I’ve read to handle this is to use a hex editor to look for the absolute jump op code and nop out those bytes. However this is unreliable as babel inserts bytes not just at the start of the method.
Microsoft CCI to the rescue again!.
So lets use CCI to handle rebuilding the binary by replacing invalid bytes with nops. This way we can now view this application in reflector and be able to navigate it. Below is the mutator class i wrote to handle NOP’ing invalid bytes. Again a very simple solution. Now the code is visible in reflector using the IL view. At least you get the “browsing” functionality and easily go to functions and view their dependencies and cross-references.
public class InvalidCodeNOPReplace : MetadataMutator { public InvalidCodeNOPReplace(IMetadataHost host) : base(host) { } public override List<IOperation> Visit(List<IOperation> operations) { operations = Utilities.ReplaceInvalidOpCodeAsNOP(operations); return base.Visit(operations); } } public static List<IOperation> ReplaceInvalidOpCodeAsNOP(List<IOperation> ops) { List<IOperation> newOps = new List<IOperation>(); foreach (IOperation op in ops) { if (!IsValidOpCode(op.OperationCode)) { Operation o = new Operation(); o.Location = op.Location; o.Offset = op.Offset; o.OperationCode = OperationCode.Nop; o.Value = 0x0; newOps.Add(o); } else { newOps.Add(op); } } return newOps; } private static void populateOpCodeDic(){ OpCodes = new Dictionary<OperationCode,int>(); foreach(int i in Enum.GetValues(typeof(OperationCode))) { OpCodes[(OperationCode)i] = i; } } public static bool IsValidOpCode(OperationCode opCode) { if (OpCodes == null) { populateOpCodeDic(); } return OpCodes.ContainsKey(opCode); } |
Unfortunately reconstructing the C# source doesn’t work at this stage due to the nops and invalid branching structure. However, I’m trying to work out a middle layer which can take a methodbody’s operations list, abstract it out, turn it in to a control flow graph, optimize it and rewrite. However i’m still stuck at the rewriting part. I hit a small snag in the logic I haven’t had time to work out just yet. Hopefully then the C# can be reconstructed.
Tomorrow I’ll post some simple methods to get readable names out of the method/properties/class names to make following logic easier.
*Edit forgot to add the IsValidOpCode method.
**Edit had to readd disappearing generic types.. Ugh!