diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 98155190..b22d333b 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -57,10 +57,13 @@ + + + diff --git a/de4dot.code/deobfuscators/Babel_NET/AssemblyResolver.cs b/de4dot.code/deobfuscators/Babel_NET/AssemblyResolver.cs index 260a1f40..1af66548 100644 --- a/de4dot.code/deobfuscators/Babel_NET/AssemblyResolver.cs +++ b/de4dot.code/deobfuscators/Babel_NET/AssemblyResolver.cs @@ -17,6 +17,7 @@ along with de4dot. If not, see . */ +using System; using System.IO; using Mono.Cecil; using de4dot.blocks; @@ -24,6 +25,7 @@ using de4dot.blocks; namespace de4dot.code.deobfuscators.Babel_NET { class AssemblyResolver { ModuleDefinition module; + ResourceDecrypter resourceDecrypter; TypeDefinition resolverType; MethodDefinition registerMethod; EmbeddedResource encryptedResource; @@ -61,8 +63,9 @@ namespace de4dot.code.deobfuscators.Babel_NET { get { return embeddedAssemblyInfos; } } - public AssemblyResolver(ModuleDefinition module) { + public AssemblyResolver(ModuleDefinition module, ResourceDecrypter resourceDecrypter) { this.module = module; + this.resourceDecrypter = resourceDecrypter; } public void find() { @@ -81,12 +84,26 @@ namespace de4dot.code.deobfuscators.Babel_NET { if (!BabelUtils.findRegisterMethod(type, out regMethod, out handler)) continue; + var decryptMethod = findDecryptMethod(type); + if (decryptMethod == null) + throw new ApplicationException("Couldn't find resource type decrypt method"); + resourceDecrypter.DecryptMethod = ResourceDecrypter.findDecrypterMethod(decryptMethod); + resolverType = type; registerMethod = regMethod; return; } } + static MethodDefinition findDecryptMethod(TypeDefinition type) { + foreach (var method in type.Methods) { + if (!DotNetUtils.isMethod(method, "System.Void", "(System.IO.Stream)")) + continue; + return method; + } + return null; + } + public void initialize(ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob) { if (resolverType == null) return; @@ -97,7 +114,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { return; } - var decrypted = new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData()); + var decrypted = resourceDecrypter.decrypt(encryptedResource.GetResourceData()); var reader = new BinaryReader(new MemoryStream(decrypted)); int numAssemblies = reader.ReadInt32(); embeddedAssemblyInfos = new EmbeddedAssemblyInfo[numAssemblies]; diff --git a/de4dot.code/deobfuscators/Babel_NET/BabelInflater.cs b/de4dot.code/deobfuscators/Babel_NET/BabelInflater.cs new file mode 100644 index 00000000..8cf7ce43 --- /dev/null +++ b/de4dot.code/deobfuscators/Babel_NET/BabelInflater.cs @@ -0,0 +1,63 @@ +/* + Copyright (C) 2011-2012 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using ICSharpCode.SharpZipLib; +using ICSharpCode.SharpZipLib.Zip.Compression; + +namespace de4dot.code.deobfuscators.Babel_NET { + class BabelInflater : Inflater { + int magic; + + public BabelInflater(bool noHeader, int magic) + : base(noHeader) { + this.magic = magic; + } + + protected override bool ReadHeader(ref bool isLastBlock, out int blockType) { + const int numBits = 4; + + int type = input.PeekBits(numBits); + if (type < 0) { + blockType = -1; + return false; + } + input.DropBits(numBits); + + if ((type & 1) != 0) + isLastBlock = true; + switch (type >> 1) { + case 1: blockType = STORED_BLOCK; break; + case 5: blockType = STATIC_TREES; break; + case 6: blockType = DYN_TREES; break; + default: throw new SharpZipBaseException("Unknown block type: " + type); + } + return true; + } + + protected override bool DecodeStoredLength() { + if ((uncomprLen = input.PeekBits(16)) < 0) + return false; + input.DropBits(16); + + uncomprLen ^= magic; + + return true; + } + } +} diff --git a/de4dot.code/deobfuscators/Babel_NET/BabelMethodCallInliner.cs b/de4dot.code/deobfuscators/Babel_NET/BabelMethodCallInliner.cs new file mode 100644 index 00000000..10d9c955 --- /dev/null +++ b/de4dot.code/deobfuscators/Babel_NET/BabelMethodCallInliner.cs @@ -0,0 +1,244 @@ +/* + Copyright (C) 2011-2012 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using System.Collections.Generic; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; +using de4dot.blocks.cflow; + +namespace de4dot.code.deobfuscators.Babel_NET { + class BabelMethodCallInliner : MethodCallInlinerBase, IBranchHandler { + InstructionEmulator emulator; + BranchEmulator branchEmulator; + int emulateIndex; + IList instructions; + + public BabelMethodCallInliner() { + emulator = new InstructionEmulator(); + branchEmulator = new BranchEmulator(emulator, this); + } + + public static List find(ModuleDefinition module, IEnumerable notInlinedMethods) { + var notInlinedMethodsDict = new Dictionary(); + foreach (var method in notInlinedMethods) + notInlinedMethodsDict[method] = true; + + var inlinedMethods = new List(); + + foreach (var type in module.GetTypes()) { + foreach (var method in type.Methods) { + if (!notInlinedMethodsDict.ContainsKey(method) && canInline(method)) + inlinedMethods.Add(method); + } + } + + return inlinedMethods; + } + + void IBranchHandler.handleNormal(int stackArgs, bool isTaken) { + if (!isTaken) + emulateIndex++; + else + emulateIndex = instructions.IndexOf((Instruction)instructions[emulateIndex].Operand); + } + + bool IBranchHandler.handleSwitch(Int32Value switchIndex) { + if (!switchIndex.allBitsValid()) + return false; + var instr = instructions[emulateIndex]; + var targets = (Instruction[])instr.Operand; + if (switchIndex.value >= 0 && switchIndex.value < targets.Length) + emulateIndex = instructions.IndexOf(targets[switchIndex.value]); + else + emulateIndex++; + return true; + } + + protected override bool deobfuscateInternal() { + bool changed = false; + var instructions = block.Instructions; + for (int i = 0; i < instructions.Count; i++) { + var instr = instructions[i].Instruction; + if (instr.OpCode.Code == Code.Call) + changed |= inlineMethod(instr, i); + } + instructions = null; + return changed; + } + + static bool canInline(MethodDefinition method) { + if (!DotNetUtils.isMethod(method, "System.Int32", "(System.Int32)")) + return false; + if (!method.IsAssembly) + return false; + if (method.GenericParameters.Count > 0) + return false; + + return method.IsStatic; + } + + bool canInline2(MethodDefinition method) { + return canInline(method) && method != blocks.Method; + } + + bool inlineMethod(Instruction callInstr, int instrIndex) { + var methodToInline = callInstr.Operand as MethodDefinition; + if (methodToInline == null) + return false; + + if (!canInline2(methodToInline)) + return false; + var body = methodToInline.Body; + if (body == null) + return false; + + if (instrIndex == 0) + return false; + + var ldci4 = block.Instructions[instrIndex - 1]; + if (!ldci4.isLdcI4()) + return false; + int newValue; + if (!getNewValue(methodToInline, ldci4.getLdcI4Value(), out newValue)) + return false; + + block.Instructions[instrIndex - 1] = new Instr(Instruction.Create(OpCodes.Nop)); + block.Instructions[instrIndex] = new Instr(DotNetUtils.createLdci4(newValue)); + return true; + } + + bool getNewValue(MethodDefinition method, int arg, out int newValue) { + newValue = 0; + emulator.init(method); + emulator.setArg(method.Parameters[0], new Int32Value(arg)); + + Instruction instr; + emulateIndex = 0; + instructions = method.Body.Instructions; + int counter = 0; + while (true) { + if (counter++ >= 50) + return false; + if (emulateIndex < 0 || emulateIndex >= instructions.Count) + return false; + instr = instructions[emulateIndex]; + switch (instr.OpCode.Code) { + case Code.Ldarg: + case Code.Ldarg_S: + case Code.Ldarg_0: + case Code.Ldarg_1: + case Code.Ldarg_2: + case Code.Ldarg_3: + case Code.Stloc: + case Code.Stloc_S: + case Code.Stloc_0: + case Code.Stloc_1: + case Code.Stloc_2: + case Code.Stloc_3: + case Code.Ldloc: + case Code.Ldloc_S: + case Code.Ldloc_0: + case Code.Ldloc_1: + case Code.Ldloc_2: + case Code.Ldloc_3: + case Code.Ldc_I4: + case Code.Ldc_I4_0: + case Code.Ldc_I4_1: + case Code.Ldc_I4_2: + case Code.Ldc_I4_3: + case Code.Ldc_I4_4: + case Code.Ldc_I4_5: + case Code.Ldc_I4_6: + case Code.Ldc_I4_7: + case Code.Ldc_I4_8: + case Code.Ldc_I4_M1: + case Code.Ldc_I4_S: + case Code.Add: + case Code.Sub: + case Code.Xor: + case Code.Or: + case Code.Nop: + case Code.Dup: + case Code.Mul: + case Code.Rem: + case Code.Div: + emulator.emulate(instr); + emulateIndex++; + break; + + case Code.Br: + case Code.Br_S: + case Code.Beq: + case Code.Beq_S: + case Code.Bge: + case Code.Bge_S: + case Code.Bge_Un: + case Code.Bge_Un_S: + case Code.Bgt: + case Code.Bgt_S: + case Code.Bgt_Un: + case Code.Bgt_Un_S: + case Code.Ble: + case Code.Ble_S: + case Code.Ble_Un: + case Code.Ble_Un_S: + case Code.Blt: + case Code.Blt_S: + case Code.Blt_Un: + case Code.Blt_Un_S: + case Code.Bne_Un: + case Code.Bne_Un_S: + case Code.Brfalse: + case Code.Brfalse_S: + case Code.Brtrue: + case Code.Brtrue_S: + case Code.Switch: + if (!branchEmulator.emulate(instr)) + return false; + break; + + case Code.Ret: + var retValue = emulator.pop(); + if (!retValue.isInt32()) + return false; + var retValue2 = (Int32Value)retValue; + if (!retValue2.allBitsValid()) + return false; + newValue = retValue2.value; + return true; + + default: + if (instr.OpCode.OpCodeType != OpCodeType.Prefix) + return false; + emulateIndex++; + break; + } + } + } + + protected override bool isCompatibleType(int paramIndex, TypeReference origType, TypeReference newType) { + if (MemberReferenceHelper.compareTypes(origType, newType)) + return true; + if (newType.IsValueType || origType.IsValueType) + return false; + return newType.FullName == "System.Object"; + } + } +} diff --git a/de4dot.code/deobfuscators/Babel_NET/BabelUtils.cs b/de4dot.code/deobfuscators/Babel_NET/BabelUtils.cs index bf06b3ad..4052e33c 100644 --- a/de4dot.code/deobfuscators/Babel_NET/BabelUtils.cs +++ b/de4dot.code/deobfuscators/Babel_NET/BabelUtils.cs @@ -17,6 +17,8 @@ along with de4dot. If not, see . */ +using System.Collections.Generic; +using System.Text; using Mono.Cecil; using Mono.Cecil.Cil; using de4dot.blocks; @@ -41,15 +43,60 @@ namespace de4dot.code.deobfuscators.Babel_NET { if (!method.IsStatic) continue; fixMethod(method); - foreach (var s in DotNetUtils.getCodeStrings(method)) { - var resource = DotNetUtils.getResource(module, s) as EmbeddedResource; - if (resource != null) - return resource; - } + var resource = findEmbeddedResource1(module, method) ?? findEmbeddedResource2(module, method); + if (resource != null) + return resource; } return null; } + static EmbeddedResource findEmbeddedResource1(ModuleDefinition module, MethodDefinition method) { + foreach (var s in DotNetUtils.getCodeStrings(method)) { + var resource = DotNetUtils.getResource(module, s) as EmbeddedResource; + if (resource != null) + return resource; + } + return null; + } + + static EmbeddedResource findEmbeddedResource2(ModuleDefinition module, MethodDefinition method) { + var strings = new List(DotNetUtils.getCodeStrings(method)); + if (strings.Count != 1) + return null; + var encryptedString = strings[0]; + + int xorKey; + if (!getXorKey2(method, out xorKey)) + return null; + + var sb = new StringBuilder(encryptedString.Length); + foreach (var c in encryptedString) + sb.Append((char)(c ^ xorKey)); + return DotNetUtils.getResource(module, sb.ToString()) as EmbeddedResource; + } + + static bool getXorKey2(MethodDefinition method, out int xorKey) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 2; i++) { + var ldelem = instrs[i]; + if (ldelem.OpCode.Code != Code.Ldelem_U2) + continue; + + var ldci4 = instrs[i + 1]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + + if (instrs[i + 2].OpCode.Code != Code.Xor) + continue; + + xorKey = DotNetUtils.getLdcI4Value(ldci4); + return true; + } + + xorKey = 0; + return false; + } + public static bool findRegisterMethod(TypeDefinition type, out MethodDefinition regMethod, out MethodDefinition handler) { foreach (var method in type.Methods) { if (!method.IsStatic || method.Body == null) diff --git a/de4dot.code/deobfuscators/Babel_NET/ConstantsDecrypter.cs b/de4dot.code/deobfuscators/Babel_NET/ConstantsDecrypter.cs index 758e793e..3ac7059b 100644 --- a/de4dot.code/deobfuscators/Babel_NET/ConstantsDecrypter.cs +++ b/de4dot.code/deobfuscators/Babel_NET/ConstantsDecrypter.cs @@ -28,6 +28,7 @@ using de4dot.blocks; namespace de4dot.code.deobfuscators.Babel_NET { class ConstantsDecrypter { ModuleDefinition module; + ResourceDecrypter resourceDecrypter; InitializedDataCreator initializedDataCreator; TypeDefinition decrypterType; MethodDefinition int32Decrypter; @@ -77,8 +78,9 @@ namespace de4dot.code.deobfuscators.Babel_NET { get { return arrayDecrypter; } } - public ConstantsDecrypter(ModuleDefinition module, InitializedDataCreator initializedDataCreator) { + public ConstantsDecrypter(ModuleDefinition module, ResourceDecrypter resourceDecrypter, InitializedDataCreator initializedDataCreator) { this.module = module; + this.resourceDecrypter = resourceDecrypter; this.initializedDataCreator = initializedDataCreator; } @@ -107,6 +109,8 @@ namespace de4dot.code.deobfuscators.Babel_NET { if (!checkNestedFields(nested)) return false; + resourceDecrypter.DecryptMethod = ResourceDecrypter.findDecrypterMethod(DotNetUtils.getMethod(nested, ".ctor")); + if (DotNetUtils.getMethod(type, "System.Int32", "(System.Int32)") == null) return false; if (DotNetUtils.getMethod(type, "System.Int64", "(System.Int32)") == null) @@ -147,7 +151,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { return; } - var decrypted = new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData()); + var decrypted = resourceDecrypter.decrypt(encryptedResource.GetResourceData()); var reader = new BinaryReader(new MemoryStream(decrypted)); int count; @@ -270,7 +274,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { } byte[] decryptArray(byte[] encryptedData, int elemSize) { - var decrypted = new ResourceDecrypter(module).decrypt(encryptedData); + var decrypted = resourceDecrypter.decrypt(encryptedData); var ary = (Array)new BinaryFormatter().Deserialize(new MemoryStream(decrypted)); if (ary is byte[]) return (byte[])ary; diff --git a/de4dot.code/deobfuscators/Babel_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Babel_NET/Deobfuscator.cs index 5146f19a..a6616e95 100644 --- a/de4dot.code/deobfuscators/Babel_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Babel_NET/Deobfuscator.cs @@ -21,11 +21,14 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using Mono.Cecil; using de4dot.blocks; +using de4dot.blocks.cflow; namespace de4dot.code.deobfuscators.Babel_NET { public class DeobfuscatorInfo : DeobfuscatorInfoBase { public const string THE_NAME = "Babel .NET"; public const string THE_TYPE = "bl"; + BoolOption inlineMethods; + BoolOption removeInlinedMethods; BoolOption decryptMethods; BoolOption decryptResources; BoolOption decryptConstants; @@ -33,6 +36,8 @@ namespace de4dot.code.deobfuscators.Babel_NET { public DeobfuscatorInfo() : base() { + inlineMethods = new BoolOption(null, makeArgName("inline"), "Inline short methods", true); + removeInlinedMethods = new BoolOption(null, makeArgName("remove-inlined"), "Remove inlined methods", true); decryptMethods = new BoolOption(null, makeArgName("methods"), "Decrypt methods", true); decryptResources = new BoolOption(null, makeArgName("rsrc"), "Decrypt resources", true); decryptConstants = new BoolOption(null, makeArgName("consts"), "Decrypt constants and arrays", true); @@ -50,6 +55,8 @@ namespace de4dot.code.deobfuscators.Babel_NET { public override IDeobfuscator createDeobfuscator() { return new Deobfuscator(new Deobfuscator.Options { ValidNameRegex = validNameRegex.get(), + InlineMethods = inlineMethods.get(), + RemoveInlinedMethods = removeInlinedMethods.get(), DecryptMethods = decryptMethods.get(), DecryptResources = decryptResources.get(), DecryptConstants = decryptConstants.get(), @@ -59,6 +66,8 @@ namespace de4dot.code.deobfuscators.Babel_NET { protected override IEnumerable