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; + } + } +}