diff --git a/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs b/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs index 7ddcf055..da23eba2 100644 --- a/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs @@ -231,6 +231,8 @@ done: } public override void deobfuscateEnd() { + if (options.RestoreFields) + fieldsRestorer.cleanUp(); removeInlinedMethods(); if (options.RestoreFields) diff --git a/de4dot.code/deobfuscators/DeepSea/FieldsRestorer.cs b/de4dot.code/deobfuscators/DeepSea/FieldsRestorer.cs index 81a7566a..78b1c0c8 100644 --- a/de4dot.code/deobfuscators/DeepSea/FieldsRestorer.cs +++ b/de4dot.code/deobfuscators/DeepSea/FieldsRestorer.cs @@ -17,6 +17,7 @@ along with de4dot. If not, see . */ +using System; using System.Collections.Generic; using Mono.Cecil; using Mono.Cecil.Cil; @@ -26,13 +27,14 @@ namespace de4dot.code.deobfuscators.DeepSea { // DS 4.x can move fields from a class to a struct. This class restores the fields. class FieldsRestorer { ModuleDefinition module; - TypeDefinitionDict structToOwner = new TypeDefinitionDict(); - Dictionary oldFieldToken = new Dictionary(); + TypeDefinitionDict> structToOwners = new TypeDefinitionDict>(); + FieldDefinitionAndDeclaringTypeDict structFieldsToFix = new FieldDefinitionAndDeclaringTypeDict(); + TypeDefinitionDict> typeToFieldsDict = new TypeDefinitionDict>(); public List FieldStructs { get { - var list = new List(structToOwner.Count); - foreach (var structType in structToOwner.getKeys()) { + var list = new List(structToOwners.Count); + foreach (var structType in structToOwners.getKeys()) { if (structType.Methods.Count != 0) continue; @@ -49,33 +51,32 @@ namespace de4dot.code.deobfuscators.DeepSea { public void initialize() { foreach (var kv in getMovedTypes()) { var structType = kv.Key; - var ownerType = kv.Value; - structToOwner.add(structType, ownerType); + structToOwners.add(structType, kv.Value); - for (int i = 0; i < ownerType.Fields.Count; i++) { - if (DotNetUtils.getType(module, ownerType.Fields[i].FieldType) != structType) - continue; - oldFieldToken[ownerType.Fields[i].MetadataToken.ToInt32()] = true; - ownerType.Fields.RemoveAt(i); - break; + foreach (var ownerType in kv.Value) { + foreach (var ownerField in ownerType.Fields) { + if (DotNetUtils.getType(module, ownerField.FieldType) != structType) + continue; + structFieldsToFix.add(ownerField, true); + break; + } + + var fieldsDict = new FieldDefinitionAndDeclaringTypeDict(); + typeToFieldsDict.add(ownerType, fieldsDict); + foreach (var structField in structType.Fields) { + var newField = DotNetUtils.createFieldDefinition(structField.Name, structField.Attributes, structField.FieldType); + ownerType.Fields.Add(newField); + fieldsDict.add(structField, newField); + } } - - var structTypeFields = new List(structType.Fields); - structType.Fields.Clear(); - foreach (var field in structTypeFields) - ownerType.Fields.Add(field); - - // Add a field so peverify won't complain if this type isn't removed - var newField = new FieldDefinition("a", FieldAttributes.Public, module.TypeSystem.Byte); - newField.MetadataToken = DotNetUtils.nextFieldToken(); - structType.Fields.Add(newField); } } - Dictionary getMovedTypes() { - var fieldTypeToTypes = new Dictionary>(); + Dictionary> getMovedTypes() { + var candidates = new Dictionary>(); + var typeToStruct = new Dictionary(); foreach (var type in module.GetTypes()) { - foreach (var field in type.Fields) { + foreach (var field in getPossibleFields(type)) { var fieldType = DotNetUtils.getType(module, field.FieldType); if (fieldType == null || !fieldType.IsValueType) continue; @@ -87,26 +88,28 @@ namespace de4dot.code.deobfuscators.DeepSea { continue; if (fieldType.Fields.Count == 0) continue; + if (fieldType.HasEvents || fieldType.HasProperties || fieldType.HasInterfaces) + continue; if (hasNonStaticMethods(fieldType)) continue; + if (hasStaticFields(fieldType)) + continue; List list; - if (!fieldTypeToTypes.TryGetValue(fieldType, out list)) - fieldTypeToTypes[fieldType] = list = new List(); + if (!candidates.TryGetValue(fieldType, out list)) + candidates[fieldType] = list = new List(); list.Add(type); + typeToStruct[type] = fieldType; + break; } } - var candidates = new Dictionary(); - foreach (var kv in fieldTypeToTypes) { - if (kv.Value.Count != 1) - continue; - candidates[kv.Key] = kv.Value[0]; - } - foreach (var type in module.GetTypes()) { + TypeDefinition structType; + typeToStruct.TryGetValue(type, out structType); + foreach (var field in type.Fields) { - if (field.DeclaringType != type) + if (field.IsStatic || field.FieldType != structType) removeType(candidates, field.FieldType); } foreach (var method in type.Methods) { @@ -123,7 +126,27 @@ namespace de4dot.code.deobfuscators.DeepSea { return candidates; } - void removeType(Dictionary candidates, TypeReference type) { + IEnumerable getPossibleFields(TypeDefinition type) { + var typeToFields = new TypeDefinitionDict>(); + foreach (var field in type.Fields) { + if (field.Attributes != FieldAttributes.Private) + continue; + var fieldType = DotNetUtils.getType(module, field.FieldType); + if (fieldType == null || !fieldType.IsValueType) + continue; + var list = typeToFields.find(fieldType); + if (list == null) + typeToFields.add(fieldType, list = new List()); + list.Add(field); + } + + foreach (var list in typeToFields.getValues()) { + if (list.Count == 1) + yield return list[0]; + } + } + + void removeType(Dictionary> candidates, TypeReference type) { var typeDef = DotNetUtils.getType(module, type); if (typeDef == null) return; @@ -146,21 +169,69 @@ namespace de4dot.code.deobfuscators.DeepSea { return false; } + static bool hasStaticFields(TypeDefinition type) { + foreach (var field in type.Fields) { + if (field.IsStatic) + return true; + } + return false; + } + public void deobfuscate(Blocks blocks) { + var instrsToRemove = new List(); foreach (var block in blocks.MethodBlocks.getAllBlocks()) { + instrsToRemove.Clear(); var instrs = block.Instructions; for (int i = instrs.Count - 1; i >= 0; i--) { var instr = instrs[i]; if (instr.OpCode.Code != Code.Ldflda) continue; - var field = instr.Operand as FieldReference; - if (field == null) + var structField = instr.Operand as FieldReference; + if (structField == null || !structFieldsToFix.find(structField)) continue; - if (!oldFieldToken.ContainsKey(field.MetadataToken.ToInt32())) - continue; - instrs.RemoveAt(i); + + var ldStFld = instrs[findLdStFieldIndex(instrs, i + 1)]; + ldStFld.Operand = getNewField(structField, ldStFld.Operand as FieldReference); + instrsToRemove.Add(i); } + if (instrsToRemove.Count > 0) + block.remove(instrsToRemove); } } + + FieldDefinition getNewField(FieldReference structField, FieldReference oldFieldRef) { + var fieldsDict = typeToFieldsDict.find(structField.DeclaringType); + if (fieldsDict == null) + throw new ApplicationException("Could not find structField declaringType"); + var newField = fieldsDict.find(oldFieldRef); + if (newField == null) + throw new ApplicationException("Could not find new field"); + return newField; + } + + static int findLdStFieldIndex(IList instrs, int index) { + int stack = 0; + for (int i = index; i < instrs.Count; i++) { + var instr = instrs[i]; + + if (stack == 0 && (instr.OpCode.Code == Code.Ldfld || instr.OpCode.Code == Code.Ldflda)) + return i; + if (stack == 1 && instr.OpCode.Code == Code.Stfld) + return i; + + int pushes, pops; + DotNetUtils.calculateStackUsage(instr.Instruction, false, out pushes, out pops); + stack -= pops; + if (stack < 0) + break; + stack += pushes; + } + throw new ApplicationException("Could not find ldfld/stfld"); + } + + public void cleanUp() { + foreach (var field in structFieldsToFix.getKeys()) + field.DeclaringType.Fields.Remove(field); + } } }