diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index 5e2d54b6..cd366ade 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -68,6 +68,8 @@
+
+
diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs b/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs
index 1cad1e19..1dcfc324 100644
--- a/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs
+++ b/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs
@@ -46,6 +46,10 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator {
}
}
+ public bool Detected {
+ get { return resolverType != null; }
+ }
+
public List AssemblyInfos {
get { return assemblyInfos; }
}
diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs b/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs
index cdceaa12..6d440bad 100644
--- a/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs
@@ -68,6 +68,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator {
bool foundObfuscatedSymbols = false;
bool foundObfuscatorUserString = false;
+ MethodsDecrypter methodsDecrypter;
ProxyCallFixer proxyCallFixer;
ResourceDecrypter resourceDecrypter;
ResourceResolver resourceResolver;
@@ -111,7 +112,8 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator {
protected override int detectInternal() {
int val = 0;
- int sum = toInt32(stringDecrypter.Detected) +
+ int sum = toInt32(methodsDecrypter.Detected) +
+ toInt32(stringDecrypter.Detected) +
toInt32(tamperDetection.Detected) +
toInt32(proxyCallFixer.Detected) +
toInt32(constantsDecrypter.Detected);
@@ -134,6 +136,8 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator {
if (checkCryptoObfuscator())
foundObfuscatedSymbols = true;
+ methodsDecrypter = new MethodsDecrypter(module);
+ methodsDecrypter.find();
proxyCallFixer = new ProxyCallFixer(module);
proxyCallFixer.findDelegateCreator();
stringDecrypter = new StringDecrypter(module);
@@ -190,6 +194,14 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator {
DeobfuscatedFile.stringDecryptersAdded();
}
+ methodsDecrypter.decrypt(resourceDecrypter);
+
+ if (methodsDecrypter.Detected) {
+ if (!assemblyResolver.Detected)
+ assemblyResolver.find();
+ if (!tamperDetection.Detected)
+ tamperDetection.find();
+ }
antiDebugger = new AntiDebugger(module, DeobfuscatedFile, this);
antiDebugger.find();
@@ -217,6 +229,9 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator {
addTypeToBeRemoved(assemblyResolver.Type, "Assembly resolver type");
addTypeToBeRemoved(tamperDetection.Type, "Tamper detection type");
addTypeToBeRemoved(antiDebugger.Type, "Anti-debugger type");
+ addTypeToBeRemoved(methodsDecrypter.Type, "Methods decrypter type");
+ addTypesToBeRemoved(methodsDecrypter.DelegateTypes, "Methods decrypter delegate type");
+ addResourceToBeRemoved(methodsDecrypter.Resource, "Encrypted methods");
proxyCallFixer.find();
diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/MethodBodyReader.cs b/de4dot.code/deobfuscators/CryptoObfuscator/MethodBodyReader.cs
new file mode 100644
index 00000000..122ac01b
--- /dev/null
+++ b/de4dot.code/deobfuscators/CryptoObfuscator/MethodBodyReader.cs
@@ -0,0 +1,118 @@
+/*
+ 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;
+using System.Collections.Generic;
+using System.IO;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using de4dot.blocks;
+
+namespace de4dot.code.deobfuscators.CryptoObfuscator {
+ class MethodBodyReader : MethodBodyReaderBase {
+ ModuleDefinition module;
+ ushort maxStackSize;
+
+ public MethodBodyReader(ModuleDefinition module, BinaryReader reader)
+ : base(reader) {
+ this.module = module;
+ }
+
+ public void read(MethodDefinition method) {
+ this.parameters = getParameters(method);
+ this.Locals = getLocals(method);
+
+ maxStackSize = (ushort)reader.ReadInt32();
+ readInstructionsNumBytes(reader.ReadUInt32());
+ readExceptionHandlers();
+ }
+
+ void readExceptionHandlers() {
+ int totalSize = reader.ReadInt32();
+ if (totalSize == 0)
+ return;
+ reader.ReadInt32();
+ readExceptionHandlers((totalSize - 4) / 24);
+ }
+
+ static IList getParameters(MethodReference method) {
+ return DotNetUtils.getParameters(method);
+ }
+
+ static IList getLocals(MethodDefinition method) {
+ if (method.Body == null)
+ return new List();
+ return new List(method.Body.Variables);
+ }
+
+ protected override FieldReference readInlineField(Instruction instr) {
+ return (FieldReference)module.LookupToken(reader.ReadInt32());
+ }
+
+ protected override MethodReference readInlineMethod(Instruction instr) {
+ return (MethodReference)module.LookupToken(reader.ReadInt32());
+ }
+
+ protected override CallSite readInlineSig(Instruction instr) {
+ return module.ReadCallSite(new MetadataToken(reader.ReadUInt32()));
+ }
+
+ protected override string readInlineString(Instruction instr) {
+ return module.GetUserString(reader.ReadUInt32());
+ }
+
+ protected override MemberReference readInlineTok(Instruction instr) {
+ return (MemberReference)module.LookupToken(reader.ReadInt32());
+ }
+
+ protected override TypeReference readInlineType(Instruction instr) {
+ return (TypeReference)module.LookupToken(reader.ReadInt32());
+ }
+
+ protected override ExceptionHandler readExceptionHandler() {
+ var eh = new ExceptionHandler((ExceptionHandlerType)reader.ReadInt32());
+
+ int tryOffset = reader.ReadInt32();
+ eh.TryStart = getInstruction(tryOffset);
+ eh.TryEnd = getInstructionOrNull(tryOffset + reader.ReadInt32());
+
+ int handlerOffset = reader.ReadInt32();
+ eh.HandlerStart = getInstruction(handlerOffset);
+ eh.HandlerEnd = getInstructionOrNull(handlerOffset + reader.ReadInt32());
+
+ switch (eh.HandlerType) {
+ case ExceptionHandlerType.Catch:
+ eh.CatchType = (TypeReference)module.LookupToken(reader.ReadInt32());
+ break;
+
+ case ExceptionHandlerType.Filter:
+ eh.FilterStart = getInstruction(reader.ReadInt32());
+ break;
+
+ case ExceptionHandlerType.Finally:
+ case ExceptionHandlerType.Fault:
+ default:
+ reader.ReadInt32();
+ break;
+ }
+
+ return eh;
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/MethodsDecrypter.cs b/de4dot.code/deobfuscators/CryptoObfuscator/MethodsDecrypter.cs
new file mode 100644
index 00000000..529e1678
--- /dev/null
+++ b/de4dot.code/deobfuscators/CryptoObfuscator/MethodsDecrypter.cs
@@ -0,0 +1,205 @@
+/*
+ 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;
+using System.Collections.Generic;
+using System.IO;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using de4dot.blocks;
+
+namespace de4dot.code.deobfuscators.CryptoObfuscator {
+ class MethodsDecrypter {
+ ModuleDefinition module;
+ TypeDefinition decrypterType;
+ MethodDefinition decryptMethod;
+ MethodDefinition decrypterCctor;
+ EmbeddedResource resource;
+ List delegateTypes = new List();
+
+ public TypeDefinition Type {
+ get { return decrypterType; }
+ }
+
+ public IEnumerable DelegateTypes {
+ get { return delegateTypes; }
+ }
+
+ public EmbeddedResource Resource {
+ get { return resource; }
+ }
+
+ public bool Detected {
+ get { return decrypterType != null; }
+ }
+
+ public MethodsDecrypter(ModuleDefinition module) {
+ this.module = module;
+ }
+
+ public void find() {
+ foreach (var type in module.Types) {
+ if (check(type))
+ break;
+ }
+ }
+
+ static readonly string[] requiredFields = new string[] {
+ "System.Byte[]",
+ "System.Collections.Generic.Dictionary`2",
+ "System.ModuleHandle",
+ };
+ bool check(TypeDefinition type) {
+ if (type.NestedTypes.Count != 1)
+ return false;
+ if (type.Fields.Count != 3)
+ return false;
+ if (!new FieldTypes(type).all(requiredFields))
+ return false;
+
+ var cctor = DotNetUtils.getMethod(type, ".cctor");
+ if (cctor == null)
+ return false;
+ var decryptMethodTmp = findDecryptMethod(type);
+ if (decryptMethodTmp == null)
+ return false;
+
+ decryptMethod = decryptMethodTmp;
+ decrypterCctor = cctor;
+ decrypterType = type;
+ return true;
+ }
+
+ static readonly string[] requiredLocals = new string[] {
+ "System.Delegate",
+ "System.ModuleHandle",
+ "System.Reflection.Emit.DynamicILInfo",
+ "System.Reflection.Emit.DynamicMethod",
+ "System.Reflection.FieldInfo",
+ "System.Reflection.FieldInfo[]",
+ "System.Reflection.MethodBase",
+ "System.Reflection.MethodBody",
+ "System.Type",
+ "System.Type[]",
+ };
+ static MethodDefinition findDecryptMethod(TypeDefinition type) {
+ foreach (var method in type.Methods) {
+ if (!method.IsStatic || method.Body == null)
+ continue;
+ if (!new LocalTypes(method).all(requiredLocals))
+ continue;
+ if (!DotNetUtils.isMethod(method, "System.Void", "(System.Int32,System.Int32,System.Int32)"))
+ continue;
+
+ return method;
+ }
+ return null;
+ }
+
+ public void decrypt(ResourceDecrypter resourceDecrypter) {
+ if (decryptMethod == null)
+ return;
+
+ resource = CoUtils.getResource(module, decrypterCctor);
+ if (resource == null)
+ return;
+ var decrypted = resourceDecrypter.decrypt(resource.GetResourceStream());
+ var reader = new BinaryReader(new MemoryStream(decrypted));
+ int numEncrypted = reader.ReadInt32();
+ Log.v("Restoring {0} encrypted methods", numEncrypted);
+ Log.indent();
+ for (int i = 0; i < numEncrypted; i++) {
+ int delegateTypeToken = reader.ReadInt32();
+ uint codeOffset = reader.ReadUInt32();
+ var origOffset = reader.BaseStream.Position;
+ reader.BaseStream.Position = codeOffset;
+ decrypt(reader, delegateTypeToken);
+ reader.BaseStream.Position = origOffset;
+ }
+ Log.deIndent();
+ }
+
+ void decrypt(BinaryReader reader, int delegateTypeToken) {
+ var delegateType = module.LookupToken(delegateTypeToken) as TypeDefinition;
+ if (delegateType == null)
+ throw new ApplicationException("Couldn't find delegate type");
+
+ int delToken, encMethToken, encDeclToken;
+ if (!getTokens(delegateType, out delToken, out encMethToken, out encDeclToken))
+ throw new ApplicationException("Could not find encrypted method tokens");
+ if (delToken != delegateTypeToken)
+ throw new ApplicationException("Invalid delegate type token");
+ var encType = module.LookupToken(encDeclToken) as TypeReference;
+ if (encType == null)
+ throw new ApplicationException("Invalid declaring type token");
+ var encMethod = module.LookupToken(encMethToken) as MethodDefinition;
+ if (encMethod == null)
+ throw new ApplicationException("Invalid encrypted method token");
+
+ var bodyReader = new MethodBodyReader(module, reader);
+ bodyReader.read(encMethod);
+ bodyReader.restoreMethod(encMethod);
+ Log.v("Restored method {0} ({1:X8}). Instrs:{2}, Locals:{3}, Exceptions:{4}",
+ Utils.removeNewlines(encMethod.FullName),
+ encMethod.MetadataToken.ToInt32(),
+ encMethod.Body.Instructions.Count,
+ encMethod.Body.Variables.Count,
+ encMethod.Body.ExceptionHandlers.Count);
+ delegateTypes.Add(delegateType);
+ }
+
+ bool getTokens(TypeDefinition delegateType, out int delegateToken, out int encMethodToken, out int encDeclaringTypeToken) {
+ delegateToken = 0;
+ encMethodToken = 0;
+ encDeclaringTypeToken = 0;
+
+ var cctor = DotNetUtils.getMethod(delegateType, ".cctor");
+ if (cctor == null)
+ return false;
+
+ var instrs = cctor.Body.Instructions;
+ for (int i = 0; i < instrs.Count - 4; i++) {
+ var ldci4_1 = instrs[i];
+ if (!DotNetUtils.isLdcI4(ldci4_1))
+ continue;
+ var ldci4_2 = instrs[i + 1];
+ if (!DotNetUtils.isLdcI4(ldci4_2))
+ continue;
+ var ldci4_3 = instrs[i + 2];
+ if (!DotNetUtils.isLdcI4(ldci4_3))
+ continue;
+ var call = instrs[i + 3];
+ if (call.OpCode.Code != Code.Call)
+ continue;
+ var calledMethod = call.Operand as MethodDefinition;
+ if (calledMethod == null)
+ continue;
+ if (calledMethod != decryptMethod)
+ continue;
+
+ delegateToken = DotNetUtils.getLdcI4Value(ldci4_1);
+ encMethodToken = DotNetUtils.getLdcI4Value(ldci4_2);
+ encDeclaringTypeToken = DotNetUtils.getLdcI4Value(ldci4_3);
+ return true;
+ }
+
+ return true;
+ }
+ }
+}