diff --git a/De4DotCommon.props b/De4DotCommon.props index 212d40db..2d2bc905 100644 --- a/De4DotCommon.props +++ b/De4DotCommon.props @@ -7,7 +7,7 @@ net48 net8.0 strict - latest + 12.0 true $(MSBuildThisFileDirectory)\de4dot.snk 3.4.0 diff --git a/de4dot.blocks/cflow/CflowUtils.cs b/de4dot.blocks/cflow/CflowUtils.cs index 99da3579..f3d6bd66 100644 --- a/de4dot.blocks/cflow/CflowUtils.cs +++ b/de4dot.blocks/cflow/CflowUtils.cs @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License */ using System.Collections.Generic; +using dnlib.DotNet.Emit; namespace de4dot.blocks.cflow { static class CflowUtils { @@ -27,9 +28,38 @@ static class CflowUtils { int index = intValue.Value; if (targets == null || index < 0 || index >= targets.Count) - return fallThrough; + return TryResolveConditionalFallthrough(fallThrough, intValue); else return targets[index]; } + + /** + * Some obfuscators have default blocks that dispatch additional cases. + * Example: 4e86d71a19f7f69471776817dc67585064b4b60542bc60e9450739bca63226ee + */ + private static Block? TryResolveConditionalFallthrough(Block? block, Int32Value value) { + while (true) { + if (block == null) return null; + + var instrs = block.Instructions; + if (instrs.Count < 3) return block; + if (!instrs[0].IsLdloc()) return block; + if (!instrs[1].IsLdcI4()) return block; + if (instrs[2].OpCode.Code is not (Code.Beq or Code.Beq_S or Code.Bne_Un or Code.Bne_Un_S)) return block; + + int constant = instrs[1].GetLdcI4Value(); + var branch = instrs[2]; + + int v = value.Value; + + bool taken = + branch.OpCode.Code is Code.Beq or Code.Beq_S ? (v == constant) : + branch.OpCode.Code is Code.Bne_Un or Code.Bne_Un_S && (v != constant); + + if (taken) + return block.Targets![0]; + block = block.FallThrough; + } + } } } diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index 72b2bcdf..dc76ee20 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -609,7 +609,13 @@ void Deobfuscate(MethodDef method, BlocksCflowDeobfuscator cflowDeobfuscator, Me deob.DeobfuscateMethodBegin(blocks); if (options.ControlFlowDeobfuscation) { cflowDeobfuscator.Initialize(blocks); - cflowDeobfuscator.Deobfuscate(); + try { + cflowDeobfuscator.Deobfuscate(); + } + catch (Exception) { + Logger.e("Error during cflow deobfuscation of {0} ({1:X8})", Utils.RemoveNewlines(method), method.MDToken.ToUInt32()); + throw; + } } if (deob.DeobfuscateOther(blocks) && options.ControlFlowDeobfuscation) diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/CflowConstantsInliner.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/CflowConstantsInliner.cs index 54f58dcc..d023bb6c 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/CflowConstantsInliner.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/CflowConstantsInliner.cs @@ -38,18 +38,13 @@ void Find() { continue; if (i + 1 >= instrs.Count) continue; - var stsfld = instrs[i + 1]; - if (stsfld.OpCode.Code != Code.Stsfld) + var store = instrs[i + 1]; + if (store.OpCode.Code is not (Code.Stsfld or Code.Stfld)) continue; - var key = stsfld.Operand as FieldDef; - if (key == null) + if (store.Operand is not FieldDef key) continue; - var value = ldcI4.GetLdcI4Value(); - if (!dictionary.ContainsKey(key)) - dictionary.Add(key, value); - else - dictionary[key] = value; + dictionary[key] = ldcI4.GetLdcI4Value(); } if (dictionary.Count < 100) { @@ -76,14 +71,21 @@ public void InlineAllConstants() { var instrs = method.Body.Instructions; for (var i = 0; i < instrs.Count; i++) { - var ldsfld = instrs[i]; - if (ldsfld.OpCode.Code != Code.Ldsfld) + bool nopNext = false; + var load = instrs[i]; + if (load.OpCode.Code != Code.Ldsfld) continue; - var ldsfldValue = ldsfld.Operand as FieldDef; - if (ldsfldValue == null) + if (i < instrs.Count - 1 && instrs[i + 1].OpCode.Code == Code.Ldfld) { + load = instrs[i + 1]; + nopNext = true; + } + if (load.Operand is not FieldDef loadField) continue; - if (dictionary.TryGetValue(ldsfldValue, out var value)) + if (dictionary.TryGetValue(loadField, out var value)) { instrs[i] = Instruction.CreateLdcI4(value); + if (nopNext) + instrs[i + 1] = Instruction.Create(OpCodes.Nop); + } } } } diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs index 64f7db4a..7102f70a 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/Deobfuscator.cs @@ -25,6 +25,7 @@ You should have received a copy of the GNU General Public License using dnlib.DotNet.Writer; using de4dot.blocks; using de4dot.blocks.cflow; +using de4dot.code.deobfuscators.dotNET_Reactor.v4.vm; namespace de4dot.code.deobfuscators.dotNET_Reactor.v4 { public class DeobfuscatorInfo : DeobfuscatorInfoBase { @@ -42,6 +43,7 @@ public class DeobfuscatorInfo : DeobfuscatorInfoBase { BoolOption removeNamespaces; BoolOption removeAntiStrongName; BoolOption renameShort; + BoolOption devirtualize; public DeobfuscatorInfo() : base(DEFAULT_REGEX) { @@ -55,6 +57,7 @@ public DeobfuscatorInfo() removeNamespaces = new BoolOption(null, MakeArgName("ns1"), "Clear namespace if there's only one class in it", true); removeAntiStrongName = new BoolOption(null, MakeArgName("sn"), "Remove anti strong name code", true); renameShort = new BoolOption(null, MakeArgName("sname"), "Rename short names", false); + devirtualize = new BoolOption(null, MakeArgName("devirtualize"), "Devirtualize methods", true); } public override string Name => THE_NAME; @@ -73,6 +76,7 @@ public override IDeobfuscator CreateDeobfuscator() => RemoveNamespaces = removeNamespaces.Get(), RemoveAntiStrongName = removeAntiStrongName.Get(), RenameShort = renameShort.Get(), + Devirtualize = devirtualize.Get(), }); protected override IEnumerable