/* Copyright (C) 2011-2015 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 dnlib.DotNet; using dnlib.DotNet.Emit; using de4dot.blocks; namespace de4dot.code.deobfuscators.Agile_NET { class StringDecrypter { ModuleDefMD module; TypeDef stringDecrypterType; FieldDef keyInitField; FieldDef keyArrayField; Dictionary stringDecrypterInfos = new Dictionary(); byte[] stringDecrypterKey; public bool Detected { get { return stringDecrypterInfos.Count != 0; } } public TypeDef Type { get { return stringDecrypterType; } } public TypeDef KeyArrayFieldType { get { return keyArrayField == null ? null : keyArrayField.DeclaringType; } } public IEnumerable StringDecrypterInfos { get { return stringDecrypterInfos.Keys; } } public StringDecrypter(ModuleDefMD module, IEnumerable stringDecrypterMethods) { this.module = module; 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"); 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; } T Lookup(T def, string errorMessage) where T : class, ICodedToken { 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) { if (type.FullName == "" || type.FullName == "") { stringDecrypterType = type; foreach (var field in type.Fields) { if (field.FullName == " ::345" || field.FullName == "/D234 ::345") { keyInitField = field; stringDecrypterKey = field.InitialValue; break; } } break; } } } 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 (DotNetUtils.DerivesFromDelegate(f.DeclaringType)) 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!)"); char[] buf = new char[es.Length]; for (int i = 0; i < es.Length; i++) buf[i] = (char)(es[i] ^ stringDecrypterKey[i % stringDecrypterKey.Length]); return new string(buf); } } }