Start work on new cflow deobfuscator

This commit is contained in:
de4dot 2011-10-17 00:22:22 +02:00
parent b6a994700c
commit 05065d6ac7
13 changed files with 2399 additions and 148 deletions

View File

@ -168,6 +168,11 @@ namespace de4dot.blocks {
replaceLastInstrsWithBranch(numInstrs, target);
}
public void replaceBccWithBranch(bool isTaken) {
Block target = isTaken ? targets[0] : fallThrough;
replaceLastInstrsWithBranch(1, target);
}
public void removeDeadBlock() {
if (sources.Count != 0)
throw new ApplicationException("Trying to remove a non-dead block");

View File

@ -74,131 +74,10 @@ namespace de4dot.blocks {
return list;
}
int removeDeadBlocks() {
public int removeDeadBlocks() {
return new DeadBlocksRemover(methodBlocks).remove();
}
class DeadBlocksRemover {
MethodBlocks methodBlocks;
Dictionary<BaseBlock, bool> checkedBaseBlocks = new Dictionary<BaseBlock, bool>();
Dictionary<ScopeBlock, bool> checkedScopeBlocks = new Dictionary<ScopeBlock, bool>();
Stack<BaseBlock> baseBlocksToCheck = new Stack<BaseBlock>();
Stack<ScopeBlock> scopeBlocksToCheck = new Stack<ScopeBlock>();
public DeadBlocksRemover(MethodBlocks methodBlocks) {
this.methodBlocks = methodBlocks;
}
public int remove() {
addScopeBlock(methodBlocks);
processAll();
return removeDeadBlocks();
}
class ScopeBlockInfo {
public ScopeBlock scopeBlock;
public IList<BaseBlock> deadBlocks = new List<BaseBlock>();
public ScopeBlockInfo(ScopeBlock scopeBlock) {
this.scopeBlock = scopeBlock;
}
}
int removeDeadBlocks() {
int numDeadBlocks = 0;
var infos = new Dictionary<ScopeBlock, ScopeBlockInfo>();
var deadBlocksDict = new Dictionary<BaseBlock, bool>();
foreach (var baseBlock in findDeadBlocks()) {
deadBlocksDict[baseBlock] = true;
ScopeBlock parent = (ScopeBlock)baseBlock.Parent;
ScopeBlockInfo info;
if (!infos.TryGetValue(parent, out info))
infos[parent] = info = new ScopeBlockInfo(parent);
info.deadBlocks.Add(baseBlock);
numDeadBlocks++;
}
foreach (var info in infos.Values)
info.scopeBlock.removeAllDeadBlocks(info.deadBlocks, deadBlocksDict);
return numDeadBlocks;
}
IList<BaseBlock> findDeadBlocks() {
var deadBlocks = new List<BaseBlock>();
foreach (var bb in methodBlocks.getAllBaseBlocks()) {
if (!checkedBaseBlocks.ContainsKey(bb))
deadBlocks.Add(bb);
}
return deadBlocks;
}
void addScopeBlock(ScopeBlock scopeBlock) {
scopeBlocksToCheck.Push(scopeBlock);
}
void processAll() {
bool didSomething;
do {
didSomething = false;
while (baseBlocksToCheck.Count > 0) {
processBaseBlock(baseBlocksToCheck.Pop());
didSomething = true;
}
while (scopeBlocksToCheck.Count > 0) {
processScopeBlock(scopeBlocksToCheck.Pop());
didSomething = true;
}
} while (didSomething);
}
void processBaseBlock(BaseBlock baseBlock) {
if (baseBlock == null || checkedBaseBlocks.ContainsKey(baseBlock))
return;
checkedBaseBlocks[baseBlock] = true;
if (baseBlock is Block) {
var block = (Block)baseBlock;
foreach (var block2 in block.getTargets())
addBaseBlock(block2);
}
else if (baseBlock is ScopeBlock) {
var scopeBlock = (ScopeBlock)baseBlock;
addScopeBlock(scopeBlock);
if (scopeBlock.BaseBlocks != null && scopeBlock.BaseBlocks.Count > 0)
addBaseBlock(scopeBlock.BaseBlocks[0]);
}
else
throw new ApplicationException(string.Format("Unknown BaseBlock type {0}", baseBlock.GetType()));
}
// Add a block to be processed later, including all its enclosing ScopeBlocks.
void addBaseBlock(BaseBlock baseBlock) {
for (BaseBlock bb = baseBlock; bb != null; bb = bb.Parent)
baseBlocksToCheck.Push(bb);
}
void processScopeBlock(ScopeBlock scopeBlock) {
if (scopeBlock == null || checkedScopeBlocks.ContainsKey(scopeBlock))
return;
checkedScopeBlocks[scopeBlock] = true;
addBaseBlock(scopeBlock);
if (scopeBlock is TryBlock) {
var tryBlock = (TryBlock)scopeBlock;
foreach (var handler in tryBlock.TryHandlerBlocks)
addScopeBlock(handler);
}
else if (scopeBlock is TryHandlerBlock) {
var tryHandlerBlock = (TryHandlerBlock)scopeBlock;
addScopeBlock(tryHandlerBlock.FilterHandlerBlock);
addScopeBlock(tryHandlerBlock.HandlerBlock);
}
}
}
public void getCode(out IList<Instruction> allInstructions, out IList<ExceptionHandler> allExceptionHandlers) {
new CodeGenerator(methodBlocks).getCode(out allInstructions, out allExceptionHandlers);
}

144
blocks/DeadBlocksRemover.cs Normal file
View File

@ -0,0 +1,144 @@
/*
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;
using System.Collections.Generic;
namespace de4dot.blocks {
class DeadBlocksRemover {
MethodBlocks methodBlocks;
Dictionary<BaseBlock, bool> checkedBaseBlocks = new Dictionary<BaseBlock, bool>();
Dictionary<ScopeBlock, bool> checkedScopeBlocks = new Dictionary<ScopeBlock, bool>();
Stack<BaseBlock> baseBlocksToCheck = new Stack<BaseBlock>();
Stack<ScopeBlock> scopeBlocksToCheck = new Stack<ScopeBlock>();
public DeadBlocksRemover(MethodBlocks methodBlocks) {
this.methodBlocks = methodBlocks;
}
public int remove() {
addScopeBlock(methodBlocks);
processAll();
return removeDeadBlocks();
}
class ScopeBlockInfo {
public ScopeBlock scopeBlock;
public IList<BaseBlock> deadBlocks = new List<BaseBlock>();
public ScopeBlockInfo(ScopeBlock scopeBlock) {
this.scopeBlock = scopeBlock;
}
}
int removeDeadBlocks() {
int numDeadBlocks = 0;
var infos = new Dictionary<ScopeBlock, ScopeBlockInfo>();
var deadBlocksDict = new Dictionary<BaseBlock, bool>();
foreach (var baseBlock in findDeadBlocks()) {
deadBlocksDict[baseBlock] = true;
ScopeBlock parent = (ScopeBlock)baseBlock.Parent;
ScopeBlockInfo info;
if (!infos.TryGetValue(parent, out info))
infos[parent] = info = new ScopeBlockInfo(parent);
info.deadBlocks.Add(baseBlock);
numDeadBlocks++;
}
foreach (var info in infos.Values)
info.scopeBlock.removeAllDeadBlocks(info.deadBlocks, deadBlocksDict);
return numDeadBlocks;
}
IList<BaseBlock> findDeadBlocks() {
var deadBlocks = new List<BaseBlock>();
foreach (var bb in methodBlocks.getAllBaseBlocks()) {
if (!checkedBaseBlocks.ContainsKey(bb))
deadBlocks.Add(bb);
}
return deadBlocks;
}
void addScopeBlock(ScopeBlock scopeBlock) {
scopeBlocksToCheck.Push(scopeBlock);
}
void processAll() {
bool didSomething;
do {
didSomething = false;
while (baseBlocksToCheck.Count > 0) {
processBaseBlock(baseBlocksToCheck.Pop());
didSomething = true;
}
while (scopeBlocksToCheck.Count > 0) {
processScopeBlock(scopeBlocksToCheck.Pop());
didSomething = true;
}
} while (didSomething);
}
void processBaseBlock(BaseBlock baseBlock) {
if (baseBlock == null || checkedBaseBlocks.ContainsKey(baseBlock))
return;
checkedBaseBlocks[baseBlock] = true;
if (baseBlock is Block) {
var block = (Block)baseBlock;
foreach (var block2 in block.getTargets())
addBaseBlock(block2);
}
else if (baseBlock is ScopeBlock) {
var scopeBlock = (ScopeBlock)baseBlock;
addScopeBlock(scopeBlock);
if (scopeBlock.BaseBlocks != null && scopeBlock.BaseBlocks.Count > 0)
addBaseBlock(scopeBlock.BaseBlocks[0]);
}
else
throw new ApplicationException(string.Format("Unknown BaseBlock type {0}", baseBlock.GetType()));
}
// Add a block to be processed later, including all its enclosing ScopeBlocks.
void addBaseBlock(BaseBlock baseBlock) {
for (BaseBlock bb = baseBlock; bb != null; bb = bb.Parent)
baseBlocksToCheck.Push(bb);
}
void processScopeBlock(ScopeBlock scopeBlock) {
if (scopeBlock == null || checkedScopeBlocks.ContainsKey(scopeBlock))
return;
checkedScopeBlocks[scopeBlock] = true;
addBaseBlock(scopeBlock);
if (scopeBlock is TryBlock) {
var tryBlock = (TryBlock)scopeBlock;
foreach (var handler in tryBlock.TryHandlerBlocks)
addScopeBlock(handler);
}
else if (scopeBlock is TryHandlerBlock) {
var tryHandlerBlock = (TryHandlerBlock)scopeBlock;
addScopeBlock(tryHandlerBlock.FilterHandlerBlock);
addScopeBlock(tryHandlerBlock.HandlerBlock);
}
}
}
}

View File

@ -483,26 +483,44 @@ namespace de4dot.blocks {
return !MemberReferenceHelper.verifyType(method.MethodReturnType.ReturnType, "mscorlib", "System.Void");
}
public static void updateStack(Instruction instr, ref int stack) {
if (instr.OpCode.FlowControl == FlowControl.Call) {
var method = (IMethodSignature)instr.Operand;
if (hasReturnValue(method))
stack++;
if (method.HasThis && instr.OpCode.Code == Code.Newobj)
stack++;
if (method.HasParameters)
stack -= method.Parameters.Count;
if (method.HasThis && instr.OpCode.Code != Code.Newobj)
stack--;
}
public static void updateStack(Instruction instr, ref int stack, bool methodHasReturnValue) {
int pushes, pops;
calculateStackUsage(instr, methodHasReturnValue, out pushes, out pops);
if (pops == -1)
stack = 0;
else
updateStack_nonCall(instr, ref stack);
stack += pushes - pops;
}
static void updateStack_nonCall(Instruction instr, ref int stack) {
// Sets pops to -1 if the stack is supposed to be cleared
public static void calculateStackUsage(Instruction instr, bool methodHasReturnValue, out int pushes, out int pops) {
if (instr.OpCode.FlowControl == FlowControl.Call)
calculateStackUsage_call(instr, out pushes, out pops);
else
calculateStackUsage_nonCall(instr, methodHasReturnValue, out pushes, out pops);
}
static void calculateStackUsage_call(Instruction instr, out int pushes, out int pops) {
pushes = 0;
pops = 0;
var method = (IMethodSignature)instr.Operand;
if (hasReturnValue(method) || (instr.OpCode.Code == Code.Newobj && method.HasThis))
pushes++;
if (method.HasParameters)
pops += method.Parameters.Count;
if (method.HasThis && instr.OpCode.Code != Code.Newobj)
pops++;
}
// Sets pops to -1 if the stack is supposed to be cleared
static void calculateStackUsage_nonCall(Instruction instr, bool methodHasReturnValue, out int pushes, out int pops) {
StackBehaviour stackBehavior;
pushes = 0;
pops = 0;
stackBehavior = instr.OpCode.StackBehaviourPush;
switch (stackBehavior) {
case StackBehaviour.Push0:
@ -514,11 +532,11 @@ namespace de4dot.blocks {
case StackBehaviour.Pushr4:
case StackBehaviour.Pushr8:
case StackBehaviour.Pushref:
stack++;
pushes++;
break;
case StackBehaviour.Push1_push1:
stack += 2;
pushes += 2;
break;
case StackBehaviour.Varpush: // only call, calli, callvirt which are handled elsewhere
@ -534,7 +552,7 @@ namespace de4dot.blocks {
case StackBehaviour.Pop1:
case StackBehaviour.Popi:
case StackBehaviour.Popref:
stack--;
pops++;
break;
case StackBehaviour.Pop1_pop1:
@ -545,7 +563,7 @@ namespace de4dot.blocks {
case StackBehaviour.Popi_popr8:
case StackBehaviour.Popref_pop1:
case StackBehaviour.Popref_popi:
stack -= 2;
pops += 2;
break;
case StackBehaviour.Popi_popi_popi:
@ -554,16 +572,16 @@ namespace de4dot.blocks {
case StackBehaviour.Popref_popi_popr4:
case StackBehaviour.Popref_popi_popr8:
case StackBehaviour.Popref_popi_popref:
stack -= 3;
pops += 3;
break;
case StackBehaviour.PopAll:
stack = 0;
pops = -1;
break;
case StackBehaviour.Varpop: // call, calli, callvirt, newobj (all handled elsewhere), and ret
// It's RET. Ignore decrementing stack once if method returns something
// since it's not important and we don't know whether it returns anything.
if (methodHasReturnValue)
pops++;
break;
default:
@ -583,8 +601,14 @@ namespace de4dot.blocks {
}
public static string getFullAssemblyName(IMetadataScope scope) {
//TODO: Returning scope.Name is probably best since the method could fail.
var asmRef = getAssemblyNameReference(scope);
return asmRef.FullName;
}
public static bool isAssembly(IMetadataScope scope, string assemblySimpleName) {
return scope.Name == assemblySimpleName ||
scope.Name.StartsWith(assemblySimpleName + ",", StringComparison.Ordinal);
}
}
}

View File

@ -52,7 +52,7 @@ namespace de4dot.blocks {
int stack = stackStart;
foreach (var instr in block.Instructions)
DotNetUtils.updateStack(instr.Instruction, ref stack);
DotNetUtils.updateStack(instr.Instruction, ref stack, false);
stackEnd = stack;
}
}

View File

@ -35,8 +35,14 @@
<Compile Include="Block.cs" />
<Compile Include="Blocks.cs" />
<Compile Include="BlocksSorter.cs" />
<Compile Include="cflow\BlockControlFlowDeobfuscator.cs" />
<Compile Include="cflow\InstructionEmulator.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="DeadBlocksRemover.cs" />
<Compile Include="DotNetUtils.cs" />
<Compile Include="FilterHandlerBlock.cs" />
<Compile Include="ForwardScanOrder.cs" />

View File

@ -0,0 +1,564 @@
/*
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;
using Mono.Cecil.Cil;
namespace de4dot.blocks.cflow {
class BlockControlFlowDeobfuscator {
Block block;
InstructionEmulator instructionEmulator = new InstructionEmulator();
public void init(Block block, IList<ParameterDefinition> args, IList<VariableDefinition> locals) {
this.block = block;
instructionEmulator.init(false, args, locals);
}
// Returns true if code was updated, false otherwise
public bool deobfuscate() {
var instructions = block.Instructions;
if (instructions.Count == 0)
return false;
for (int i = 0; i < instructions.Count - 1; i++) {
instructionEmulator.emulate(instructions[i].Instruction);
}
switch (block.LastInstr.OpCode.Code) {
case Code.Beq:
case Code.Beq_S: return emulate_Beq();
case Code.Bge:
case Code.Bge_S: return emulate_Bge();
case Code.Bge_Un:
case Code.Bge_Un_S: return emulate_Bge_Un();
case Code.Bgt:
case Code.Bgt_S: return emulate_Bgt();
case Code.Bgt_Un:
case Code.Bgt_Un_S: return emulate_Bgt_Un();
case Code.Ble:
case Code.Ble_S: return emulate_Ble();
case Code.Ble_Un:
case Code.Ble_Un_S: return emulate_Ble_Un();
case Code.Blt:
case Code.Blt_S: return emulate_Blt();
case Code.Blt_Un:
case Code.Blt_Un_S: return emulate_Blt_Un();
case Code.Bne_Un:
case Code.Bne_Un_S: return emulate_Bne_Un();
case Code.Brfalse:
case Code.Brfalse_S:return emulate_Brfalse();
case Code.Brtrue:
case Code.Brtrue_S: return emulate_Brtrue();
default:
return false;
}
}
bool emulateBranch(int stackArgs, bool isTaken) {
// Pop the arguments to the bcc instruction. The dead code remover will get rid of the
// pop and any pushed arguments. Insert the pops just before the bcc instr.
for (int i = 0; i < stackArgs; i++)
block.insert(block.Instructions.Count - 1, Instruction.Create(OpCodes.Pop));
block.replaceBccWithBranch(isTaken);
return true;
}
bool emulate_Beq() {
var val2 = instructionEmulator.pop();
var val1 = instructionEmulator.pop();
//TODO: If it's an unknown int32/64, push 1 if val1 is same ref as val2
if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
var int2 = (Int32Value)val2;
return emulateBranch(2, int1.value == int2.value);
}
else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
var long2 = (Int64Value)val2;
return emulateBranch(2, long1.value == long2.value);
}
else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) {
var real1 = (Real8Value)val1;
var real2 = (Real8Value)val2;
return emulateBranch(2, real1.value == real2.value);
}
else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) {
return emulateBranch(2, true);
}
else {
return false;
}
}
bool emulate_Bne_Un() {
var val2 = instructionEmulator.pop();
var val1 = instructionEmulator.pop();
//TODO: If it's an unknown int32/64, push 1 if val1 is same ref as val2
if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
var int2 = (Int32Value)val2;
return emulateBranch(2, (uint)int1.value != (uint)int2.value);
}
else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
var long2 = (Int64Value)val2;
return emulateBranch(2, (ulong)long1.value != (ulong)long2.value);
}
else if (val1.valueType == ValueType.Real8 && val2.valueType == ValueType.Real8) {
var real1 = (Real8Value)val1;
var real2 = (Real8Value)val2;
return emulateBranch(2, real1.value != real2.value);
}
else if (val1.valueType == ValueType.Null && val2.valueType == ValueType.Null) {
return emulateBranch(2, false);
}
else {
return false;
}
}
bool emulate_Bge() {
var val2 = instructionEmulator.pop();
var val1 = instructionEmulator.pop();
//TODO: Support floats
if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
var int2 = (Int32Value)val2;
return emulateBranch(2, int1.value >= int2.value);
}
else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
var long2 = (Int64Value)val2;
return emulateBranch(2, long1.value >= long2.value);
}
else if (val1.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
if (int1.value == int.MaxValue)
return emulateBranch(2, true); // max >= x => true
else
return false;
}
else if (val2.valueType == ValueType.Int32) {
var int2 = (Int32Value)val2;
if (int2.value == int.MinValue)
return emulateBranch(2, true); // x >= min => true
else
return false;
}
else if (val1.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
if (long1.value == long.MaxValue)
return emulateBranch(2, true); // max >= x => true
else
return false;
}
else if (val2.valueType == ValueType.Int64) {
var long2 = (Int64Value)val2;
if (long2.value == long.MinValue)
return emulateBranch(2, true); // x >= min => true
else
return false;
}
else {
return false;
}
}
bool emulate_Bge_Un() {
var val2 = instructionEmulator.pop();
var val1 = instructionEmulator.pop();
//TODO: Support floats
if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
var int2 = (Int32Value)val2;
return emulateBranch(2, (uint)int1.value >= (uint)int2.value);
}
else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
var long2 = (Int64Value)val2;
return emulateBranch(2, (ulong)long1.value >= (ulong)long2.value);
}
else if (val1.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
if ((uint)int1.value == uint.MaxValue)
return emulateBranch(2, true); // max >= x => true
else
return false;
}
else if (val2.valueType == ValueType.Int32) {
var int2 = (Int32Value)val2;
if ((uint)int2.value == uint.MinValue)
return emulateBranch(2, true); // x >= min => true
else
return false;
}
else if (val1.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
if ((ulong)long1.value == ulong.MaxValue)
return emulateBranch(2, true); // max >= x => true
else
return false;
}
else if (val2.valueType == ValueType.Int64) {
var long2 = (Int64Value)val2;
if ((ulong)long2.value == ulong.MinValue)
return emulateBranch(2, true); // x >= min => true
else
return false;
}
else {
return false;
}
}
bool emulate_Bgt() {
var val2 = instructionEmulator.pop();
var val1 = instructionEmulator.pop();
//TODO: Support floats
if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
var int2 = (Int32Value)val2;
return emulateBranch(2, int1.value > int2.value);
}
else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
var long2 = (Int64Value)val2;
return emulateBranch(2, long1.value > long2.value);
}
else if (val1.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
if (int1.value == int.MinValue)
return emulateBranch(2, false); // min > x => false
else
return false;
}
else if (val2.valueType == ValueType.Int32) {
var int2 = (Int32Value)val2;
if (int2.value == int.MaxValue)
return emulateBranch(2, false); // x > max => false
else
return false;
}
else if (val1.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
if (long1.value == long.MinValue)
return emulateBranch(2, false); // min > x => false
else
return false;
}
else if (val2.valueType == ValueType.Int64) {
var long2 = (Int64Value)val2;
if (long2.value == long.MaxValue)
return emulateBranch(2, false); // x > max => false
else
return false;
}
else {
return false;
}
}
bool emulate_Bgt_Un() {
var val2 = instructionEmulator.pop();
var val1 = instructionEmulator.pop();
//TODO: Support floats
if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
var int2 = (Int32Value)val2;
return emulateBranch(2, (uint)int1.value > (uint)int2.value);
}
else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
var long2 = (Int64Value)val2;
return emulateBranch(2, (ulong)long1.value > (ulong)long2.value);
}
else if (val1.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
if ((uint)int1.value == uint.MinValue)
return emulateBranch(2, false); // min > x => false
else
return false;
}
else if (val2.valueType == ValueType.Int32) {
var int2 = (Int32Value)val2;
if ((uint)int2.value == uint.MaxValue)
return emulateBranch(2, false); // x > max => false
else
return false;
}
else if (val1.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
if ((ulong)long1.value == ulong.MinValue)
return emulateBranch(2, false); // min > x => false
else
return false;
}
else if (val2.valueType == ValueType.Int64) {
var long2 = (Int64Value)val2;
if ((ulong)long2.value == ulong.MaxValue)
return emulateBranch(2, false); // x > max => false
else
return false;
}
else {
return false;
}
}
bool emulate_Ble() {
var val2 = instructionEmulator.pop();
var val1 = instructionEmulator.pop();
//TODO: Support floats
if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
var int2 = (Int32Value)val2;
return emulateBranch(2, int1.value <= int2.value);
}
else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
var long2 = (Int64Value)val2;
return emulateBranch(2, long1.value <= long2.value);
}
else if (val1.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
if (int1.value == int.MinValue)
return emulateBranch(2, true); // min <= x => true
else
return false;
}
else if (val2.valueType == ValueType.Int32) {
var int2 = (Int32Value)val2;
if (int2.value == int.MaxValue)
return emulateBranch(2, true); // x <= max => true
else
return false;
}
else if (val1.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
if (long1.value == long.MinValue)
return emulateBranch(2, true); // min <= x => true
else
return false;
}
else if (val2.valueType == ValueType.Int64) {
var long2 = (Int64Value)val2;
if (long2.value == long.MaxValue)
return emulateBranch(2, true); // x <= max => true
else
return false;
}
else {
return false;
}
}
bool emulate_Ble_Un() {
var val2 = instructionEmulator.pop();
var val1 = instructionEmulator.pop();
//TODO: Support floats
if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
var int2 = (Int32Value)val2;
return emulateBranch(2, (uint)int1.value <= (uint)int2.value);
}
else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
var long2 = (Int64Value)val2;
return emulateBranch(2, (ulong)long1.value <= (ulong)long2.value);
}
else if (val1.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
if ((uint)int1.value == uint.MinValue)
return emulateBranch(2, true); // min <= x => true
else
return false;
}
else if (val2.valueType == ValueType.Int32) {
var int2 = (Int32Value)val2;
if ((uint)int2.value == uint.MaxValue)
return emulateBranch(2, true); // x <= max => true
else
return false;
}
else if (val1.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
if ((ulong)long1.value == ulong.MinValue)
return emulateBranch(2, true); // min <= x => true
else
return false;
}
else if (val2.valueType == ValueType.Int64) {
var long2 = (Int64Value)val2;
if ((ulong)long2.value == ulong.MaxValue)
return emulateBranch(2, true); // x <= max => true
else
return false;
}
else {
return false;
}
}
bool emulate_Blt() {
var val2 = instructionEmulator.pop();
var val1 = instructionEmulator.pop();
//TODO: Support floats
if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
var int2 = (Int32Value)val2;
return emulateBranch(2, int1.value < int2.value);
}
else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
var long2 = (Int64Value)val2;
return emulateBranch(2, long1.value < long2.value);
}
else if (val1.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
if (int1.value == int.MaxValue)
return emulateBranch(2, false); // max < x => false
else
return false;
}
else if (val2.valueType == ValueType.Int32) {
var int2 = (Int32Value)val2;
if (int2.value == int.MinValue)
return emulateBranch(2, false); // x < min => false
else
return false;
}
else if (val1.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
if (long1.value == long.MaxValue)
return emulateBranch(2, false); // max < x => false
else
return false;
}
else if (val2.valueType == ValueType.Int64) {
var long2 = (Int64Value)val2;
if (long2.value == long.MinValue)
return emulateBranch(2, false); // x < min => false
else
return false;
}
else {
return false;
}
}
bool emulate_Blt_Un() {
var val2 = instructionEmulator.pop();
var val1 = instructionEmulator.pop();
//TODO: Support floats
if (val1.valueType == ValueType.Int32 && val2.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
var int2 = (Int32Value)val2;
return emulateBranch(2, (uint)int1.value < (uint)int2.value);
}
else if (val1.valueType == ValueType.Int64 && val2.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
var long2 = (Int64Value)val2;
return emulateBranch(2, (ulong)long1.value < (ulong)long2.value);
}
else if (val1.valueType == ValueType.Int32) {
var int1 = (Int32Value)val1;
if ((uint)int1.value == uint.MaxValue)
return emulateBranch(2, false); // max < x => false
else
return false;
}
else if (val2.valueType == ValueType.Int32) {
var int2 = (Int32Value)val2;
if ((uint)int2.value == uint.MinValue)
return emulateBranch(2, false); // x < min => false
else
return false;
}
else if (val1.valueType == ValueType.Int64) {
var long1 = (Int64Value)val1;
if ((ulong)long1.value == ulong.MaxValue)
return emulateBranch(2, false); // max < x => false
else
return false;
}
else if (val2.valueType == ValueType.Int64) {
var long2 = (Int64Value)val2;
if ((ulong)long2.value == ulong.MinValue)
return emulateBranch(2, false); // x < min => false
else
return false;
}
else {
return false;
}
}
bool emulate_Brfalse() {
var val1 = instructionEmulator.pop();
//TODO: Support floats
if (val1.valueType == ValueType.Int32)
return emulateBranch(1, ((Int32Value)val1).value == 0);
else if (val1.valueType == ValueType.Int64)
return emulateBranch(1, ((Int64Value)val1).value == 0);
else if (val1.valueType == ValueType.Null)
return emulateBranch(1, true);
else
return false;
}
bool emulate_Brtrue() {
var val1 = instructionEmulator.pop();
//TODO: Support floats
if (val1.valueType == ValueType.Int32)
return emulateBranch(1, ((Int32Value)val1).value != 0);
else if (val1.valueType == ValueType.Int64)
return emulateBranch(1, ((Int64Value)val1).value != 0);
else if (val1.valueType == ValueType.Null)
return emulateBranch(1, false);
else
return false;
}
}
}

View File

@ -0,0 +1,67 @@
/*
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 {
public class BlocksControlFlowDeobfuscator {
BlockControlFlowDeobfuscator blockControlFlowDeobfuscator = new BlockControlFlowDeobfuscator();
Blocks blocks;
int numRemovedDeadBlocks;
public int NumberOfRemovedDeadBlocks {
get { return numRemovedDeadBlocks; }
}
public void init(Blocks blocks) {
this.blocks = blocks;
numRemovedDeadBlocks = 0;
}
public void deobfuscate() {
bool changed;
do {
changed = false;
removeDeadBlocks();
mergeBlocks();
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
//TODO: Only do this if it's a bcc block. switch blocks should use other code.
blockControlFlowDeobfuscator.init(block, blocks.Method.Parameters, blocks.Locals);
changed |= blockControlFlowDeobfuscator.deobfuscate();
}
} while (changed);
}
void removeDeadBlocks() {
numRemovedDeadBlocks += new DeadBlocksRemover(blocks.MethodBlocks).remove();
}
void mergeBlocks() {
foreach (var scopeBlock in getAllScopeBlocks(blocks.MethodBlocks))
scopeBlock.mergeBlocks();
}
IEnumerable<ScopeBlock> getAllScopeBlocks(ScopeBlock scopeBlock) {
var list = new List<ScopeBlock>();
list.Add(scopeBlock);
list.AddRange(scopeBlock.getAllScopeBlocks());
return list;
}
}
}

File diff suppressed because it is too large Load Diff

126
blocks/cflow/Value.cs Normal file
View File

@ -0,0 +1,126 @@
/*
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/>.
*/
namespace de4dot.blocks.cflow {
enum ValueType : byte {
Unknown,
Null,
Boxed,
Int32,
Int64,
Real8,
String,
}
abstract class Value {
public readonly ValueType valueType;
protected Value(ValueType valueType) {
this.valueType = valueType;
}
}
class UnknownValue : Value {
public UnknownValue()
: base(ValueType.Unknown) {
}
public override string ToString() {
return "<unknown>";
}
}
class NullValue : Value {
// There's only one type of null
public static readonly NullValue Instance = new NullValue();
NullValue()
: base(ValueType.Null) {
}
public override string ToString() {
return "null";
}
}
class BoxedValue : Value {
public readonly Value value;
public BoxedValue(Value value)
: base(ValueType.Boxed) {
this.value = value;
}
public override string ToString() {
return string.Format("box({0})", value.ToString());
}
}
class Int32Value : Value {
public readonly int value;
public Int32Value(int value)
: base(ValueType.Int32) {
this.value = value;
}
public override string ToString() {
return value.ToString();
}
}
class Int64Value : Value {
public readonly long value;
public Int64Value(long value)
: base(ValueType.Int64) {
this.value = value;
}
public override string ToString() {
return value.ToString() + "L";
}
}
class Real8Value : Value {
public readonly double value;
public Real8Value(double value)
: base(ValueType.Real8) {
this.value = value;
}
public override string ToString() {
return value.ToString() + "D";
}
}
class StringValue : Value {
public readonly string value;
public StringValue(string value)
: base(ValueType.String) {
this.value = value;
}
public override string ToString() {
return string.Format("\"{0}\"", value);
}
}
}

View File

@ -0,0 +1,96 @@
/*
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;
using System.Text;
using System.Collections.Generic;
namespace de4dot.blocks.cflow {
class ValueStack {
List<Value> stack = new List<Value>();
public void init() {
stack.Clear();
}
public void clear() {
stack.Clear();
}
public void push(Value value) {
stack.Add(value);
}
public Value peek() {
if (stack.Count == 0)
return new UnknownValue();
return stack[stack.Count - 1];
}
public Value pop() {
Value value = peek();
if (stack.Count != 0)
stack.RemoveAt(stack.Count - 1);
return value;
}
public void push(int count) {
if (count < 0)
throw new ArgumentOutOfRangeException("count");
for (int i = 0; i < count; i++)
pushUnknown();
}
public void pushUnknown() {
push(new UnknownValue());
}
public void pop(int count) {
if (count < 0)
throw new ArgumentOutOfRangeException("count");
if (count >= stack.Count)
stack.Clear();
else if (count > 0)
stack.RemoveRange(stack.Count - count, count);
}
public void copyTop() {
push(peek());
}
public override string ToString() {
if (stack.Count == 0)
return "<empty>";
var sb = new StringBuilder();
const int maxValues = 5;
for (int i = 0; i < maxValues; i++) {
int index = stack.Count - i - 1;
if (index < 0)
break;
if (i > 0)
sb.Append(", ");
sb.Append(stack[index].ToString());
}
if (maxValues < stack.Count)
sb.Append(", ...");
return sb.ToString();
}
}
}

View File

@ -26,6 +26,7 @@ using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.deobfuscators;
using de4dot.blocks;
using de4dot.blocks.cflow;
using de4dot.AssemblyClient;
namespace de4dot {
@ -441,6 +442,7 @@ namespace de4dot {
Log.v("Deobfuscating methods");
var methodPrinter = new MethodPrinter();
var cflowObfuscator = new BlocksControlFlowDeobfuscator();
foreach (var method in allMethods) {
Log.v("Deobfuscating {0} ({1:X8})", method, method.MetadataToken.ToUInt32());
Log.indent();
@ -450,7 +452,9 @@ namespace de4dot {
deob.deobfuscateMethodBegin(blocks);
if (options.ControlFlowDeobfuscation) {
int numDeadBlocks = blocks.deobfuscate();
cflowObfuscator.init(blocks);
cflowObfuscator.deobfuscate();
int numDeadBlocks = cflowObfuscator.NumberOfRemovedDeadBlocks;
if (numDeadBlocks > 0)
Log.v("Removed {0} dead block(s)", numDeadBlocks);
}

View File

@ -218,7 +218,7 @@ namespace de4dot.deobfuscators {
if (stack <= 0)
return null;
var instr = instrs[i];
DotNetUtils.updateStack(instr.Instruction, ref stack);
DotNetUtils.updateStack(instr.Instruction, ref stack, false);
if (stack < 0)
return null;