Add switch cflow deobfuscator

This commit is contained in:
de4dot 2011-10-18 23:31:50 +02:00
parent f374308d1e
commit 80acf1d59f
10 changed files with 326 additions and 123 deletions

View File

@ -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() {

View File

@ -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

View File

@ -35,16 +35,18 @@
<Compile Include="Block.cs" />
<Compile Include="Blocks.cs" />
<Compile Include="BlocksSorter.cs" />
<Compile Include="cflow\BlockControlFlowDeobfuscator.cs" />
<Compile Include="cflow\BlockCflowDeobfuscator.cs" />
<Compile Include="cflow\CflowUtils.cs" />
<Compile Include="cflow\InstructionEmulator.cs" />
<Compile Include="cflow\Int32Value.cs" />
<Compile Include="cflow\Int64Value.cs" />
<Compile Include="cflow\Real8Value.cs" />
<Compile Include="cflow\SwitchCflowDeobfuscator.cs" />
<Compile Include="cflow\Value.cs" />
<Compile Include="cflow\ValueStack.cs" />
<Compile Include="CodeGenerator.cs" />
<Compile Include="CondBranchDeobfuscator.cs" />
<Compile Include="cflow\BlocksControlFlowDeobfuscator.cs" />
<Compile Include="cflow\BlocksCflowDeobfuscator.cs" />
<Compile Include="DeadBlocksRemover.cs" />
<Compile Include="DotNetUtils.cs" />
<Compile Include="FilterHandlerBlock.cs" />

View File

@ -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;
}
}

View File

@ -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<Block>();
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<ScopeBlock> getAllScopeBlocks(ScopeBlock scopeBlock) {

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
namespace de4dot.blocks.cflow {
static class CflowUtils {
public static Block getSwitchTarget(IList<Block> 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];
}
}
}

View File

@ -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<Instr> 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();

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using Mono.Cecil.Cil;
namespace de4dot.blocks.cflow {
class SwitchCflowDeobfuscator {
List<Block> allBlocks;
Blocks blocks;
InstructionEmulator instructionEmulator = new InstructionEmulator();
public void init(Blocks blocks, List<Block> 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<Block>(switchBlock.Targets);
foreach (var source in new List<Block>(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<Block>(switchBlock.Targets);
foreach (var source in new List<Block>(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<Block> targets, Block fallThrough, Block source, Value value) {
if (!value.isInt32())
return null;
return CflowUtils.getSwitchTarget(targets, fallThrough, (Int32Value)value);
}
}
}

View File

@ -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;
}

View File

@ -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();