diff --git a/blocks/ScopeBlock.cs b/blocks/ScopeBlock.cs index af30eedc..2ca81c95 100644 --- a/blocks/ScopeBlock.cs +++ b/blocks/ScopeBlock.cs @@ -290,5 +290,12 @@ namespace de4dot.blocks { throw new ApplicationException("Could not remove dead block"); block.removeGuaranteedDeadBlock(); } + + public void add(Block block) { + if (block.Parent != null) + throw new ApplicationException("Block already has a parent"); + baseBlocks.Add(block); + block.Parent = this; + } } } diff --git a/blocks/cflow/SwitchCflowDeobfuscator.cs b/blocks/cflow/SwitchCflowDeobfuscator.cs index efddc005..ae61943e 100644 --- a/blocks/cflow/SwitchCflowDeobfuscator.cs +++ b/blocks/cflow/SwitchCflowDeobfuscator.cs @@ -41,6 +41,9 @@ namespace de4dot.blocks.cflow { if (isSwitchType1(switchBlock) && deobfuscateType1(switchBlock)) return true; + if (isSwitchType2(switchBlock) && deobfuscateType2(switchBlock)) + return true; + if (switchBlock.FirstInstr.isLdloc() && fixSwitchBranch(switchBlock)) return true; @@ -60,6 +63,36 @@ namespace de4dot.blocks.cflow { return switchBlock.FirstInstr.isLdloc(); } + bool isSwitchType2(Block switchBlock) { + VariableDefinition local = null; + foreach (var instr in switchBlock.Instructions) { + if (!instr.isLdloc()) + continue; + local = Instr.getLocalVar(blocks.Locals, instr); + break; + } + if (local == null) + return false; + + foreach (var source in switchBlock.Sources) { + var instrs = source.Instructions; + for (int i = 1; i < instrs.Count; i++) { + var ldci4 = instrs[i - 1]; + if (!ldci4.isLdcI4()) + continue; + var stloc = instrs[i]; + if (!stloc.isStloc()) + continue; + if (Instr.getLocalVar(blocks.Locals, stloc) != local) + continue; + + return true; + } + } + + return false; + } + bool isStLdlocBranch(Block switchBlock, bool isSwitch) { int numInstrs = 2 + (isSwitch ? 1 : 0); return switchBlock.Instructions.Count == numInstrs && @@ -295,6 +328,83 @@ namespace de4dot.blocks.cflow { return changed; } + bool deobfuscateType2(Block switchBlock) { + bool changed = false; + + var bccSources = new List(); + foreach (var source in new List(switchBlock.Sources)) { + if (source.LastInstr.isConditionalBranch()) { + bccSources.Add(source); + continue; + } + if (!source.canAppend(switchBlock)) + continue; + if (!willHaveKnownTarget(switchBlock, source)) + continue; + + source.append(switchBlock); + changed = true; + } + + foreach (var bccSource in bccSources) { + if (!willHaveKnownTarget(switchBlock, bccSource)) + continue; + var consts = getBccLocalConstants(bccSource); + if (consts.Count == 0) + continue; + var newFallThrough = createBlock(consts, bccSource.FallThrough); + var newTarget = createBlock(consts, bccSource.Targets[0]); + var oldFallThrough = bccSource.FallThrough; + var oldTarget = bccSource.Targets[0]; + bccSource.setNewFallThrough(newFallThrough); + bccSource.setNewTarget(0, newTarget); + newFallThrough.setNewFallThrough(oldFallThrough); + newTarget.setNewFallThrough(oldTarget); + changed = true; + } + + return changed; + } + + static Block createBlock(Dictionary consts, Block fallThrough) { + var block = new Block(); + foreach (var kv in consts) { + block.Instructions.Add(new Instr(DotNetUtils.createLdci4(kv.Value))); + block.Instructions.Add(new Instr(Instruction.Create(OpCodes.Stloc, kv.Key))); + } + fallThrough.Parent.add(block); + return block; + } + + Dictionary getBccLocalConstants(Block block) { + var dict = new Dictionary(); + var instrs = block.Instructions; + for (int i = 0; i < instrs.Count; i++) { + var instr = instrs[i]; + if (instr.isStloc()) { + var local = Instr.getLocalVar(blocks.Locals, instr); + if (local == null) + continue; + var ldci4 = i == 0 ? null : instrs[i - 1]; + if (ldci4 == null || !ldci4.isLdcI4()) + dict.Remove(local); + else + dict[local] = ldci4.getLdcI4Value(); + } + else if (instr.isLdloc()) { + var local = Instr.getLocalVar(blocks.Locals, instr); + if (local != null) + dict.Remove(local); + } + else if (instr.OpCode.Code == Code.Ldloca || instr.OpCode.Code == Code.Ldloca_S) { + var local = instr.Operand as VariableDefinition; + if (local != null) + dict.Remove(local); + } + } + return dict; + } + bool emulateGetTarget(Block switchBlock, out Block target) { instructionEmulator.init(blocks); try {