Add better BL support

This commit is contained in:
de4dot 2012-06-12 10:37:51 +02:00
parent 4a29eae1c8
commit fa594c6213
12 changed files with 1137 additions and 126 deletions

View File

@ -57,10 +57,13 @@
<Compile Include="DeobfuscatorContext.cs" />
<Compile Include="deobfuscators\ArrayFinder.cs" />
<Compile Include="deobfuscators\Babel_NET\AssemblyResolver.cs" />
<Compile Include="deobfuscators\Babel_NET\BabelInflater.cs" />
<Compile Include="deobfuscators\Babel_NET\BabelMethodCallInliner.cs" />
<Compile Include="deobfuscators\Babel_NET\BabelUtils.cs" />
<Compile Include="deobfuscators\Babel_NET\ConstantsDecrypter.cs" />
<Compile Include="deobfuscators\Babel_NET\Deobfuscator.cs" />
<Compile Include="deobfuscators\Babel_NET\ImageReader.cs" />
<Compile Include="deobfuscators\Babel_NET\InflaterCreator.cs" />
<Compile Include="deobfuscators\Babel_NET\MemberReferenceConverter.cs" />
<Compile Include="deobfuscators\Babel_NET\MethodBodyReader.cs" />
<Compile Include="deobfuscators\Babel_NET\MethodReferenceReader.cs" />

View File

@ -17,6 +17,7 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
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];

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Instruction> instructions;
public BabelMethodCallInliner() {
emulator = new InstructionEmulator();
branchEmulator = new BranchEmulator(emulator, this);
}
public static List<MethodDefinition> find(ModuleDefinition module, IEnumerable<MethodDefinition> notInlinedMethods) {
var notInlinedMethodsDict = new Dictionary<MethodDefinition, bool>();
foreach (var method in notInlinedMethods)
notInlinedMethodsDict[method] = true;
var inlinedMethods = new List<MethodDefinition>();
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";
}
}
}

View File

@ -17,6 +17,8 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
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<string>(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)

View File

@ -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;

View File

@ -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<Option> getOptionsInternal() {
return new List<Option>() {
inlineMethods,
removeInlinedMethods,
decryptMethods,
decryptResources,
decryptConstants,
@ -71,6 +80,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
Options options;
bool foundBabelAttribute = false;
string obfuscatorName = DeobfuscatorInfo.THE_NAME;
bool startedDeobfuscating = false;
ResourceResolver resourceResolver;
AssemblyResolver assemblyResolver;
@ -84,6 +94,8 @@ namespace de4dot.code.deobfuscators.Babel_NET {
MethodsDecrypter methodsDecrypter;
internal class Options : OptionsBase {
public bool InlineMethods { get; set; }
public bool RemoveInlinedMethods { get; set; }
public bool DecryptMethods { get; set; }
public bool DecryptResources { get; set; }
public bool DecryptConstants { get; set; }
@ -102,6 +114,19 @@ namespace de4dot.code.deobfuscators.Babel_NET {
get { return obfuscatorName; }
}
protected override bool CanInlineMethods {
get { return startedDeobfuscating ? options.InlineMethods : true; }
}
public override IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators {
get {
var list = new List<IBlocksDeobfuscator>();
if (CanInlineMethods)
list.Add(new BabelMethodCallInliner());
return list;
}
}
public Deobfuscator(Options options)
: base(options) {
this.options = options;
@ -131,17 +156,18 @@ namespace de4dot.code.deobfuscators.Babel_NET {
protected override void scanForObfuscator() {
findBabelAttribute();
resourceResolver = new ResourceResolver(module);
var resourceDecrypterCreator = new ResourceDecrypterCreator(module, DeobfuscatedFile);
resourceResolver = new ResourceResolver(module, resourceDecrypterCreator.create(), DeobfuscatedFile);
resourceResolver.find();
assemblyResolver = new AssemblyResolver(module);
assemblyResolver = new AssemblyResolver(module, resourceDecrypterCreator.create());
assemblyResolver.find();
stringDecrypter = new StringDecrypter(module);
stringDecrypter = new StringDecrypter(module, resourceDecrypterCreator.create());
stringDecrypter.find(DeobfuscatedFile);
constantsDecrypter = new ConstantsDecrypter(module, initializedDataCreator);
constantsDecrypter = new ConstantsDecrypter(module, resourceDecrypterCreator.create(), initializedDataCreator);
constantsDecrypter.find();
proxyCallFixer = new ProxyCallFixer(module);
proxyCallFixer.findDelegateCreator();
methodsDecrypter = new MethodsDecrypter(module, DeobfuscatedFile.DeobfuscatorContext);
methodsDecrypter = new MethodsDecrypter(module, resourceDecrypterCreator.create(), DeobfuscatedFile.DeobfuscatorContext);
methodsDecrypter.find();
}
@ -216,6 +242,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
}
proxyCallFixer.find();
startedDeobfuscating = true;
}
void dumpEmbeddedAssemblies() {
@ -250,6 +277,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
}
public override void deobfuscateEnd() {
removeInlinedMethods();
if (CanRemoveStringDecrypterType) {
addResourceToBeRemoved(stringDecrypter.Resource, "Encrypted strings");
addTypeToBeRemoved(stringDecrypter.Type, "String decrypter type");
@ -259,6 +287,12 @@ namespace de4dot.code.deobfuscators.Babel_NET {
base.deobfuscateEnd();
}
void removeInlinedMethods() {
if (!options.InlineMethods || !options.RemoveInlinedMethods)
return;
removeInlinedMethods(BabelMethodCallInliner.find(module, staticStringInliner.Methods));
}
public override IEnumerable<int> getStringDecrypterMethods() {
var list = new List<int>();
if (stringDecrypter.DecryptMethod != null)

View File

@ -0,0 +1,117 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
using ICSharpCode.SharpZipLib.Zip.Compression;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Babel_NET {
class InflaterCreator {
public static Inflater create(MethodDefinition method, bool noHeader) {
return create(findInflaterType(method), noHeader);
}
public static Inflater create(TypeDefinition inflaterType, bool noHeader) {
if (inflaterType == null)
return createNormal(noHeader);
var initHeaderMethod = findInitHeaderMethod(inflaterType);
if (initHeaderMethod == null)
return createNormal(noHeader, "Could not find inflater init header method");
var magic = getMagic(initHeaderMethod);
if (!magic.HasValue)
return createNormal(noHeader);
return new BabelInflater(noHeader, magic.Value);
}
static Inflater createNormal(bool noHeader, string errorMessage = null) {
if (errorMessage != null)
Log.w("{0}", errorMessage);
return new Inflater(noHeader);
}
static TypeDefinition findInflaterType(MethodDefinition method) {
if (method == null || method.Body == null)
return null;
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code != Code.Call)
continue;
var calledMethod = instr.Operand as MethodDefinition;
if (calledMethod == null || !calledMethod.IsStatic)
continue;
var type = calledMethod.DeclaringType;
foreach (var nested in type.NestedTypes) {
if (DeobUtils.hasInteger(DotNetUtils.getMethod(nested, ".ctor"), 0x8001))
return type;
}
}
return null;
}
static MethodDefinition findInitHeaderMethod(TypeDefinition inflaterType) {
foreach (var nested in inflaterType.NestedTypes) {
var method = findInitHeaderMethod2(nested);
if (method != null)
return method;
}
return null;
}
static MethodDefinition findInitHeaderMethod2(TypeDefinition nested) {
foreach (var method in nested.Methods) {
if (method.IsStatic || method.Body == null)
continue;
if (!DotNetUtils.isMethod(method, "System.Boolean", "()"))
continue;
return method;
}
return null;
}
static int? getMagic(MethodDefinition method) {
if (method == null || method.Body == null)
return null;
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count - 3; i++) {
var ldci4_1 = instrs[i];
if (!DotNetUtils.isLdcI4(ldci4_1) || DotNetUtils.getLdcI4Value(ldci4_1) != 16)
continue;
var callvirt = instrs[i + 1];
if (callvirt.OpCode.Code != Code.Callvirt)
continue;
var ldci4_2 = instrs[i + 2];
if (!DotNetUtils.isLdcI4(ldci4_2))
continue;
if (instrs[i + 3].OpCode.Code != Code.Xor)
continue;
return DotNetUtils.getLdcI4Value(ldci4_2);
}
return null;
}
}
}

View File

@ -27,6 +27,7 @@ using de4dot.blocks;
namespace de4dot.code.deobfuscators.Babel_NET {
class MethodsDecrypter {
ModuleDefinition module;
ResourceDecrypter resourceDecrypter;
IDeobfuscatorContext deobfuscatorContext;
Dictionary<string, ImageReader> imageReaders = new Dictionary<string, ImageReader>(StringComparer.Ordinal);
TypeDefinition methodsDecrypterCreator;
@ -38,8 +39,9 @@ namespace de4dot.code.deobfuscators.Babel_NET {
get { return methodsDecrypterCreator != null; }
}
public MethodsDecrypter(ModuleDefinition module, IDeobfuscatorContext deobfuscatorContext) {
public MethodsDecrypter(ModuleDefinition module, ResourceDecrypter resourceDecrypter, IDeobfuscatorContext deobfuscatorContext) {
this.module = module;
this.resourceDecrypter = resourceDecrypter;
this.deobfuscatorContext = deobfuscatorContext;
}
@ -62,6 +64,8 @@ namespace de4dot.code.deobfuscators.Babel_NET {
if (decrypterType == null)
continue;
resourceDecrypter.DecryptMethod = findDecryptMethod(decrypterType);
methodsDecrypterCreator = type;
methodsDecrypter = decrypterType;
decryptExecuteMethod = executeMethod;
@ -69,6 +73,15 @@ namespace de4dot.code.deobfuscators.Babel_NET {
}
}
static MethodDefinition findDecryptMethod(TypeDefinition type) {
foreach (var method in type.Methods) {
var decryptMethod = ResourceDecrypter.findDecrypterMethod(method);
if (decryptMethod != null)
return decryptMethod;
}
return null;
}
TypeDefinition findMethodsDecrypterType(TypeDefinition type) {
foreach (var field in type.Fields) {
var fieldType = DotNetUtils.getType(module, field.FieldType);
@ -92,29 +105,35 @@ namespace de4dot.code.deobfuscators.Babel_NET {
return;
encryptedResource = BabelUtils.findEmbeddedResource(module, methodsDecrypter, simpleDeobfuscator, deob);
if (encryptedResource == null) {
Log.w("Could not find encrypted methods resource");
return;
}
addImageReader("", new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData()));
if (encryptedResource != null)
addImageReader("", resourceDecrypter.decrypt(encryptedResource.GetResourceData()));
}
void addImageReader(string name, byte[] data) {
ImageReader addImageReader(string name, byte[] data) {
var imageReader = new ImageReader(deobfuscatorContext, module, data);
if (!imageReader.initialize()) {
Log.w("Could not read encrypted methods");
return;
return null;
}
if (imageReaders.ContainsKey(name))
throw new ApplicationException(string.Format("ImageReader for name '{0}' already exists", name));
imageReaders[name] = imageReader;
return imageReader;
}
class EncryptInfo {
public string encryptedMethodName;
public string feature;
public MethodDefinition method;
public string FullName {
get {
if (string.IsNullOrEmpty(feature))
return encryptedMethodName;
return string.Format("{0}:{1}", feature, encryptedMethodName);
}
}
public EncryptInfo(string encryptedMethodName, string feature, MethodDefinition method) {
this.encryptedMethodName = encryptedMethodName;
this.feature = feature;
@ -134,18 +153,53 @@ namespace de4dot.code.deobfuscators.Babel_NET {
int totalEncryptedMethods = 0;
foreach (var info in getEncryptedMethods()) {
totalEncryptedMethods++;
ImageReader imageReader;
if (!imageReaders.TryGetValue(info.feature, out imageReader)) {
var imageReader = getImageReader(info.feature);
if (imageReader == null) {
numNonDecryptedMethods++;
continue;
}
Log.v("Decrypting method {0:X8}", info.method.MetadataToken.ToInt32());
imageReader.restore(info.encryptedMethodName, info.method);
imageReader.restore(info.FullName, info.method);
}
if (numNonDecryptedMethods > 0)
Log.w("{0}/{1} methods not decrypted", numNonDecryptedMethods, totalEncryptedMethods);
}
ImageReader getImageReader(string feature) {
ImageReader imageReader;
if (imageReaders.TryGetValue(feature, out imageReader))
return imageReader;
return createImageReader(feature);
}
ImageReader createImageReader(string feature) {
if (string.IsNullOrEmpty(feature))
return null;
try {
var encrypted = File.ReadAllBytes(getFile(Path.GetDirectoryName(module.FullyQualifiedName), feature));
var decrypted = resourceDecrypter.decrypt(encrypted);
return addImageReader(feature, decrypted);
}
catch {
return null;
}
}
static string getFile(string dir, string name) {
try {
var di = new DirectoryInfo(dir);
foreach (var file in di.GetFiles()) {
if (Utils.StartsWith(file.Name, name, StringComparison.OrdinalIgnoreCase))
return file.FullName;
}
}
catch {
}
return null;
}
List<EncryptInfo> getEncryptedMethods() {
var infos = new List<EncryptInfo>();

View File

@ -19,104 +19,243 @@
using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip.Compression;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Babel_NET {
class ResourceDecrypterCreator {
ModuleDefinition module;
ISimpleDeobfuscator simpleDeobfuscator;
public ResourceDecrypterCreator(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator) {
this.module = module;
this.simpleDeobfuscator = simpleDeobfuscator;
}
public ResourceDecrypter create() {
return new ResourceDecrypter(module, simpleDeobfuscator);
}
}
class ResourceDecrypter {
ModuleDefinition module;
ISimpleDeobfuscator simpleDeobfuscator;
MethodDefinition decryptMethod;
IDecrypter decrypter;
public ResourceDecrypter(ModuleDefinition module) {
public ResourceDecrypter(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator) {
this.module = module;
this.simpleDeobfuscator = simpleDeobfuscator;
}
interface IDecrypter {
byte[] decrypt(byte[] encryptedData);
}
// v3.0
class Decrypter1 : IDecrypter {
ModuleDefinition module;
public Decrypter1(ModuleDefinition module) {
this.module = module;
}
public byte[] decrypt(byte[] encryptedData) {
byte[] key, iv;
var reader = new BinaryReader(new MemoryStream(encryptedData));
bool isCompressed = getHeaderData(reader, out key, out iv);
var data = DeobUtils.desDecrypt(encryptedData,
(int)reader.BaseStream.Position,
(int)(reader.BaseStream.Length - reader.BaseStream.Position),
key, iv);
if (isCompressed)
data = DeobUtils.inflate(data, true);
return data;
}
bool getHeaderData(BinaryReader reader, out byte[] key, out byte[] iv) {
iv = reader.ReadBytes(reader.ReadByte());
bool hasEmbeddedKey = reader.ReadBoolean();
if (hasEmbeddedKey)
key = reader.ReadBytes(reader.ReadByte());
else {
key = new byte[reader.ReadByte()];
Array.Copy(module.Assembly.Name.PublicKey, 0, key, 0, key.Length);
}
reader.ReadBytes(reader.ReadInt32()); // hash
return true;
}
}
// v3.5+
class Decrypter2 : IDecrypter {
ModuleDefinition module;
public Decrypter2(ModuleDefinition module) {
this.module = module;
}
public byte[] decrypt(byte[] encryptedData) {
int index = 0;
byte[] key, iv;
bool isCompressed = getKeyIv(getHeaderData(encryptedData, ref index), out key, out iv);
var data = DeobUtils.desDecrypt(encryptedData, index, encryptedData.Length - index, key, iv);
if (isCompressed)
data = DeobUtils.inflate(data, true);
return data;
}
byte[] getHeaderData(byte[] encryptedData, ref int index) {
bool xorDecrypt = encryptedData[index++] != 0;
var headerData = new byte[BitConverter.ToUInt16(encryptedData, index)];
Array.Copy(encryptedData, index + 2, headerData, 0, headerData.Length);
index += headerData.Length + 2;
if (!xorDecrypt)
return headerData;
var key = new byte[8];
Array.Copy(encryptedData, index, key, 0, key.Length);
index += key.Length;
for (int i = 0; i < headerData.Length; i++)
headerData[i] ^= key[i % key.Length];
return headerData;
}
bool getKeyIv(byte[] headerData, out byte[] key, out byte[] iv) {
var reader = new BinaryReader(new MemoryStream(headerData));
// 3.0 - 3.5 don't have this field
if (headerData[(int)reader.BaseStream.Position] != 8) {
var license = reader.ReadString();
}
// 4.2 (and earlier?) always compress the data
bool isCompressed = true;
if (headerData[(int)reader.BaseStream.Position] != 8)
isCompressed = reader.ReadBoolean();
iv = reader.ReadBytes(reader.ReadByte());
bool hasEmbeddedKey = reader.ReadBoolean();
if (hasEmbeddedKey)
key = reader.ReadBytes(reader.ReadByte());
else {
key = new byte[reader.ReadByte()];
Array.Copy(module.Assembly.Name.PublicKey, 12, key, 0, key.Length);
key[5] |= 0x80;
}
return isCompressed;
}
}
// v5.0+ retail
class Decrypter3 : IDecrypter {
ModuleDefinition module;
Inflater inflater;
public Decrypter3(ModuleDefinition module, MethodDefinition decryptMethod) {
this.module = module;
this.inflater = InflaterCreator.create(decryptMethod, true);
}
public byte[] decrypt(byte[] encryptedData) {
int index = 0;
byte[] key, iv;
bool isCompressed = getKeyIv(getHeaderData(encryptedData, ref index), out key, out iv);
var data = DeobUtils.desDecrypt(encryptedData, index, encryptedData.Length - index, key, iv);
if (isCompressed)
data = DeobUtils.inflate(data, inflater);
return data;
}
byte[] getHeaderData(byte[] encryptedData, ref int index) {
bool xorDecrypt = encryptedData[index++] != 0;
var headerData = new byte[BitConverter.ToUInt16(encryptedData, index)];
Array.Copy(encryptedData, index + 2, headerData, 0, headerData.Length);
index += headerData.Length + 2;
if (!xorDecrypt)
return headerData;
var key = new byte[6];
Array.Copy(encryptedData, index, key, 0, key.Length);
index += key.Length;
for (int i = 0; i < headerData.Length; i++)
headerData[i] ^= key[i % key.Length];
return headerData;
}
bool getKeyIv(byte[] headerData, out byte[] key, out byte[] iv) {
var reader = new BinaryReader(new MemoryStream(headerData));
var license = reader.ReadString();
bool isCompressed = reader.ReadBoolean();
var unkData = reader.ReadBytes(reader.ReadInt32());
bool hasEmbeddedKey = reader.ReadBoolean();
iv = reader.ReadBytes(reader.ReadByte());
if (hasEmbeddedKey)
key = reader.ReadBytes(reader.ReadByte());
else {
key = new byte[reader.ReadByte()];
Array.Copy(module.Assembly.Name.PublicKey, 12, key, 0, key.Length);
key[5] |= 0x80;
}
return isCompressed;
}
}
public MethodDefinition DecryptMethod {
set {
if (value == null)
return;
if (decryptMethod == null) {
decryptMethod = value;
simpleDeobfuscator.deobfuscate(decryptMethod);
}
else if (decryptMethod != value)
throw new ApplicationException("Found another decrypter method");
}
}
public static MethodDefinition findDecrypterMethod(MethodDefinition method) {
if (method == null || method.Body == null)
return null;
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code != Code.Call)
continue;
var calledMethod = instr.Operand as MethodDefinition;
if (calledMethod == null || !calledMethod.IsStatic || calledMethod.Body == null)
continue;
if (!DotNetUtils.isMethod(calledMethod, "System.IO.MemoryStream", "(System.IO.Stream)"))
continue;
return calledMethod;
}
return null;
}
public byte[] decrypt(byte[] encryptedData) {
if (decrypter == null)
decrypter = createDecrypter(encryptedData);
return decrypter.decrypt(encryptedData);
}
IDecrypter createDecrypter(byte[] encryptedData) {
if (decryptMethod != null && DeobUtils.hasInteger(decryptMethod, 6))
return new Decrypter3(module, decryptMethod);
if (isV30(encryptedData))
return decryptV30(encryptedData);
return decryptV35(encryptedData);
return new Decrypter1(module);
return new Decrypter2(module);
}
static bool isV30(byte[] data) {
return data.Length > 10 && data[0] == 8 && data[9] <= 1 && data[10] == 8;
}
// v3.0
byte[] decryptV30(byte[] encryptedData) {
byte[] key, iv;
var reader = new BinaryReader(new MemoryStream(encryptedData));
bool isCompressed = getHeaderData30(reader, out key, out iv);
var data = DeobUtils.desDecrypt(encryptedData,
(int)reader.BaseStream.Position,
(int)(reader.BaseStream.Length - reader.BaseStream.Position),
key, iv);
if (isCompressed)
data = DeobUtils.inflate(data, true);
return data;
}
bool getHeaderData30(BinaryReader reader, out byte[] key, out byte[] iv) {
iv = reader.ReadBytes(reader.ReadByte());
bool hasEmbeddedKey = reader.ReadBoolean();
if (hasEmbeddedKey)
key = reader.ReadBytes(reader.ReadByte());
else {
key = new byte[reader.ReadByte()];
Array.Copy(module.Assembly.Name.PublicKey, 0, key, 0, key.Length);
}
reader.ReadBytes(reader.ReadInt32()); // hash
return true;
}
// v3.5+
byte[] decryptV35(byte[] encryptedData) {
int index = 0;
byte[] key, iv;
bool isCompressed = getKeyIv(getHeaderData(encryptedData, ref index), out key, out iv);
var data = DeobUtils.desDecrypt(encryptedData, index, encryptedData.Length - index, key, iv);
if (isCompressed)
data = DeobUtils.inflate(data, true);
return data;
}
byte[] getHeaderData(byte[] encryptedData, ref int index) {
bool xorDecrypt = encryptedData[index++] != 0;
var headerData = new byte[BitConverter.ToUInt16(encryptedData, index)];
Array.Copy(encryptedData, index + 2, headerData, 0, headerData.Length);
index += headerData.Length + 2;
if (!xorDecrypt)
return headerData;
var key = new byte[8];
Array.Copy(encryptedData, index, key, 0, key.Length);
index += key.Length;
for (int i = 0; i < headerData.Length; i++)
headerData[i] ^= key[i % key.Length];
return headerData;
}
bool getKeyIv(byte[] headerData, out byte[] key, out byte[] iv) {
var reader = new BinaryReader(new MemoryStream(headerData));
// 3.0 - 3.5 don't have this field
if (headerData[(int)reader.BaseStream.Position] != 8) {
var license = reader.ReadString();
}
// 4.2 (and earlier?) always compress the data
bool isCompressed = true;
if (headerData[(int)reader.BaseStream.Position] != 8)
isCompressed = reader.ReadBoolean();
iv = reader.ReadBytes(reader.ReadByte());
bool hasEmbeddedKey = reader.ReadBoolean();
if (hasEmbeddedKey)
key = reader.ReadBytes(reader.ReadByte());
else {
key = new byte[reader.ReadByte()];
Array.Copy(module.Assembly.Name.PublicKey, 12, key, 0, key.Length);
key[5] |= 0x80;
}
return isCompressed;
}
}
}

View File

@ -17,16 +17,23 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.IO;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Babel_NET {
class ResourceResolver {
ModuleDefinition module;
ResourceDecrypter resourceDecrypter;
ISimpleDeobfuscator simpleDeobfuscator;
TypeDefinition resolverType;
MethodDefinition registerMethod;
EmbeddedResource encryptedResource;
bool hasXorKeys;
int xorKey1, xorKey2;
public bool Detected {
get { return resolverType != null; }
@ -40,8 +47,10 @@ namespace de4dot.code.deobfuscators.Babel_NET {
get { return registerMethod; }
}
public ResourceResolver(ModuleDefinition module) {
public ResourceResolver(ModuleDefinition module, ResourceDecrypter resourceDecrypter, ISimpleDeobfuscator simpleDeobfuscator) {
this.module = module;
this.resourceDecrypter = resourceDecrypter;
this.simpleDeobfuscator = simpleDeobfuscator;
}
public void find() {
@ -54,7 +63,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
foreach (var type in module.Types) {
if (type.HasEvents)
continue;
if (!new FieldTypes(type).exactly(requiredTypes))
if (!new FieldTypes(type).all(requiredTypes))
continue;
MethodDefinition regMethod, handler;
@ -65,6 +74,12 @@ namespace de4dot.code.deobfuscators.Babel_NET {
if (resource == null)
continue;
var decryptMethod = findDecryptMethod(type);
if (decryptMethod == null)
throw new ApplicationException("Couldn't find resource type decrypt method");
resourceDecrypter.DecryptMethod = ResourceDecrypter.findDecrypterMethod(decryptMethod);
initXorKeys(decryptMethod);
resolverType = type;
registerMethod = regMethod;
encryptedResource = resource;
@ -72,6 +87,46 @@ namespace de4dot.code.deobfuscators.Babel_NET {
}
}
static MethodDefinition findDecryptMethod(TypeDefinition type) {
foreach (var method in type.Methods) {
if (!DotNetUtils.isMethod(method, "System.Reflection.Assembly", "(System.IO.Stream)"))
continue;
return method;
}
return null;
}
void initXorKeys(MethodDefinition method) {
simpleDeobfuscator.deobfuscate(method);
var ints = new List<int>();
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var callvirt = instrs[i];
if (callvirt.OpCode.Code != Code.Callvirt)
continue;
var calledMethod = callvirt.Operand as MethodReference;
if (calledMethod == null)
continue;
if (calledMethod.FullName != "System.Int32 System.IO.BinaryReader::ReadInt32()")
continue;
var ldci4 = instrs[i + 1];
if (!DotNetUtils.isLdcI4(ldci4))
continue;
if (instrs[i + 2].OpCode.Code != Code.Xor)
continue;
ints.Add(DotNetUtils.getLdcI4Value(ldci4));
}
if (ints.Count == 2) {
hasXorKeys = true;
xorKey1 = ints[0];
xorKey2 = ints[1];
}
}
public EmbeddedResource mergeResources() {
if (encryptedResource == null)
return null;
@ -82,12 +137,19 @@ namespace de4dot.code.deobfuscators.Babel_NET {
}
byte[] decryptResourceAssembly() {
var decrypted = new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData());
var decrypted = resourceDecrypter.decrypt(encryptedResource.GetResourceData());
var reader = new BinaryReader(new MemoryStream(decrypted));
int numResources = reader.ReadInt32();
int numResources = reader.ReadInt32() ^ xorKey1;
for (int i = 0; i < numResources; i++)
reader.ReadString();
return reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));
int len;
if (hasXorKeys)
len = reader.ReadInt32() ^ xorKey2;
else
len = (int)(reader.BaseStream.Length - reader.BaseStream.Position);
return reader.ReadBytes(len);
}
}
}

View File

@ -24,10 +24,12 @@ using System.Text;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
using de4dot.blocks.cflow;
namespace de4dot.code.deobfuscators.Babel_NET {
class StringDecrypter {
ModuleDefinition module;
ResourceDecrypter resourceDecrypter;
ISimpleDeobfuscator simpleDeobfuscator;
TypeDefinition decrypterType;
EmbeddedResource encryptedResource;
@ -91,18 +93,39 @@ namespace de4dot.code.deobfuscators.Babel_NET {
class DecrypterInfoV3 : IDecrypterInfo {
Dictionary<int, string> offsetToString = new Dictionary<int, string>();
ResourceDecrypter resourceDecrypter;
InstructionEmulator emulator = new InstructionEmulator();
public int OffsetMagic { get; set; }
public IList<Instruction> OffsetCalcInstructions { get; set; }
public MethodDefinition Decrypter { get; set; }
public bool NeedsResource {
get { return true; }
}
public DecrypterInfoV3(ResourceDecrypter resourceDecrypter) {
this.resourceDecrypter = resourceDecrypter;
}
public void initialize(ModuleDefinition module, EmbeddedResource resource) {
var decrypted = new ResourceDecrypter(module).decrypt(resource.GetResourceData());
var decrypted = resourceDecrypter.decrypt(resource.GetResourceData());
var reader = new BinaryReader(new MemoryStream(decrypted));
while (reader.BaseStream.Position < reader.BaseStream.Length)
offsetToString[(int)reader.BaseStream.Position] = reader.ReadString();
offsetToString[getOffset((int)reader.BaseStream.Position)] = reader.ReadString();
}
MethodDefinition dummyMethod;
int getOffset(int offset) {
if (OffsetCalcInstructions == null || OffsetCalcInstructions.Count == 0)
return offset;
if (dummyMethod == null) {
dummyMethod = new MethodDefinition("", 0, new TypeReference("", "", null, null));
dummyMethod.Body = new MethodBody(dummyMethod);
}
emulator.init(dummyMethod);
emulator.push(new Int32Value(offset));
foreach (var instr in OffsetCalcInstructions)
emulator.emulate(instr);
return ((Int32Value)emulator.pop()).value;
}
public string decrypt(object[] args) {
@ -110,7 +133,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
}
string decrypt(int offset) {
return offsetToString[offset ^ OffsetMagic];
return offsetToString[offset];
}
}
@ -130,8 +153,9 @@ namespace de4dot.code.deobfuscators.Babel_NET {
get { return encryptedResource; }
}
public StringDecrypter(ModuleDefinition module) {
public StringDecrypter(ModuleDefinition module, ResourceDecrypter resourceDecrypter) {
this.module = module;
this.resourceDecrypter = resourceDecrypter;
}
public void find(ISimpleDeobfuscator simpleDeobfuscator) {
@ -225,16 +249,18 @@ namespace de4dot.code.deobfuscators.Babel_NET {
if (DotNetUtils.getMethod(nested, ".ctor") == null)
return null;
if (nested.Fields.Count == 1) {
if (nested.Fields.Count == 1 || nested.Fields.Count == 3) {
// 4.0+
if (!MemberReferenceHelper.compareTypes(nested.Fields[0].FieldType, nested))
if (!hasFieldType(nested.Fields, nested))
return null;
var decrypterBuilderMethod = DotNetUtils.getMethod(nested, "System.Reflection.Emit.MethodBuilder", "(System.Reflection.Emit.TypeBuilder)");
if (decrypterBuilderMethod == null)
return null;
resourceDecrypter.DecryptMethod = ResourceDecrypter.findDecrypterMethod(DotNetUtils.getMethod(nested, ".ctor"));
var nestedDecrypter = DotNetUtils.getMethod(nested, "System.String", "(System.Int32)");
if (nestedDecrypter == null || nestedDecrypter.IsStatic)
return null;
@ -243,16 +269,16 @@ namespace de4dot.code.deobfuscators.Babel_NET {
return null;
simpleDeobfuscator.deobfuscate(decrypterBuilderMethod);
return new DecrypterInfoV3 {
return new DecrypterInfoV3(resourceDecrypter) {
Decrypter = decrypter,
OffsetMagic = getOffsetMagic(decrypterBuilderMethod),
OffsetCalcInstructions = getOffsetCalcInstructions(decrypterBuilderMethod),
};
}
else if (nested.Fields.Count == 2) {
// 3.0 - 3.5
if (checkFields(nested, "System.Collections.Hashtable", nested)) {
// 3.5
// 3.0 - 3.5
var nestedDecrypter = DotNetUtils.getMethod(nested, "System.String", "(System.Int32)");
if (nestedDecrypter == null || nestedDecrypter.IsStatic)
return null;
@ -260,7 +286,9 @@ namespace de4dot.code.deobfuscators.Babel_NET {
if (decrypter == null || !decrypter.IsStatic)
return null;
return new DecrypterInfoV3 { Decrypter = decrypter };
resourceDecrypter.DecryptMethod = ResourceDecrypter.findDecrypterMethod(DotNetUtils.getMethod(nested, ".ctor"));
return new DecrypterInfoV3(resourceDecrypter) { Decrypter = decrypter };
}
else if (checkFields(nested, "System.Byte[]", nested)) {
// 3.0
@ -280,6 +308,205 @@ namespace de4dot.code.deobfuscators.Babel_NET {
return null;
}
class ReflectionToCecilMethodCreator {
MethodDefinition method;
List<Instruction> instructions = new List<Instruction>();
InstructionEmulator emulator;
int index;
class UserValue : UnknownValue {
public readonly object obj;
public UserValue(object obj) {
this.obj = obj;
}
public override string ToString() {
if (obj == null)
return "<null>";
return obj.ToString();
}
}
public List<Instruction> Instructions {
get { return instructions; }
}
public ReflectionToCecilMethodCreator(MethodDefinition method) {
this.method = method;
this.emulator = new InstructionEmulator(method);
}
public bool create() {
int arrayIndex;
Value array;
object value;
while (true) {
var instr = method.Body.Instructions[index];
switch (instr.OpCode.Code) {
case Code.Ret:
return true;
case Code.Newarr:
var arrayType = (TypeReference)instr.Operand;
int arrayCount = ((Int32Value)emulator.pop()).value;
if (arrayType.FullName == "System.Char")
emulator.push(new UserValue(new char[arrayCount]));
else
emulator.push(new UnknownValue());
break;
case Code.Call:
case Code.Callvirt:
if (!doCall(instr))
return false;
break;
case Code.Ldelem_U1:
arrayIndex = ((Int32Value)emulator.pop()).value;
array = (Value)emulator.pop();
if (array is UserValue)
emulator.push(new Int32Value(((byte[])((UserValue)array).obj)[arrayIndex]));
else
emulator.push(Int32Value.createUnknownUInt8());
break;
case Code.Stelem_I1:
value = emulator.pop();
arrayIndex = ((Int32Value)emulator.pop()).value;
array = (Value)emulator.pop();
if (array is UserValue)
((byte[])((UserValue)array).obj)[arrayIndex] = (byte)((Int32Value)value).value;
break;
case Code.Stelem_I2:
value = emulator.pop();
arrayIndex = ((Int32Value)emulator.pop()).value;
array = (Value)emulator.pop();
if (array is UserValue)
((char[])((UserValue)array).obj)[arrayIndex] = (char)((Int32Value)value).value;
break;
case Code.Ldelem_Ref:
arrayIndex = ((Int32Value)emulator.pop()).value;
array = (Value)emulator.pop();
var userValue = array as UserValue;
if (userValue != null && userValue.obj is string[])
emulator.push(new StringValue(((string[])userValue.obj)[arrayIndex]));
else
emulator.push(new UnknownValue());
break;
case Code.Ldsfld:
emulator.push(new UserValue((FieldReference)instr.Operand));
break;
default:
emulator.emulate(instr);
break;
}
index++;
}
}
bool doCall(Instruction instr) {
var calledMethod = (MethodReference)instr.Operand;
if (calledMethod.FullName == "System.Byte[] System.Convert::FromBase64String(System.String)") {
emulator.push(new UserValue(Convert.FromBase64String(((StringValue)emulator.pop()).value)));
return true;
}
else if (calledMethod.FullName == "System.String System.Text.Encoding::GetString(System.Byte[])") {
emulator.push(new StringValue(Encoding.UTF8.GetString((byte[])((UserValue)emulator.pop()).obj)));
return true;
}
else if (calledMethod.FullName == "System.Int32 System.Int32::Parse(System.String)") {
emulator.push(new Int32Value(int.Parse(((StringValue)emulator.pop()).value)));
return true;
}
else if (calledMethod.FullName == "System.String[] System.String::Split(System.Char[])") {
var ary = (char[])((UserValue)emulator.pop()).obj;
var s = ((StringValue)emulator.pop()).value;
emulator.push(new UserValue(s.Split(ary)));
return true;
}
else if (calledMethod.HasThis && calledMethod.DeclaringType.FullName == "System.Reflection.Emit.ILGenerator" && calledMethod.Name == "Emit") {
Value operand = null;
if (calledMethod.Parameters.Count == 2)
operand = emulator.pop();
var opcode = reflectionToCecilOpCode((FieldReference)((UserValue)emulator.pop()).obj);
emulator.pop(); // the this ptr
addInstruction(new Instruction {
OpCode = opcode,
Operand = createCecilOperand(opcode, operand),
});
return true;
}
else {
emulator.emulate(instr);
return true;
}
}
object createCecilOperand(OpCode opcode, Value op) {
if (op is Int32Value)
return ((Int32Value)op).value;
if (op is StringValue)
return ((StringValue)op).value;
return null;
}
void addInstruction(Instruction instr) {
instructions.Add(instr);
}
static OpCode reflectionToCecilOpCode(FieldReference reflectionField) {
var field = typeof(OpCodes).GetField(reflectionField.Name);
if (field == null || field.FieldType != typeof(OpCode))
return null;
return (OpCode)field.GetValue(null);
}
}
static List<Instruction> getOffsetCalcInstructions(MethodDefinition method) {
var creator = new ReflectionToCecilMethodCreator(method);
creator.create();
var instrs = creator.Instructions;
int index = 0;
index = findInstruction(instrs, index, OpCodes.Conv_I4);
if (index < 0)
return null;
int startInstr = ++index;
index = findInstruction(instrs, index, OpCodes.Box);
if (index < 0)
return null;
int endInstr = index - 1;
var transformInstructions = new List<Instruction>();
for (int i = startInstr; i <= endInstr; i++)
transformInstructions.Add(instrs[i]);
return transformInstructions;
}
static int findInstruction(IList<Instruction> instrs, int index, OpCode opcode) {
if (index < 0)
return -1;
for (int i = index; i < instrs.Count; i++) {
if (instrs[i].OpCode == opcode)
return i;
}
return -1;
}
static bool hasFieldType(IEnumerable<FieldDefinition> fields, TypeReference fieldType) {
foreach (var field in fields) {
if (MemberReferenceHelper.compareTypes(field.FieldType, fieldType))
return true;
}
return false;
}
static int getOffsetMagic(MethodDefinition method) {
var instrs = method.Body.Instructions;
for (int i = 0; i < instrs.Count - 4; i++) {