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