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