From 80acf1d59f8ec15ca88860ea09eb36dcc689aefc Mon Sep 17 00:00:00 2001 From: de4dot Date: Tue, 18 Oct 2011 23:31:50 +0200 Subject: [PATCH] Add switch cflow deobfuscator --- blocks/Instr.cs | 34 +++-- blocks/ScopeBlock.cs | 6 +- blocks/blocks.csproj | 6 +- ...bfuscator.cs => BlockCflowDeobfuscator.cs} | 76 +++++------- ...fuscator.cs => BlocksCflowDeobfuscator.cs} | 31 +++-- blocks/cflow/CflowUtils.cs | 35 ++++++ blocks/cflow/InstructionEmulator.cs | 117 +++++++++--------- blocks/cflow/SwitchCflowDeobfuscator.cs | 114 +++++++++++++++++ blocks/cflow/Value.cs | 28 +++++ de4dot.code/ObfuscatedFile.cs | 2 +- 10 files changed, 326 insertions(+), 123 deletions(-) rename blocks/cflow/{BlockControlFlowDeobfuscator.cs => BlockCflowDeobfuscator.cs} (73%) rename blocks/cflow/{BlocksControlFlowDeobfuscator.cs => BlocksCflowDeobfuscator.cs} (68%) create mode 100644 blocks/cflow/CflowUtils.cs create mode 100644 blocks/cflow/SwitchCflowDeobfuscator.cs diff --git a/blocks/Instr.cs b/blocks/Instr.cs index 2c93799d..0aee4d2e 100644 --- a/blocks/Instr.cs +++ b/blocks/Instr.cs @@ -134,22 +134,32 @@ namespace de4dot.blocks { // Return true if it's one of the stloc instructions public bool isStloc() { - return OpCode == OpCodes.Stloc || - OpCode == OpCodes.Stloc_0 || - OpCode == OpCodes.Stloc_1 || - OpCode == OpCodes.Stloc_2 || - OpCode == OpCodes.Stloc_3 || - OpCode == OpCodes.Stloc_S; + switch (OpCode.Code) { + case Code.Stloc: + case Code.Stloc_0: + case Code.Stloc_1: + case Code.Stloc_2: + case Code.Stloc_3: + case Code.Stloc_S: + return true; + default: + return false; + } } // Returns true if it's one of the ldloc instructions public bool isLdloc() { - return OpCode == OpCodes.Ldloc || - OpCode == OpCodes.Ldloc_0 || - OpCode == OpCodes.Ldloc_1 || - OpCode == OpCodes.Ldloc_2 || - OpCode == OpCodes.Ldloc_3 || - OpCode == OpCodes.Ldloc_S; + switch (OpCode.Code) { + case Code.Ldloc: + case Code.Ldloc_0: + case Code.Ldloc_1: + case Code.Ldloc_2: + case Code.Ldloc_3: + case Code.Ldloc_S: + return true; + default: + return false; + } } public bool isNop() { diff --git a/blocks/ScopeBlock.cs b/blocks/ScopeBlock.cs index 08798c30..0a2a2b65 100644 --- a/blocks/ScopeBlock.cs +++ b/blocks/ScopeBlock.cs @@ -241,7 +241,8 @@ namespace de4dot.blocks { // For each block, if it has only one target, and the target has only one source, then // merge them into one block. - public void mergeBlocks() { + public int mergeBlocks() { + int mergedBlocks = 0; var blocks = findBlocks(); for (int i = 0; i < blocks.Count; i++) { var block = blocks[i]; @@ -263,7 +264,10 @@ namespace de4dot.blocks { if (targetIndex < i) i--; i--; // Redo since there may be more blocks we can merge + mergedBlocks++; } + + return mergedBlocks; } // If bb is in baseBlocks (a direct child), return bb. If bb is a BaseBlock in a diff --git a/blocks/blocks.csproj b/blocks/blocks.csproj index 13fb747f..5f5137a6 100644 --- a/blocks/blocks.csproj +++ b/blocks/blocks.csproj @@ -35,16 +35,18 @@ - + + + - + diff --git a/blocks/cflow/BlockControlFlowDeobfuscator.cs b/blocks/cflow/BlockCflowDeobfuscator.cs similarity index 73% rename from blocks/cflow/BlockControlFlowDeobfuscator.cs rename to blocks/cflow/BlockCflowDeobfuscator.cs index e97ccce2..b7b02878 100644 --- a/blocks/cflow/BlockControlFlowDeobfuscator.cs +++ b/blocks/cflow/BlockCflowDeobfuscator.cs @@ -22,7 +22,7 @@ using Mono.Cecil; using Mono.Cecil.Cil; namespace de4dot.blocks.cflow { - class BlockControlFlowDeobfuscator { + class BlockCflowDeobfuscator { Block block; InstructionEmulator instructionEmulator = new InstructionEmulator(); @@ -95,11 +95,11 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) return emulateBranch(2, Int32Value.compareEq((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) return emulateBranch(2, Int64Value.compareEq((Int64Value)val1, (Int64Value)val2)); - else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) + else if (val1.isNull() && val2.isNull()) return emulateBranch(2, true); else return false; @@ -109,11 +109,11 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) return emulateBranch(2, Int32Value.compareNeq((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) return emulateBranch(2, Int64Value.compareNeq((Int64Value)val1, (Int64Value)val2)); - else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) + else if (val1.isNull() && val2.isNull()) return emulateBranch(2, false); else return false; @@ -123,9 +123,9 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) return emulateBranch(2, Int32Value.compareGe((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) return emulateBranch(2, Int64Value.compareGe((Int64Value)val1, (Int64Value)val2)); else return false; @@ -135,9 +135,9 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) return emulateBranch(2, Int32Value.compareGe_Un((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) return emulateBranch(2, Int64Value.compareGe_Un((Int64Value)val1, (Int64Value)val2)); else return false; @@ -147,9 +147,9 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) return emulateBranch(2, Int32Value.compareGt((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) return emulateBranch(2, Int64Value.compareGt((Int64Value)val1, (Int64Value)val2)); else return false; @@ -159,9 +159,9 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) return emulateBranch(2, Int32Value.compareGt_Un((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) return emulateBranch(2, Int64Value.compareGt_Un((Int64Value)val1, (Int64Value)val2)); else return false; @@ -171,9 +171,9 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) return emulateBranch(2, Int32Value.compareLe((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) return emulateBranch(2, Int64Value.compareLe((Int64Value)val1, (Int64Value)val2)); else return false; @@ -183,9 +183,9 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) return emulateBranch(2, Int32Value.compareLe_Un((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) return emulateBranch(2, Int64Value.compareLe_Un((Int64Value)val1, (Int64Value)val2)); else return false; @@ -195,9 +195,9 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) return emulateBranch(2, Int32Value.compareLt((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) return emulateBranch(2, Int64Value.compareLt((Int64Value)val1, (Int64Value)val2)); else return false; @@ -207,9 +207,9 @@ namespace de4dot.blocks.cflow { var val2 = instructionEmulator.pop(); var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) return emulateBranch(2, Int32Value.compareLt_Un((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) return emulateBranch(2, Int64Value.compareLt_Un((Int64Value)val1, (Int64Value)val2)); else return false; @@ -218,11 +218,11 @@ namespace de4dot.blocks.cflow { bool emulate_Brfalse() { var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32) + if (val1.isInt32()) return emulateBranch(1, Int32Value.compareFalse((Int32Value)val1)); - else if (val1.valueType == ValueType.Int64) + else if (val1.isInt64()) return emulateBranch(1, Int64Value.compareFalse((Int64Value)val1)); - else if (val1.valueType == ValueType.Null) + else if (val1.isNull()) return emulateBranch(1, true); else return false; @@ -231,11 +231,11 @@ namespace de4dot.blocks.cflow { bool emulate_Brtrue() { var val1 = instructionEmulator.pop(); - if (val1.valueType == ValueType.Int32) + if (val1.isInt32()) return emulateBranch(1, Int32Value.compareTrue((Int32Value)val1)); - else if (val1.valueType == ValueType.Int64) + else if (val1.isInt64()) return emulateBranch(1, Int64Value.compareTrue((Int64Value)val1)); - else if (val1.valueType == ValueType.Null) + else if (val1.isNull()) return emulateBranch(1, false); else return false; @@ -244,22 +244,14 @@ namespace de4dot.blocks.cflow { bool emulate_Switch() { var val1 = instructionEmulator.pop(); - if (val1.valueType != ValueType.Int32) + if (!val1.isInt32()) + return false; + var target = CflowUtils.getSwitchTarget(block.Targets, block.FallThrough, (Int32Value)val1); + if (target == null) return false; - var int1 = (Int32Value)val1; - if (!int1.allBitsValid()) - return false; - - int index = int1.value; - var targets = block.Targets; - Block newTarget; - if (targets == null || index < 0 || index >= targets.Count) - newTarget = block.FallThrough; - else - newTarget = targets[index]; popPushedArgs(1); - block.replaceSwitchWithBranch(newTarget); + block.replaceSwitchWithBranch(target); return true; } } diff --git a/blocks/cflow/BlocksControlFlowDeobfuscator.cs b/blocks/cflow/BlocksCflowDeobfuscator.cs similarity index 68% rename from blocks/cflow/BlocksControlFlowDeobfuscator.cs rename to blocks/cflow/BlocksCflowDeobfuscator.cs index 01b99543..40fc0b85 100644 --- a/blocks/cflow/BlocksControlFlowDeobfuscator.cs +++ b/blocks/cflow/BlocksCflowDeobfuscator.cs @@ -21,8 +21,8 @@ using System.Collections.Generic; using Mono.Cecil.Cil; namespace de4dot.blocks.cflow { - public class BlocksControlFlowDeobfuscator { - BlockControlFlowDeobfuscator blockControlFlowDeobfuscator = new BlockControlFlowDeobfuscator(); + public class BlocksCflowDeobfuscator { + BlockCflowDeobfuscator blockControlFlowDeobfuscator = new BlockCflowDeobfuscator(); Blocks blocks; int numRemovedDeadBlocks; @@ -36,28 +36,41 @@ namespace de4dot.blocks.cflow { } public void deobfuscate() { + var allBlocks = new List(); + var switchCflowDeobfuscator = new SwitchCflowDeobfuscator(); bool changed; do { changed = false; - removeDeadBlocks(); - mergeBlocks(); - foreach (var block in blocks.MethodBlocks.getAllBlocks()) { + changed |= removeDeadBlocks(); + changed |= mergeBlocks(); + + allBlocks.Clear(); + allBlocks.AddRange(blocks.MethodBlocks.getAllBlocks()); + + foreach (var block in allBlocks) { var lastInstr = block.LastInstr; if (!DotNetUtils.isConditionalBranch(lastInstr.OpCode.Code) && lastInstr.OpCode.Code != Code.Switch) continue; blockControlFlowDeobfuscator.init(block, blocks.Method.Parameters, blocks.Locals); changed |= blockControlFlowDeobfuscator.deobfuscate(); } + + switchCflowDeobfuscator.init(blocks, allBlocks); + changed |= switchCflowDeobfuscator.deobfuscate(); } while (changed); } - void removeDeadBlocks() { - numRemovedDeadBlocks += new DeadBlocksRemover(blocks.MethodBlocks).remove(); + bool removeDeadBlocks() { + int count = new DeadBlocksRemover(blocks.MethodBlocks).remove(); + numRemovedDeadBlocks += count; + return count > 0; } - void mergeBlocks() { + bool mergeBlocks() { + bool changed = false; foreach (var scopeBlock in getAllScopeBlocks(blocks.MethodBlocks)) - scopeBlock.mergeBlocks(); + changed |= scopeBlock.mergeBlocks() > 0; + return changed; } IEnumerable getAllScopeBlocks(ScopeBlock scopeBlock) { diff --git a/blocks/cflow/CflowUtils.cs b/blocks/cflow/CflowUtils.cs new file mode 100644 index 00000000..b13ad442 --- /dev/null +++ b/blocks/cflow/CflowUtils.cs @@ -0,0 +1,35 @@ +/* + Copyright (C) 2011 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; + +namespace de4dot.blocks.cflow { + static class CflowUtils { + public static Block getSwitchTarget(IList targets, Block fallThrough, Int32Value intValue) { + if (!intValue.allBitsValid()) + return null; + + int index = intValue.value; + if (targets == null || index < 0 || index >= targets.Count) + return fallThrough; + else + return targets[index]; + } + } +} diff --git a/blocks/cflow/InstructionEmulator.cs b/blocks/cflow/InstructionEmulator.cs index bf114aef..47bd0de3 100644 --- a/blocks/cflow/InstructionEmulator.cs +++ b/blocks/cflow/InstructionEmulator.cs @@ -102,11 +102,11 @@ namespace de4dot.blocks.cflow { return new UnknownValue(); } - Value getArg(int i) { + public Value getArg(int i) { return getValue(args, i); } - Value getArg(ParameterDefinition arg) { + public Value getArg(ParameterDefinition arg) { return getArg(arg.Index); } @@ -121,11 +121,11 @@ namespace de4dot.blocks.cflow { return new UnknownValue(); } - Value getLocal(int i) { + public Value getLocal(int i) { return getValue(locals, i); } - Value getLocal(VariableDefinition local) { + public Value getLocal(VariableDefinition local) { return getLocal(local.Index); } @@ -144,6 +144,11 @@ namespace de4dot.blocks.cflow { return valueStack.pop(); } + public void emulate(IEnumerable instructions) { + foreach (var instr in instructions) + emulate(instr.Instruction); + } + public void emulate(Instruction instr) { switch (instr.OpCode.Code) { case Code.Starg: @@ -482,11 +487,11 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Add((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Add((Int64Value)val1, (Int64Value)val2)); - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) + else if (val1.isReal8() && val2.isReal8()) valueStack.push(Real8Value.Add((Real8Value)val1, (Real8Value)val2)); else valueStack.pushUnknown(); @@ -496,11 +501,11 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Sub((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Sub((Int64Value)val1, (Int64Value)val2)); - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) + else if (val1.isReal8() && val2.isReal8()) valueStack.push(Real8Value.Sub((Real8Value)val1, (Real8Value)val2)); else valueStack.pushUnknown(); @@ -510,11 +515,11 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Mul((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Mul((Int64Value)val1, (Int64Value)val2)); - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) + else if (val1.isReal8() && val2.isReal8()) valueStack.push(Real8Value.Mul((Real8Value)val1, (Real8Value)val2)); else valueStack.pushUnknown(); @@ -524,11 +529,11 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Div((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Div((Int64Value)val1, (Int64Value)val2)); - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) + else if (val1.isReal8() && val2.isReal8()) valueStack.push(Real8Value.Div((Real8Value)val1, (Real8Value)val2)); else valueStack.pushUnknown(); @@ -538,9 +543,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Div_Un((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Div_Un((Int64Value)val1, (Int64Value)val2)); else valueStack.pushUnknown(); @@ -550,11 +555,11 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Rem((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Rem((Int64Value)val1, (Int64Value)val2)); - else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) + else if (val1.isReal8() && val2.isReal8()) valueStack.push(Real8Value.Rem((Real8Value)val1, (Real8Value)val2)); else valueStack.pushUnknown(); @@ -564,9 +569,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Rem_Un((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Rem_Un((Int64Value)val1, (Int64Value)val2)); else valueStack.pushUnknown(); @@ -575,11 +580,11 @@ namespace de4dot.blocks.cflow { void emulate_Neg(Instruction instr) { var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32) + if (val1.isInt32()) valueStack.push(Int32Value.Neg((Int32Value)val1)); - else if (val1.valueType == ValueType.Int64) + else if (val1.isInt64()) valueStack.push(Int64Value.Neg((Int64Value)val1)); - else if (val1.valueType == ValueType.Real8) + else if (val1.isReal8()) valueStack.push(Real8Value.Neg((Real8Value)val1)); else valueStack.pushUnknown(); @@ -589,9 +594,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.And((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.And((Int64Value)val1, (Int64Value)val2)); else valueStack.pushUnknown(); @@ -601,9 +606,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Or((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Or((Int64Value)val1, (Int64Value)val2)); else valueStack.pushUnknown(); @@ -613,9 +618,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Xor((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Xor((Int64Value)val1, (Int64Value)val2)); else valueStack.pushUnknown(); @@ -624,9 +629,9 @@ namespace de4dot.blocks.cflow { void emulate_Not(Instruction instr) { var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32) + if (val1.isInt32()) valueStack.push(Int32Value.Not((Int32Value)val1)); - else if (val1.valueType == ValueType.Int64) + else if (val1.isInt64()) valueStack.push(Int64Value.Not((Int64Value)val1)); else valueStack.pushUnknown(); @@ -636,9 +641,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Shl((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int32) + else if (val1.isInt64() && val2.isInt32()) valueStack.push(Int64Value.Shl((Int64Value)val1, (Int32Value)val2)); else valueStack.pushUnknown(); @@ -648,9 +653,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Shr((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int32) + else if (val1.isInt64() && val2.isInt32()) valueStack.push(Int64Value.Shr((Int64Value)val1, (Int32Value)val2)); else valueStack.pushUnknown(); @@ -660,9 +665,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Shr_Un((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int32) + else if (val1.isInt64() && val2.isInt32()) valueStack.push(Int64Value.Shr_Un((Int64Value)val1, (Int32Value)val2)); else valueStack.pushUnknown(); @@ -672,11 +677,11 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Ceq((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Ceq((Int64Value)val1, (Int64Value)val2)); - else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) + else if (val1.isNull() && val2.isNull()) valueStack.push(new Int32Value(1)); else valueStack.push(Int32Value.createUnknownBool()); @@ -686,9 +691,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Cgt((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Cgt((Int64Value)val1, (Int64Value)val2)); else valueStack.push(Int32Value.createUnknownBool()); @@ -698,9 +703,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Cgt_Un((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Cgt_Un((Int64Value)val1, (Int64Value)val2)); else valueStack.push(Int32Value.createUnknownBool()); @@ -710,9 +715,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Clt((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Clt((Int64Value)val1, (Int64Value)val2)); else valueStack.push(Int32Value.createUnknownBool()); @@ -722,9 +727,9 @@ namespace de4dot.blocks.cflow { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.Clt_Un((Int32Value)val1, (Int32Value)val2)); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.Clt_Un((Int64Value)val1, (Int64Value)val2)); else valueStack.push(Int32Value.createUnknownBool()); @@ -732,7 +737,7 @@ namespace de4dot.blocks.cflow { void emulate_Unbox_Any(Instruction instr) { var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Boxed) + if (val1.isBoxed()) valueStack.push(((BoxedValue)val1).value); else valueStack.pushUnknown(); @@ -781,7 +786,7 @@ namespace de4dot.blocks.cflow { void emulate_Castclass(Instruction instr) { var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Null) + if (val1.isNull()) valueStack.push(val1); else valueStack.pushUnknown(); @@ -790,7 +795,7 @@ namespace de4dot.blocks.cflow { void emulate_Isinst(Instruction instr) { var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Null) + if (val1.isNull()) valueStack.push(val1); else valueStack.pushUnknown(); @@ -815,9 +820,9 @@ namespace de4dot.blocks.cflow { void emulateIntOps2() { var val2 = valueStack.pop(); var val1 = valueStack.pop(); - if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) + if (val1.isInt32() && val2.isInt32()) valueStack.push(Int32Value.createUnknown()); - else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) + else if (val1.isInt64() && val2.isInt64()) valueStack.push(Int64Value.createUnknown()); else valueStack.pushUnknown(); diff --git a/blocks/cflow/SwitchCflowDeobfuscator.cs b/blocks/cflow/SwitchCflowDeobfuscator.cs new file mode 100644 index 00000000..40650fb6 --- /dev/null +++ b/blocks/cflow/SwitchCflowDeobfuscator.cs @@ -0,0 +1,114 @@ +/* + Copyright (C) 2011 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.Cil; + +namespace de4dot.blocks.cflow { + class SwitchCflowDeobfuscator { + List allBlocks; + Blocks blocks; + InstructionEmulator instructionEmulator = new InstructionEmulator(); + + public void init(Blocks blocks, List allBlocks) { + this.blocks = blocks; + this.allBlocks = allBlocks; + } + + public bool deobfuscate() { + bool changed = false; + foreach (var switchBlock in allBlocks) { + if (switchBlock.LastInstr.OpCode.Code != Code.Switch) + continue; + if (isSwitchTopOfStack(switchBlock)) + changed |= topOfstackDeobfuscate(switchBlock); + else if (isSwitchLocal(switchBlock)) + changed |= localDeobfuscate(switchBlock); + } + return changed; + } + + bool isSwitchTopOfStack(Block switchBlock) { + return switchBlock.Instructions.Count == 1; + } + + bool topOfstackDeobfuscate(Block switchBlock) { + bool changed = false; + if (switchBlock.Targets == null) + return changed; + var targets = new List(switchBlock.Targets); + foreach (var source in new List(switchBlock.Sources)) { + if (!isBranchBlock(source)) + continue; + instructionEmulator.init(false, blocks.Method.Parameters, blocks.Locals); + instructionEmulator.emulate(source.Instructions); + + var target = getSwitchTarget(targets, switchBlock.FallThrough, source, instructionEmulator.pop()); + if (target == null) + continue; + source.replaceLastNonBranchWithBranch(0, target); + source.add(new Instr(Instruction.Create(OpCodes.Pop))); + changed = true; + } + return changed; + } + + bool isSwitchLocal(Block switchBlock) { + return switchBlock.Instructions.Count == 2 && switchBlock.Instructions[0].isLdloc(); + } + + bool localDeobfuscate(Block switchBlock) { + bool changed = false; + + var switchVariable = Instr.getLocalVar(blocks.Locals, switchBlock.Instructions[0]); + if (switchVariable == null) + return changed; + + if (switchBlock.Targets == null) + return changed; + var targets = new List(switchBlock.Targets); + foreach (var source in new List(switchBlock.Sources)) { + if (!isBranchBlock(source)) + continue; + instructionEmulator.init(false, blocks.Method.Parameters, blocks.Locals); + instructionEmulator.emulate(source.Instructions); + + var target = getSwitchTarget(targets, switchBlock.FallThrough, source, instructionEmulator.getLocal(switchVariable)); + if (target == null) + continue; + source.replaceLastNonBranchWithBranch(0, target); + changed = true; + } + + return changed; + } + + bool isBranchBlock(Block block) { + if (block.FallThrough != null) + return block.Targets == null || block.Targets.Count == 0; + return block.Targets != null && block.Targets.Count == 1; + } + + Block getSwitchTarget(IList targets, Block fallThrough, Block source, Value value) { + if (!value.isInt32()) + return null; + return CflowUtils.getSwitchTarget(targets, fallThrough, (Int32Value)value); + } + } +} diff --git a/blocks/cflow/Value.cs b/blocks/cflow/Value.cs index 8bc992d9..e68ef552 100644 --- a/blocks/cflow/Value.cs +++ b/blocks/cflow/Value.cs @@ -37,6 +37,34 @@ namespace de4dot.blocks.cflow { abstract class Value { public readonly ValueType valueType; + public bool isUnknown() { + return valueType == ValueType.Unknown; + } + + public bool isNull() { + return valueType == ValueType.Null; + } + + public bool isBoxed() { + return valueType == ValueType.Boxed; + } + + public bool isInt32() { + return valueType == ValueType.Int32; + } + + public bool isInt64() { + return valueType == ValueType.Int64; + } + + public bool isReal8() { + return valueType == ValueType.Real8; + } + + public bool isString() { + return valueType == ValueType.String; + } + protected Value(ValueType valueType) { this.valueType = valueType; } diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index 521c6bf2..26c75c80 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -442,7 +442,7 @@ namespace de4dot { Log.v("Deobfuscating methods"); var methodPrinter = new MethodPrinter(); - var cflowObfuscator = new BlocksControlFlowDeobfuscator(); + var cflowObfuscator = new BlocksCflowDeobfuscator(); foreach (var method in allMethods) { Log.v("Deobfuscating {0} ({1:X8})", method, method.MetadataToken.ToUInt32()); Log.indent();