From f9ed45c670002d9c52f1d01b6de571bea319dbd2 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 28 Sep 2013 19:07:03 +0200 Subject: [PATCH] Restore ldnull instructions --- de4dot.code/de4dot.code.csproj | 1 + .../CryptoObfuscator/CoMethodCallInliner.cs | 2 +- .../CryptoObfuscator/Deobfuscator.cs | 10 +- .../CryptoObfuscator/InlinedMethodTypes.cs | 16 ++- .../CryptoObfuscator/LdnullFixer.cs | 126 ++++++++++++++++++ 5 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 de4dot.code/deobfuscators/CryptoObfuscator/LdnullFixer.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 881d9378..278f80bd 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -131,6 +131,7 @@ + diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/CoMethodCallInliner.cs b/de4dot.code/deobfuscators/CryptoObfuscator/CoMethodCallInliner.cs index 312196f4..d632ff43 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/CoMethodCallInliner.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/CoMethodCallInliner.cs @@ -37,7 +37,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { return false; if (method.HasGenericParameters) return false; - if (!inlinedMethodTypes.IsValidMethodType(method.DeclaringType)) + if (!InlinedMethodTypes.IsValidMethodType(method.DeclaringType)) return false; return true; diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs b/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs index 874c4632..851c6ae3 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs @@ -32,12 +32,14 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { BoolOption removeTamperProtection; BoolOption decryptConstants; BoolOption inlineMethods; + BoolOption fixLdnull; public DeobfuscatorInfo() : base(DEFAULT_REGEX) { removeTamperProtection = new BoolOption(null, MakeArgName("tamper"), "Remove tamper protection code", true); decryptConstants = new BoolOption(null, MakeArgName("consts"), "Decrypt constants", true); inlineMethods = new BoolOption(null, MakeArgName("inline"), "Inline short methods", true); + fixLdnull = new BoolOption(null, MakeArgName("ldnull"), "Restore ldnull instructions", true); } public override string Name { @@ -54,6 +56,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { RemoveTamperProtection = removeTamperProtection.get(), DecryptConstants = decryptConstants.get(), InlineMethods = inlineMethods.get(), + FixLdnull = fixLdnull.get(), }); } @@ -62,6 +65,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { removeTamperProtection, decryptConstants, inlineMethods, + fixLdnull, }; } } @@ -93,6 +97,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { public bool RemoveTamperProtection { get; set; } public bool DecryptConstants { get; set; } public bool InlineMethods { get; set; } + public bool FixLdnull { get; set; } } public override string Type { @@ -275,13 +280,14 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { } public override void DeobfuscateEnd() { + if (options.FixLdnull) + new LdnullFixer(module, inlinedMethodTypes).Restore(); RemoveProxyDelegates(proxyCallFixer); if (CanRemoveStringDecrypterType) { AddResourceToBeRemoved(stringDecrypter.Resource, "Encrypted strings"); AddTypeToBeRemoved(stringDecrypter.Type, "String decrypter type"); } - if (options.InlineMethods) - AddTypesToBeRemoved(inlinedMethodTypes.Types, "Inlined methods types"); + AddTypesToBeRemoved(inlinedMethodTypes.Types, "Inlined methods type"); base.DeobfuscateEnd(); } diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/InlinedMethodTypes.cs b/de4dot.code/deobfuscators/CryptoObfuscator/InlinedMethodTypes.cs index fce00acc..929c80f9 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/InlinedMethodTypes.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/InlinedMethodTypes.cs @@ -39,7 +39,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { } } - bool IsValidType(TypeDef type) { + static bool IsValidType(TypeDef type) { if (type == null) return false; @@ -62,7 +62,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { return true; } - public bool IsValidMethodType(TypeDef type) { + public static bool IsValidMethodType(TypeDef type) { if (!IsValidType(type)) return false; @@ -74,6 +74,18 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { return true; } + public static bool IsValidFieldType(TypeDef type) { + if (!IsValidType(type)) + return false; + + if (type.HasMethods) + return false; + if (type.Fields.Count != 1) + return false; + + return true; + } + public void Add(TypeDef type) { if (type == null || types.ContainsKey(type)) return; diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/LdnullFixer.cs b/de4dot.code/deobfuscators/CryptoObfuscator/LdnullFixer.cs new file mode 100644 index 00000000..3337153b --- /dev/null +++ b/de4dot.code/deobfuscators/CryptoObfuscator/LdnullFixer.cs @@ -0,0 +1,126 @@ +/* + Copyright (C) 2011-2013 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 de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace de4dot.code.deobfuscators.CryptoObfuscator { + class LdnullFixer { + readonly ModuleDef module; + readonly InlinedMethodTypes inlinedMethodTypes; + + public LdnullFixer(ModuleDef module, InlinedMethodTypes inlinedMethodTypes) { + this.module = module; + this.inlinedMethodTypes = inlinedMethodTypes; + } + + public void Restore() { + var fields = FindFieldTypes(FindFieldTypes()); + Restore(fields); + foreach (var field in fields.Keys) + inlinedMethodTypes.Add(field.DeclaringType); + } + + FieldDefAndDeclaringTypeDict FindFieldTypes() { + var dict = new FieldDefAndDeclaringTypeDict(); + + foreach (var type in module.GetTypes()) { + foreach (var method in type.Methods) { + var body = method.Body; + if (body == null) + continue; + foreach (var instr in body.Instructions) { + if (instr.OpCode.Code != Code.Ldsfld) + continue; + var field = instr.Operand as FieldDef; + if (field == null) + continue; + var declType = field.DeclaringType; + if (declType == null) + continue; + if (!InlinedMethodTypes.IsValidFieldType(declType)) + continue; + dict.Add(field, field); + } + } + } + + return dict; + } + + Dictionary FindFieldTypes(FieldDefAndDeclaringTypeDict fields) { + var validFields = new Dictionary(fields.Count); + foreach (var field in fields.GetKeys()) + validFields.Add(field, false); + + foreach (var type in module.GetTypes()) { + if (validFields.Count == 0) + break; + + foreach (var method in type.Methods) { + var body = method.Body; + if (body == null) + continue; + foreach (var instr in body.Instructions) { + if (instr.OpCode.Code == Code.Ldsfld) + continue; + var field = instr.Operand as IField; + if (field == null) + continue; + + var validType = fields.Find(field); + if (validType == null) + continue; + + validFields.Remove(validType); + } + } + } + + return validFields; + } + + int Restore(Dictionary nullFields) { + int numRestored = 0; + foreach (var type in module.GetTypes()) { + foreach (var method in type.Methods) { + var body = method.Body; + if (body == null) + continue; + foreach (var instr in body.Instructions) { + if (instr.OpCode.Code != Code.Ldsfld) + continue; + var field = instr.Operand as FieldDef; + if (field == null) + continue; + if (!nullFields.ContainsKey(field)) + continue; + + instr.OpCode = OpCodes.Ldnull; + instr.Operand = null; + numRestored++; + } + } + } + return numRestored; + } + } +}