diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index d124243d..bb780311 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -87,6 +87,7 @@
+
diff --git a/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs b/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs
index 50d95bdb..7ddcf055 100644
--- a/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/DeepSea/Deobfuscator.cs
@@ -29,6 +29,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
BoolOption removeInlinedMethods;
BoolOption decryptResources;
BoolOption dumpEmbeddedAssemblies;
+ BoolOption restoreFields;
public DeobfuscatorInfo()
: base() {
@@ -36,6 +37,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
removeInlinedMethods = new BoolOption(null, makeArgName("remove-inlined"), "Remove inlined methods", true);
decryptResources = new BoolOption(null, makeArgName("rsrc"), "Decrypt resources", true);
dumpEmbeddedAssemblies = new BoolOption(null, makeArgName("embedded"), "Dump embedded assemblies", true);
+ restoreFields = new BoolOption(null, makeArgName("fields"), "Restore fields", true);
}
public override string Name {
@@ -53,6 +55,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
RemoveInlinedMethods = removeInlinedMethods.get(),
DecryptResources = decryptResources.get(),
DumpEmbeddedAssemblies = dumpEmbeddedAssemblies.get(),
+ RestoreFields = restoreFields.get(),
});
}
@@ -62,6 +65,7 @@ namespace de4dot.code.deobfuscators.DeepSea {
removeInlinedMethods,
decryptResources,
dumpEmbeddedAssemblies,
+ restoreFields,
};
}
}
@@ -74,12 +78,14 @@ namespace de4dot.code.deobfuscators.DeepSea {
StringDecrypter stringDecrypter;
ResourceResolver resourceResolver;
AssemblyResolver assemblyResolver;
+ FieldsRestorer fieldsRestorer;
internal class Options : OptionsBase {
public bool InlineMethods { get; set; }
public bool RemoveInlinedMethods { get; set; }
public bool DecryptResources { get; set; }
public bool DumpEmbeddedAssemblies { get; set; }
+ public bool RestoreFields { get; set; }
}
public override string Type {
@@ -170,6 +176,11 @@ done:
public override void deobfuscateBegin() {
base.deobfuscateBegin();
+ if (options.RestoreFields) {
+ fieldsRestorer = new FieldsRestorer(module);
+ fieldsRestorer.initialize();
+ }
+
foreach (var method in stringDecrypter.DecrypterMethods) {
staticStringInliner.add(method, (method2, args) => {
return stringDecrypter.decrypt(method2, args);
@@ -213,9 +224,18 @@ done:
addMethodToBeRemoved(assemblyResolver.HandlerMethod, "Assembly resolver handler method");
}
+ public override void deobfuscateMethodEnd(blocks.Blocks blocks) {
+ if (options.RestoreFields)
+ fieldsRestorer.deobfuscate(blocks);
+ base.deobfuscateMethodEnd(blocks);
+ }
+
public override void deobfuscateEnd() {
removeInlinedMethods();
+ if (options.RestoreFields)
+ addTypesToBeRemoved(fieldsRestorer.FieldStructs, "Type with moved fields");
+
if (Operations.DecryptStrings != OpDecryptString.None) {
addMethodsToBeRemoved(stringDecrypter.DecrypterMethods, "String decrypter method");
stringDecrypter.cleanup();
diff --git a/de4dot.code/deobfuscators/DeepSea/FieldsRestorer.cs b/de4dot.code/deobfuscators/DeepSea/FieldsRestorer.cs
new file mode 100644
index 00000000..af67fcb0
--- /dev/null
+++ b/de4dot.code/deobfuscators/DeepSea/FieldsRestorer.cs
@@ -0,0 +1,164 @@
+/*
+ Copyright (C) 2011-2012 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 Mono.Cecil;
+using Mono.Cecil.Cil;
+using de4dot.blocks;
+
+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();
+
+ public List FieldStructs {
+ get {
+ var list = new List(structToOwner.Count);
+ foreach (var structType in structToOwner.getKeys()) {
+ if (structType.Methods.Count != 0)
+ continue;
+
+ list.Add(structType);
+ }
+ return list;
+ }
+ }
+
+ public FieldsRestorer(ModuleDefinition module) {
+ this.module = module;
+ }
+
+ public void initialize() {
+ foreach (var kv in getMovedTypes()) {
+ var structType = kv.Key;
+ var ownerType = kv.Value;
+ structToOwner.add(structType, ownerType);
+
+ 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;
+ }
+
+ 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
+ structType.Fields.Add(new FieldDefinition("a", FieldAttributes.Public, module.TypeSystem.Byte));
+ }
+ }
+
+ Dictionary getMovedTypes() {
+ var fieldTypeToTypes = new Dictionary>();
+ foreach (var type in module.GetTypes()) {
+ foreach (var field in type.Fields) {
+ var fieldType = DotNetUtils.getType(module, field.FieldType);
+ if (fieldType == null || !fieldType.IsValueType)
+ continue;
+ if ((fieldType.Attributes & ~TypeAttributes.Sealed) != TypeAttributes.NestedAssembly)
+ continue;
+ if (fieldType.NestedTypes.Count > 0)
+ continue;
+ if (fieldType.GenericParameters.Count > 0)
+ continue;
+ if (fieldType.Fields.Count == 0)
+ continue;
+ if (hasNonStaticMethods(fieldType))
+ continue;
+
+ List list;
+ if (!fieldTypeToTypes.TryGetValue(fieldType, out list))
+ fieldTypeToTypes[fieldType] = list = new List();
+ list.Add(type);
+ }
+ }
+
+ 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()) {
+ foreach (var field in type.Fields) {
+ if (field.DeclaringType != type)
+ removeType(candidates, field.FieldType);
+ }
+ foreach (var method in type.Methods) {
+ removeType(candidates, method.MethodReturnType.ReturnType);
+ foreach (var parameter in method.Parameters)
+ removeType(candidates, parameter.ParameterType);
+ if (method.Body != null) {
+ foreach (var local in method.Body.Variables)
+ removeType(candidates, local.VariableType);
+ }
+ }
+ }
+
+ return candidates;
+ }
+
+ void removeType(Dictionary candidates, TypeReference type) {
+ var typeDef = DotNetUtils.getType(module, type);
+ if (typeDef == null)
+ return;
+ candidates.Remove(typeDef);
+ }
+
+ static bool hasNonStaticMethods(TypeDefinition type) {
+ foreach (var method in type.Methods) {
+ if (method.Name == ".cctor")
+ continue;
+ if (!method.IsStatic)
+ return true;
+ if (method.GenericParameters.Count > 0)
+ continue;
+ if (method.Body == null)
+ return true;
+ if (method.HasPInvokeInfo || method.PInvokeInfo != null)
+ return true;
+ }
+ return false;
+ }
+
+ public void deobfuscate(Blocks blocks) {
+ foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
+ 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)
+ continue;
+ if (!oldFieldToken.ContainsKey(field.MetadataToken.ToInt32()))
+ continue;
+ instrs.RemoveAt(i);
+ }
+ }
+ }
+ }
+}