Add switch cflow deobfuscator
This commit is contained in:
parent
f374308d1e
commit
80acf1d59f
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
35
blocks/cflow/CflowUtils.cs
Normal file
35
blocks/cflow/CflowUtils.cs
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
114
blocks/cflow/SwitchCflowDeobfuscator.cs
Normal file
114
blocks/cflow/SwitchCflowDeobfuscator.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue
Block a user