diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index a56d2163..ce7eb2d1 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -66,6 +66,7 @@ + diff --git a/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs b/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs index 391869e3..96d06c58 100644 --- a/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs +++ b/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs @@ -29,7 +29,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { TypeDef cliSecureRtType; MethodDef postInitializeMethod; MethodDef initializeMethod; - MethodDef stringDecrypterMethod; + Dictionary stringDecrypterInfos = new Dictionary(); MethodDef loadMethod; bool foundSig; @@ -41,8 +41,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { get { return cliSecureRtType; } } - public MethodDef StringDecrypterMethod { - get { return stringDecrypterMethod; } + public IEnumerable StringDecrypterInfos { + get { return stringDecrypterInfos.Keys; } } public MethodDef PostInitializeMethod { @@ -66,7 +66,11 @@ namespace de4dot.code.deobfuscators.Agile_NET { cliSecureRtType = Lookup(oldOne.cliSecureRtType, "Could not find CliSecureRt type"); postInitializeMethod = Lookup(oldOne.postInitializeMethod, "Could not find postInitializeMethod method"); initializeMethod = Lookup(oldOne.initializeMethod, "Could not find initializeMethod method"); - stringDecrypterMethod = Lookup(oldOne.stringDecrypterMethod, "Could not find stringDecrypterMethod method"); + foreach (var info in oldOne.stringDecrypterInfos.Keys) { + var m = Lookup(info.Method, "Could not find string decrypter method"); + var f = Lookup(info.Field, "Could not find string decrypter field"); + stringDecrypterInfos[new StringDecrypterInfo(m, f)] = true; + } loadMethod = Lookup(oldOne.loadMethod, "Could not find loadMethod method"); foundSig = oldOne.foundSig; } @@ -100,11 +104,11 @@ namespace de4dot.code.deobfuscators.Agile_NET { if (!HasInitializeMethod(type, "_Initialize") && !HasInitializeMethod(type, "_Initialize64")) continue; - stringDecrypterMethod = FindStringDecrypterMethod(type); initializeMethod = calledMethod; postInitializeMethod = FindMethod(type, "System.Void", "PostInitialize", "()"); loadMethod = FindMethod(type, "System.IntPtr", "Load", "()"); cliSecureRtType = type; + FindStringDecrypters(); return true; } } @@ -112,6 +116,15 @@ namespace de4dot.code.deobfuscators.Agile_NET { return false; } + void FindStringDecrypters() { + AddStringDecrypterMethod(FindStringDecrypterMethod(cliSecureRtType)); + } + + void AddStringDecrypterMethod(MethodDef method) { + if (method != null) + stringDecrypterInfos[new StringDecrypterInfo(method)] = true; + } + static string[] requiredFields6 = new string[] { "System.Byte[]", }; @@ -134,7 +147,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { if (cs == null) continue; - stringDecrypterMethod = cs; + AddStringDecrypterMethod(cs); cliSecureRtType = type; return true; } @@ -208,7 +221,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { continue; cliSecureRtType = type; - stringDecrypterMethod = cs; + AddStringDecrypterMethod(cs); return; } } diff --git a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs index fd577753..a3e6a3f9 100644 --- a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs @@ -180,7 +180,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { FindCliSecureAttribute(); cliSecureRtType = new CliSecureRtType(module); cliSecureRtType.Find(ModuleBytes); - stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterMethod); + stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterInfos); stringDecrypter.Find(); resourceDecrypter = new ResourceDecrypter(module); resourceDecrypter.Find(); @@ -246,7 +246,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { base.DeobfuscateBegin(); cliSecureRtType.FindStringDecrypterMethod(); - stringDecrypter.Method = cliSecureRtType.StringDecrypterMethod; + stringDecrypter.AddDecrypterInfos(cliSecureRtType.StringDecrypterInfos); + stringDecrypter.Initialize(); AddAttributesToBeRemoved(cliSecureAttributes, "Obfuscator attribute"); @@ -265,7 +266,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { proxyCallFixer.Find(); - staticStringInliner.Add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0])); + foreach (var info in stringDecrypter.StringDecrypterInfos) + staticStringInliner.Add(info.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0])); DeobfuscatedFile.StringDecryptersAdded(); if (options.DecryptMethods) { @@ -295,6 +297,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { } public override void DeobfuscateMethodEnd(Blocks blocks) { + if (Operations.DecryptStrings != OpDecryptString.None) + stringDecrypter.Deobfuscate(blocks); proxyCallFixer.Deobfuscate(blocks); RemoveStackFrameHelperCode(blocks); base.DeobfuscateMethodEnd(blocks); @@ -310,8 +314,14 @@ namespace de4dot.code.deobfuscators.Agile_NET { } if (CanRemoveStringDecrypterType) { AddTypeToBeRemoved(stringDecrypter.Type, "String decrypter type"); + foreach (var info in stringDecrypter.StringDecrypterInfos) { + if (info.Method.DeclaringType != cliSecureRtType.Type) + AddMethodToBeRemoved(info.Method, "String decrypter method"); + if (info.Field != null && info.Field.DeclaringType != stringDecrypter.Type) + AddFieldToBeRemoved(info.Field, "String decrypter field"); + } if (options.DecryptMethods) - AddTypeToBeRemoved(cliSecureRtType.Type, "Obfuscator type"); + AddTypeToBeRemoved(cliSecureRtType.Type ?? stringDecrypter.KeyArrayFieldType, "Obfuscator type"); } if (options.DecryptMethods) { AddResources("Obfuscator protection files"); @@ -327,8 +337,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { public override IEnumerable GetStringDecrypterMethods() { var list = new List(); - if (stringDecrypter.Method != null) - list.Add(stringDecrypter.Method.MDToken.ToInt32()); + foreach (var info in stringDecrypter.StringDecrypterInfos) + list.Add(info.Method.MDToken.ToInt32()); return list; } diff --git a/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs index 91bf919f..2bab0efe 100644 --- a/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs @@ -18,38 +18,53 @@ */ using System; +using System.Collections.Generic; using System.Text; using dnlib.DotNet; +using dnlib.DotNet.Emit; +using de4dot.blocks; namespace de4dot.code.deobfuscators.Agile_NET { class StringDecrypter { ModuleDefMD module; TypeDef stringDecrypterType; - MethodDef stringDecrypterMethod; + FieldDef keyInitField; + FieldDef keyArrayField; + Dictionary stringDecrypterInfos = new Dictionary(); byte[] stringDecrypterKey; public bool Detected { - get { return stringDecrypterMethod != null; } + get { return stringDecrypterInfos.Count != 0; } } public TypeDef Type { get { return stringDecrypterType; } } - public MethodDef Method { - get { return stringDecrypterMethod; } - set { stringDecrypterMethod = value; } + public TypeDef KeyArrayFieldType { + get { return keyArrayField == null ? null : keyArrayField.DeclaringType; } } - public StringDecrypter(ModuleDefMD module, MethodDef stringDecrypterMethod) { + public IEnumerable StringDecrypterInfos { + get { return stringDecrypterInfos.Keys; } + } + + public StringDecrypter(ModuleDefMD module, IEnumerable stringDecrypterMethods) { this.module = module; - this.stringDecrypterMethod = stringDecrypterMethod; + foreach (var sdm in stringDecrypterMethods) + this.stringDecrypterInfos[sdm] = true; } public StringDecrypter(ModuleDefMD module, StringDecrypter oldOne) { this.module = module; stringDecrypterType = Lookup(oldOne.stringDecrypterType, "Could not find stringDecrypterType"); - stringDecrypterMethod = Lookup(oldOne.stringDecrypterMethod, "Could not find stringDecrypterMethod"); + keyInitField = Lookup(oldOne.keyInitField, "Could not find key init field"); + keyArrayField = Lookup(oldOne.keyArrayField, "Could not find key array field"); + foreach (var info in oldOne.stringDecrypterInfos.Keys) { + var m = Lookup(info.Method, "Could not find string decrypter method"); + var f = Lookup(info.Field, "Could not find string decrypter field"); + stringDecrypterInfos[new StringDecrypterInfo(m, f)] = true; + } stringDecrypterKey = oldOne.stringDecrypterKey; } @@ -57,6 +72,11 @@ namespace de4dot.code.deobfuscators.Agile_NET { return DeobUtils.Lookup(module, def, errorMessage); } + public void AddDecrypterInfos(IEnumerable infos) { + foreach (var info in infos) + stringDecrypterInfos[info] = true; + } + public void Find() { stringDecrypterKey = new byte[1] { 0xFF }; foreach (var type in module.Types) { @@ -64,6 +84,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { stringDecrypterType = type; foreach (var field in type.Fields) { if (field.FullName == " ::345" || field.FullName == "/D234 ::345") { + keyInitField = field; stringDecrypterKey = field.InitialValue; break; } @@ -73,6 +94,119 @@ namespace de4dot.code.deobfuscators.Agile_NET { } } + public void Initialize() { + if (keyInitField == null) + return; + + foreach (var type in module.Types) { + var cctor = type.FindStaticConstructor(); + if (cctor == null) + continue; + keyArrayField = GetKeyArrayField(cctor, keyInitField); + if (keyArrayField != null) + break; + } + if (keyArrayField == null) + return; + + foreach (var type in module.GetTypes()) { + FieldDef field; + var method = FindStringDecrypters(type, keyArrayField, out field); + if (method == null) + continue; + + stringDecrypterInfos[new StringDecrypterInfo(method, field)] = true; + } + } + + static FieldDef GetKeyArrayField(MethodDef method, FieldDef keyInitField) { + if (method == null || method.Body == null) + return null; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count; i++) { + if (instrs[i].OpCode.Code != Code.Ldtoken) + continue; + + i++; + for (; i < instrs.Count; i++) { + var instr = instrs[i]; + if (instr.OpCode.Code != Code.Stsfld) + continue; + var field = instr.Operand as FieldDef; + if (field == null || !field.IsStatic || field.DeclaringType != method.DeclaringType) + continue; + if (field.FieldSig.GetFieldType().GetFullName() != "System.Byte[]") + continue; + return field; + } + } + return null; + } + + static MethodDef FindStringDecrypters(TypeDef type, FieldDef keyArrayField, out FieldDef field) { + FieldDef foundField = null; + foreach (var method in type.Methods) { + if (!method.IsAssembly || !method.IsStatic) + continue; + if (!DotNetUtils.IsMethod(method, "System.String", "(System.String)")) + continue; + if (!method.HasBody) + continue; + + bool accessedArrayField = false; + foreach (var instr in method.Body.Instructions) { + var f = instr.Operand as FieldDef; + accessedArrayField |= f == keyArrayField; + if (f == null || f == keyArrayField || f == foundField) + continue; + if (f.FieldSig.GetFieldType().GetFullName() != "System.Collections.Hashtable" || + foundField != null) + goto exit; + foundField = f; + } + if (!accessedArrayField) + continue; + + field = foundField; + return method; + } + +exit: ; + field = null; + return null; + } + + public void Deobfuscate(Blocks blocks) { + if (!blocks.Method.IsStaticConstructor) + return; + + var decrypterFields = new Dictionary(stringDecrypterInfos.Count); + foreach (var info in stringDecrypterInfos.Keys) { + if (info.Field != null) + decrypterFields[info.Field] = true; + } + + foreach (var block in blocks.MethodBlocks.GetAllBlocks()) { + var instrs = block.Instructions; + for (int i = instrs.Count - 2; i >= 0; i--) { + var newobj = instrs[i]; + if (newobj.OpCode.Code != Code.Newobj) + continue; + var ctor = newobj.Operand as IMethod; + if (ctor == null || ctor.FullName != "System.Void System.Collections.Hashtable::.ctor()") + continue; + var stsfld = instrs[i + 1]; + if (stsfld.OpCode.Code != Code.Stsfld) + continue; + var field = stsfld.Operand as FieldDef; + if (field == null || !decrypterFields.ContainsKey(field)) + continue; + + block.Remove(i, 2); + } + } + } + public string Decrypt(string es) { if (stringDecrypterKey == null) throw new ApplicationException("Trying to decrypt strings when stringDecrypterKey is null (could not find it!)"); diff --git a/de4dot.code/deobfuscators/Agile_NET/StringDecrypterInfo.cs b/de4dot.code/deobfuscators/Agile_NET/StringDecrypterInfo.cs new file mode 100644 index 00000000..52dac2fb --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/StringDecrypterInfo.cs @@ -0,0 +1,52 @@ +/* + Copyright (C) 2011-2014 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 dnlib.DotNet; + +namespace de4dot.code.deobfuscators.Agile_NET { + class StringDecrypterInfo { + public readonly MethodDef Method; + public readonly FieldDef Field; + + public StringDecrypterInfo(MethodDef method) + : this(method, null) { + } + + public StringDecrypterInfo(MethodDef method, FieldDef field) { + this.Method = method; + this.Field = field; + } + + public override int GetHashCode() { + int hash = 0; + if (Method != null) + hash ^= Method.GetHashCode(); + if (Field != null) + hash ^= Field.GetHashCode(); + return hash; + } + + public override bool Equals(object obj) { + var other = obj as StringDecrypterInfo; + return other != null && + Method == other.Method && + Field == other.Field; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs index a6431820..ccb40008 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs @@ -33,11 +33,13 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { public MyDeobfuscator(ModuleDefMD module) { cliSecureRtType = new CliSecureRtType(module); cliSecureRtType.Find(null); - stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterMethod); + stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterInfos); stringDecrypter.Find(); cliSecureRtType.FindStringDecrypterMethod(); - stringDecrypter.Method = cliSecureRtType.StringDecrypterMethod; - staticStringInliner.Add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0])); + stringDecrypter.AddDecrypterInfos(cliSecureRtType.StringDecrypterInfos); + stringDecrypter.Initialize(); + foreach (var info in stringDecrypter.StringDecrypterInfos) + staticStringInliner.Add(info.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0])); } void RestoreMethod(Blocks blocks) {