From 3f1b9152bd40615fc43536bc9752cab29fec5524 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 22 Oct 2011 14:31:38 +0200 Subject: [PATCH] Add CO deobfuscator. Can decrypt embedded assemblies. --- blocks/DotNetUtils.cs | 4 + de4dot.code/ObfuscatedFile.cs | 6 +- de4dot.code/Program.cs | 1 + de4dot.code/Utils.cs | 7 + de4dot.code/de4dot.code.csproj | 5 +- .../CryptoObfuscator/AssemblyResolver.cs | 144 ++++++++++++++++ .../CryptoObfuscator/Deobfuscator.cs | 161 ++++++++++++++++++ .../CryptoObfuscator/ResourceDecrypter.cs | 103 +++++++++++ .../CryptoObfuscator/ResourceResolver.cs | 82 +++++++++ .../deobfuscators/IDeobfuscatedFile.cs | 2 +- .../SmartAssembly/AssemblyResolverInfo.cs | 2 +- .../SmartAssembly/ResourceResolverInfo.cs | 2 +- .../deobfuscators/SmartAssembly/SA_Utils.cs | 29 ---- .../deobfuscators/Unknown/Deobfuscator.cs | 17 -- 14 files changed, 513 insertions(+), 52 deletions(-) create mode 100644 de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs create mode 100644 de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs create mode 100644 de4dot.code/deobfuscators/CryptoObfuscator/ResourceDecrypter.cs create mode 100644 de4dot.code/deobfuscators/CryptoObfuscator/ResourceResolver.cs delete mode 100644 de4dot.code/deobfuscators/SmartAssembly/SA_Utils.cs diff --git a/blocks/DotNetUtils.cs b/blocks/DotNetUtils.cs index 3d377474..2134718d 100644 --- a/blocks/DotNetUtils.cs +++ b/blocks/DotNetUtils.cs @@ -468,6 +468,10 @@ namespace de4dot.blocks { public static IList getInstructions(IList instructions, int i, params OpCode[] opcodes) { if (i + opcodes.Length > instructions.Count) return null; + if (opcodes.Length == 0) + return new List(); + if (opcodes[0] != instructions[i].OpCode) + return null; var list = new List(opcodes.Length); for (int j = 0; j < opcodes.Length; j++) { diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index c783af78..031cfb71 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -749,9 +749,11 @@ namespace de4dot { deobfuscate(method, "Static string decryption", (blocks) => theDeob.deobfuscateStrings(blocks)); } - void IDeobfuscatedFile.createAssemblyFile(byte[] data, string assemblyName) { + void IDeobfuscatedFile.createAssemblyFile(byte[] data, string assemblyName, string extension) { + if (extension == null) + extension = ".dll"; var baseDir = Utils.getDirName(options.NewFilename); - var newName = Path.Combine(baseDir, assemblyName + ".dll"); + var newName = Path.Combine(baseDir, assemblyName + extension); Log.n("Creating file {0}", newName); using (var writer = new BinaryWriter(new FileStream(newName, FileMode.Create))) { writer.Write(data); diff --git a/de4dot.code/Program.cs b/de4dot.code/Program.cs index c2571a8f..959ec59b 100644 --- a/de4dot.code/Program.cs +++ b/de4dot.code/Program.cs @@ -30,6 +30,7 @@ namespace de4dot { return new List { new de4dot.deobfuscators.Unknown.DeobfuscatorInfo(), new de4dot.deobfuscators.CliSecure.DeobfuscatorInfo(), + new de4dot.deobfuscators.CryptoObfuscator.DeobfuscatorInfo(), new de4dot.deobfuscators.Dotfuscator.DeobfuscatorInfo(), new de4dot.deobfuscators.Eazfuscator.DeobfuscatorInfo(), new de4dot.deobfuscators.SmartAssembly.DeobfuscatorInfo(), diff --git a/de4dot.code/Utils.cs b/de4dot.code/Utils.cs index 65f7967d..ed1f492e 100644 --- a/de4dot.code/Utils.cs +++ b/de4dot.code/Utils.cs @@ -191,5 +191,12 @@ namespace de4dot { return false; return left.Substring(0, right.Length).Equals(right, stringComparison); } + + public static string getAssemblySimpleName(string name) { + int i = name.IndexOf(','); + if (i < 0) + return name; + return name.Substring(0, i); + } } } diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 5d9739b6..3dcd2afe 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -57,6 +57,10 @@ + + + + @@ -80,7 +84,6 @@ - diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs b/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs new file mode 100644 index 00000000..61380ffa --- /dev/null +++ b/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs @@ -0,0 +1,144 @@ +/* + Copyright (C) 2011 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.Text; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; + +namespace de4dot.deobfuscators.CryptoObfuscator { + class AssemblyResolver { + ModuleDefinition module; + ResourceDecrypter resourceDecrypter; + TypeDefinition resolverType; + List assemblyInfos; + + public class AssemblyInfo { + public string assemblyName; + public EmbeddedResource resource; + public EmbeddedResource symbolsResource; + public AssemblyInfo(string assemblyName, EmbeddedResource resource, EmbeddedResource symbolsResource) { + this.assemblyName = assemblyName; + this.resource = resource; + this.symbolsResource = symbolsResource; + } + + public override string ToString() { + return string.Format("{{{0} => {1}}}", assemblyName, resource.Name); + } + } + + public List AssemblyInfos { + get { return assemblyInfos; } + } + + public AssemblyResolver(ModuleDefinition module, ResourceDecrypter resourceDecrypter) { + this.module = module; + this.resourceDecrypter = resourceDecrypter; + } + + public void find() { + var cctor = DotNetUtils.getMethod(DotNetUtils.getModuleType(module), ".cctor"); + if (cctor == null) + return; + + foreach (var tuple in DotNetUtils.getCalledMethods(module, cctor)) { + var method = tuple.Item2; + if (method.Name == ".cctor" || method.Name == ".ctor") + continue; + if (!method.IsStatic || !DotNetUtils.isMethod(method, "System.Void", "()")) + continue; + if (checkType(tuple.Item1, method)) + break; + } + } + + bool checkType(TypeDefinition type, MethodDefinition initMethod) { + if (DotNetUtils.findFieldType(type, "System.Collections.Hashtable", true) == null) + return false; + if (!checkInitMethod(initMethod)) + return false; + + List newAssemblyInfos = null; + foreach (var s in DotNetUtils.getCodeStrings(initMethod)) { + newAssemblyInfos = initializeEmbeddedAssemblies(s); + if (newAssemblyInfos != null) + break; + } + if (newAssemblyInfos == null) + return false; + + resolverType = type; + assemblyInfos = newAssemblyInfos; + return true; + } + + bool checkInitMethod(MethodDefinition initMethod) { + if (!initMethod.HasBody) + return false; + + var instructions = initMethod.Body.Instructions; + for (int i = 0; i < instructions.Count; i++) { + var instrs = DotNetUtils.getInstructions(instructions, i, OpCodes.Ldnull, OpCodes.Ldftn, OpCodes.Newobj); + if (instrs == null) + continue; + + MethodReference methodRef; + var ldftn = instrs[1]; + var newobj = instrs[2]; + + methodRef = ldftn.Operand as MethodReference; + if (methodRef == null || !MemberReferenceHelper.compareTypes(initMethod.DeclaringType, methodRef.DeclaringType)) + continue; + + methodRef = newobj.Operand as MethodReference; + if (methodRef == null || methodRef.FullName != "System.Void System.ResolveEventHandler::.ctor(System.Object,System.IntPtr)") + continue; + + return true; + } + + return false; + } + + List initializeEmbeddedAssemblies(string s) { + var sb = new StringBuilder(s.Length); + foreach (var c in s) + sb.Append((char)~c); + var tmpAssemblyInfos = sb.ToString().Split(new string[] { "##" }, StringSplitOptions.RemoveEmptyEntries); + if (tmpAssemblyInfos.Length == 0 || (tmpAssemblyInfos.Length & 1) == 1) + return null; + + var newAssemblyInfos = new List(tmpAssemblyInfos.Length / 2); + for (int i = 0; i < tmpAssemblyInfos.Length; i += 2) { + var assemblyName = tmpAssemblyInfos[i]; + var resourceName = tmpAssemblyInfos[i + 1]; + var resource = DotNetUtils.getResource(module, resourceName) as EmbeddedResource; + var symbolsResource = DotNetUtils.getResource(module, resourceName + "#") as EmbeddedResource; + if (resource == null) + return null; + newAssemblyInfos.Add(new AssemblyInfo(assemblyName, resource, symbolsResource)); + } + + return newAssemblyInfos; + } + } +} diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs b/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs new file mode 100644 index 00000000..4b9a4f73 --- /dev/null +++ b/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs @@ -0,0 +1,161 @@ +/* + Copyright (C) 2011 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 System.Text.RegularExpressions; +using Mono.Cecil; +using de4dot.blocks; + +namespace de4dot.deobfuscators.CryptoObfuscator { + class DeobfuscatorInfo : DeobfuscatorInfoBase { + const string DEFAULT_REGEX = @"!^[A-Z]{1,3}(?:`\d+)?$&!^c[0-9a-f]{32}(?:`\d+)?$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX; + public DeobfuscatorInfo() + : base("co", DEFAULT_REGEX) { + } + + internal static string ObfuscatorType { + get { return "CryptoObfuscator"; } + } + + public override string Type { + get { return ObfuscatorType; } + } + + public override IDeobfuscator createDeobfuscator() { + return new Deobfuscator(new Deobfuscator.Options { + ValidNameRegex = validNameRegex.get(), + }); + } + + protected override IEnumerable