diff --git a/de4dot.code/deobfuscators/TypesRestorer.cs b/de4dot.code/deobfuscators/TypesRestorer.cs index b8327b52..e23f4635 100644 --- a/de4dot.code/deobfuscators/TypesRestorer.cs +++ b/de4dot.code/deobfuscators/TypesRestorer.cs @@ -29,27 +29,42 @@ namespace de4dot.deobfuscators { class TypesRestorer { ModuleDefinition module; List allMethods; - Dictionary argInfos = new Dictionary(); - List changedArgInfos = new List(); - Dictionary fieldWrites = new Dictionary(); + Dictionary> argInfos = new Dictionary>(); + List> changedArgInfos = new List>(); + List> changedReturnTypes = new List>(); + TypeInfo methodReturnInfo; + Dictionary> fieldWrites = new Dictionary>(); - class ArgAccessInfo { + class TypeInfo { public Dictionary types = new Dictionary(); public TypeReference newType = null; - public ParameterDefinition arg; + public T arg; - public ArgAccessInfo(ParameterDefinition arg) { + public TypeInfo(T arg) { this.arg = arg; } - } - class FieldWriteInfo { - public Dictionary types = new Dictionary(); - public TypeReference newType = null; - public FieldDefinition field; + public bool updateNewType() { + if (types.Count == 0) + return false; - public FieldWriteInfo(FieldDefinition field) { - this.field = field; + TypeReference theNewType = null; + foreach (var key in types.Keys) { + if (theNewType == null) { + theNewType = key.TypeReference; + continue; + } + theNewType = getCommonBaseClass(theNewType, key.TypeReference); + if (theNewType == null) + break; + } + if (theNewType == null) + return false; + if (MemberReferenceHelper.compareTypes(theNewType, newType)) + return false; + + newType = theNewType; + return true; } } @@ -68,7 +83,7 @@ namespace de4dot.deobfuscators { continue; var key = new FieldReferenceAndDeclaringTypeKey(field); - fieldWrites[key] = new FieldWriteInfo(field); + fieldWrites[key] = new TypeInfo(field); } } @@ -83,82 +98,96 @@ namespace de4dot.deobfuscators { bool deobfuscateMethods() { changedArgInfos.Clear(); + changedReturnTypes.Clear(); foreach (var method in allMethods) { + methodReturnInfo = new TypeInfo(method.MethodReturnType.Parameter2); deobfuscateMethod(method); + if (methodReturnInfo.updateNewType()) + changedReturnTypes.Add(methodReturnInfo); + foreach (var info in argInfos.Values) { - if (info.types.Count == 0) - continue; - - TypeReference newType = null; - foreach (var key in info.types.Keys) { - if (newType == null) { - newType = key.TypeReference; - continue; - } - newType = getCommonBaseClass(newType, key.TypeReference); - if (newType == null) - break; - } - if (newType == null) - continue; - if (MemberReferenceHelper.compareTypes(newType, info.newType)) - continue; - - info.newType = newType; - changedArgInfos.Add(info); + if (info.updateNewType()) + changedArgInfos.Add(info); } } - if (changedArgInfos.Count == 0) + if (changedArgInfos.Count == 0 && changedReturnTypes.Count == 0) return false; - changedArgInfos.Sort((a, b) => { - if (a.arg.Method.MetadataToken.ToInt32() < b.arg.Method.MetadataToken.ToInt32()) return -1; - if (a.arg.Method.MetadataToken.ToInt32() > b.arg.Method.MetadataToken.ToInt32()) return 1; - - if (a.arg.Index < b.arg.Index) return -1; - if (a.arg.Index < b.arg.Index) return 1; - - return 0; - }); + changedArgInfos.Sort((a, b) => sortTypeInfos(a, b)); + changedReturnTypes.Sort((a, b) => sortTypeInfos(a, b)); bool changed = false; - Log.v("Changing method arg types from object -> real type"); - Log.indent(); - IMethodSignature updatedMethod = null; - Log.indent(); - foreach (var info in changedArgInfos) { - if (info.newType == null || MemberReferenceHelper.isSystemObject(info.newType)) - continue; - if (updatedMethod == null || updatedMethod != info.arg.Method) { - updatedMethod = info.arg.Method; - Log.deIndent(); - Log.v("Method {0:X8}", updatedMethod.MetadataToken.ToInt32()); - Log.indent(); + if (changedArgInfos.Count > 0) { + Log.v("Changing method arg types from object -> real type"); + Log.indent(); + IMethodSignature updatedMethod = null; + Log.indent(); + foreach (var info in changedArgInfos) { + if (info.newType == null || MemberReferenceHelper.isSystemObject(info.newType)) + continue; + + if (updatedMethod == null || updatedMethod != info.arg.Method) { + updatedMethod = info.arg.Method; + Log.deIndent(); + Log.v("Method {0:X8}", updatedMethod.MetadataToken.ToInt32()); + Log.indent(); + } + Log.v("{0}: new type: {1} ({2:X8})", info.arg.Index, info.newType, info.newType.MetadataToken.ToInt32()); + info.arg.ParameterType = info.newType; + changed = true; } - Log.v("{0}: new type: {1} ({2:X8})", info.arg.Index, info.newType, info.newType.MetadataToken.ToInt32()); - info.arg.ParameterType = info.newType; - changed = true; + Log.deIndent(); + Log.deIndent(); } - Log.deIndent(); - Log.deIndent(); + + if (changedReturnTypes.Count > 0) { + Log.v("Changing method return types from object -> real type"); + Log.indent(); + foreach (var info in changedReturnTypes) { + if (info.newType == null || MemberReferenceHelper.isSystemObject(info.newType)) + continue; + Log.v("{0:X8}: new type {1} ({2:X8})", info.arg.Method.MetadataToken.ToInt32(), info.newType, info.newType.MetadataToken.ToInt32()); + info.arg.Method.MethodReturnType.ReturnType = info.newType; + info.arg.ParameterType = info.newType; + changed = true; + } + Log.deIndent(); + } + return changed; } + static int sortTypeInfos(TypeInfo a, TypeInfo b) { + if (a.arg.Method.MetadataToken.ToInt32() < b.arg.Method.MetadataToken.ToInt32()) + return -1; + if (a.arg.Method.MetadataToken.ToInt32() > b.arg.Method.MetadataToken.ToInt32()) + return 1; + + if (a.arg.Index < b.arg.Index) + return -1; + if (a.arg.Index < b.arg.Index) + return 1; + + return 0; + } + void deobfuscateMethod(MethodDefinition method) { if (!method.IsStatic || method.Body == null) return; + bool fixReturnType = MemberReferenceHelper.isSystemObject(method.MethodReturnType.ReturnType); + argInfos.Clear(); foreach (var arg in method.Parameters) { if (arg.ParameterType == null || arg.ParameterType.IsValueType) continue; if (!MemberReferenceHelper.isSystemObject(arg.ParameterType)) continue; - argInfos[arg] = new ArgAccessInfo(arg); + argInfos[arg] = new TypeInfo(arg); } - if (argInfos.Count == 0) + if (argInfos.Count == 0 && !fixReturnType) return; var methodParams = DotNetUtils.getParameters(method); @@ -169,6 +198,15 @@ namespace de4dot.deobfuscators { for (int i = 0; i < instructions.Count; i++) { var instr = instructions[i]; switch (instr.OpCode.Code) { + case Code.Ret: + if (!fixReturnType) + break; + var type = getLoadedType(method, instructions, i); + if (type == null) + break; + methodReturnInfo.types[new TypeReferenceKey(type)] = true; + break; + case Code.Call: case Code.Calli: case Code.Callvirt: @@ -327,12 +365,10 @@ namespace de4dot.deobfuscators { if (methodParam == null || type == null) return false; - if (type.IsValueType) - return false; - if (MemberReferenceHelper.isSystemObject(type)) + if (!isValidType(type)) return false; - ArgAccessInfo info; + TypeInfo info; if (!argInfos.TryGetValue(methodParam, out info)) return false; var key = new TypeReferenceKey(type); @@ -389,13 +425,13 @@ namespace de4dot.deobfuscators { if (!updateFields()) return false; - var infos = new List(fieldWrites.Values); + var infos = new List>(fieldWrites.Values); infos.Sort((a, b) => { - if (a.field.DeclaringType.MetadataToken.ToInt32() < b.field.DeclaringType.MetadataToken.ToInt32()) return -1; - if (a.field.DeclaringType.MetadataToken.ToInt32() > b.field.DeclaringType.MetadataToken.ToInt32()) return 1; + if (a.arg.DeclaringType.MetadataToken.ToInt32() < b.arg.DeclaringType.MetadataToken.ToInt32()) return -1; + if (a.arg.DeclaringType.MetadataToken.ToInt32() > b.arg.DeclaringType.MetadataToken.ToInt32()) return 1; - if (a.field.MetadataToken.ToInt32() < b.field.MetadataToken.ToInt32()) return -1; - if (a.field.MetadataToken.ToInt32() > b.field.MetadataToken.ToInt32()) return 1; + if (a.arg.MetadataToken.ToInt32() < b.arg.MetadataToken.ToInt32()) return -1; + if (a.arg.MetadataToken.ToInt32() > b.arg.MetadataToken.ToInt32()) return 1; return 0; }); @@ -409,9 +445,9 @@ namespace de4dot.deobfuscators { if (info.newType == null || MemberReferenceHelper.isSystemObject(info.newType)) continue; - fieldWrites.Remove(new FieldReferenceAndDeclaringTypeKey(info.field)); - Log.v("{0:X8}: new type: {1} ({2:X8})", info.field.MetadataToken.ToInt32(), info.newType, info.newType.MetadataToken.ToInt32()); - info.field.FieldType = info.newType; + fieldWrites.Remove(new FieldReferenceAndDeclaringTypeKey(info.arg)); + Log.v("{0:X8}: new type: {1} ({2:X8})", info.arg.MetadataToken.ToInt32(), info.newType, info.newType.MetadataToken.ToInt32()); + info.arg.FieldType = info.newType; changed = true; } Log.deIndent(); @@ -432,122 +468,114 @@ namespace de4dot.deobfuscators { continue; var field = instr.Operand as FieldReference; - FieldWriteInfo info; + TypeInfo info; if (!fieldWrites.TryGetValue(new FieldReferenceAndDeclaringTypeKey(field), out info)) continue; - int instrIndex = i; - var prev = getPreviousInstruction(instructions, ref instrIndex); - if (prev == null) - continue; - - TypeReference fieldType; - switch (prev.OpCode.Code) { - case Code.Ldstr: - fieldType = module.TypeSystem.String; - break; - - case Code.Call: - case Code.Calli: - case Code.Callvirt: - var calledMethod = prev.Operand as MethodReference; - if (calledMethod == null) - continue; - fieldType = calledMethod.MethodReturnType.ReturnType; - break; - - case Code.Newarr: - fieldType = prev.Operand as TypeReference; - if (fieldType == null) - continue; - fieldType = new ArrayType(fieldType); - break; - - case Code.Newobj: - var ctor = prev.Operand as MethodReference; - if (ctor == null) - continue; - fieldType = ctor.DeclaringType; - break; - - case Code.Castclass: - case Code.Isinst: - fieldType = prev.Operand as TypeReference; - break; - - case Code.Ldarg: - case Code.Ldarg_S: - case Code.Ldarg_0: - case Code.Ldarg_1: - case Code.Ldarg_2: - case Code.Ldarg_3: - fieldType = DotNetUtils.getArgType(method, prev); - break; - - case Code.Ldloc: - case Code.Ldloc_S: - case Code.Ldloc_0: - case Code.Ldloc_1: - case Code.Ldloc_2: - case Code.Ldloc_3: - var local = DotNetUtils.getLocalVar(method.Body.Variables, prev); - if (local == null) - continue; - fieldType = local.VariableType; - break; - - case Code.Ldfld: - case Code.Ldsfld: - var field2 = prev.Operand as FieldReference; - if (field2 == null) - continue; - fieldType = field2.FieldType; - break; - - default: - continue; - } - + var fieldType = getLoadedType(method, instructions, i); if (fieldType == null) continue; - if (fieldType.IsValueType) - continue; - if (MemberReferenceHelper.isSystemObject(fieldType)) - continue; - if (MemberReferenceHelper.verifyType(fieldType, "mscorlib", "System.Void")) - continue; - if (fieldType is GenericParameter) - continue; info.types[new TypeReferenceKey(fieldType)] = true; } } bool changed = false; - foreach (var info in fieldWrites.Values) { - if (info.types.Count == 0) - continue; + foreach (var info in fieldWrites.Values) + changed |= info.updateNewType(); + return changed; + } - TypeReference newType = null; - foreach (var key in info.types.Keys) { - if (newType == null) { - newType = key.TypeReference; - continue; - } - newType = getCommonBaseClass(newType, key.TypeReference); - if (newType == null) - break; - } - if (newType == null) - continue; - if (MemberReferenceHelper.compareTypes(newType, info.newType)) - continue; + TypeReference getLoadedType(MethodDefinition method, IList instructions, int instrIndex) { + var prev = getPreviousInstruction(instructions, ref instrIndex); + if (prev == null) + return null; - info.newType = newType; - changed = true; + TypeReference fieldType; + switch (prev.OpCode.Code) { + case Code.Ldstr: + fieldType = module.TypeSystem.String; + break; + + case Code.Call: + case Code.Calli: + case Code.Callvirt: + var calledMethod = prev.Operand as MethodReference; + if (calledMethod == null) + return null; + fieldType = calledMethod.MethodReturnType.ReturnType; + break; + + case Code.Newarr: + fieldType = prev.Operand as TypeReference; + if (fieldType == null) + return null; + fieldType = new ArrayType(fieldType); + break; + + case Code.Newobj: + var ctor = prev.Operand as MethodReference; + if (ctor == null) + return null; + fieldType = ctor.DeclaringType; + break; + + case Code.Castclass: + case Code.Isinst: + fieldType = prev.Operand as TypeReference; + break; + + case Code.Ldarg: + case Code.Ldarg_S: + case Code.Ldarg_0: + case Code.Ldarg_1: + case Code.Ldarg_2: + case Code.Ldarg_3: + fieldType = DotNetUtils.getArgType(method, prev); + break; + + case Code.Ldloc: + case Code.Ldloc_S: + case Code.Ldloc_0: + case Code.Ldloc_1: + case Code.Ldloc_2: + case Code.Ldloc_3: + var local = DotNetUtils.getLocalVar(method.Body.Variables, prev); + if (local == null) + return null; + fieldType = local.VariableType; + break; + + case Code.Ldfld: + case Code.Ldsfld: + var field2 = prev.Operand as FieldReference; + if (field2 == null) + return null; + fieldType = field2.FieldType; + break; + + default: + return null; } - return changed; + if (!isValidType(fieldType)) + return null; + + return fieldType; + } + + static bool isValidType(TypeReference type) { + if (type == null) + return false; + if (type.IsValueType) + return false; + if (MemberReferenceHelper.isSystemObject(type)) + return false; + if (MemberReferenceHelper.verifyType(type, "mscorlib", "System.Void")) + return false; + if (type is GenericParameter) + return false; + return true; } static TypeReference getCommonBaseClass(TypeReference a, TypeReference b) {