Start work on new cflow deobfuscator
This commit is contained in:
parent
b6a994700c
commit
05065d6ac7
|
@ -168,6 +168,11 @@ namespace de4dot.blocks {
|
||||||
replaceLastInstrsWithBranch(numInstrs, target);
|
replaceLastInstrsWithBranch(numInstrs, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void replaceBccWithBranch(bool isTaken) {
|
||||||
|
Block target = isTaken ? targets[0] : fallThrough;
|
||||||
|
replaceLastInstrsWithBranch(1, target);
|
||||||
|
}
|
||||||
|
|
||||||
public void removeDeadBlock() {
|
public void removeDeadBlock() {
|
||||||
if (sources.Count != 0)
|
if (sources.Count != 0)
|
||||||
throw new ApplicationException("Trying to remove a non-dead block");
|
throw new ApplicationException("Trying to remove a non-dead block");
|
||||||
|
|
123
blocks/Blocks.cs
123
blocks/Blocks.cs
|
@ -74,131 +74,10 @@ namespace de4dot.blocks {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
int removeDeadBlocks() {
|
public int removeDeadBlocks() {
|
||||||
return new DeadBlocksRemover(methodBlocks).remove();
|
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) {
|
public void getCode(out IList<Instruction> allInstructions, out IList<ExceptionHandler> allExceptionHandlers) {
|
||||||
new CodeGenerator(methodBlocks).getCode(out allInstructions, out allExceptionHandlers);
|
new CodeGenerator(methodBlocks).getCode(out allInstructions, out allExceptionHandlers);
|
||||||
}
|
}
|
||||||
|
|
144
blocks/DeadBlocksRemover.cs
Normal file
144
blocks/DeadBlocksRemover.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -483,26 +483,44 @@ namespace de4dot.blocks {
|
||||||
return !MemberReferenceHelper.verifyType(method.MethodReturnType.ReturnType, "mscorlib", "System.Void");
|
return !MemberReferenceHelper.verifyType(method.MethodReturnType.ReturnType, "mscorlib", "System.Void");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateStack(Instruction instr, ref int stack) {
|
public static void updateStack(Instruction instr, ref int stack, bool methodHasReturnValue) {
|
||||||
if (instr.OpCode.FlowControl == FlowControl.Call) {
|
int pushes, pops;
|
||||||
|
calculateStackUsage(instr, methodHasReturnValue, out pushes, out pops);
|
||||||
|
if (pops == -1)
|
||||||
|
stack = 0;
|
||||||
|
else
|
||||||
|
stack += pushes - pops;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
var method = (IMethodSignature)instr.Operand;
|
||||||
if (hasReturnValue(method))
|
if (hasReturnValue(method) || (instr.OpCode.Code == Code.Newobj && method.HasThis))
|
||||||
stack++;
|
pushes++;
|
||||||
if (method.HasThis && instr.OpCode.Code == Code.Newobj)
|
|
||||||
stack++;
|
|
||||||
|
|
||||||
if (method.HasParameters)
|
if (method.HasParameters)
|
||||||
stack -= method.Parameters.Count;
|
pops += method.Parameters.Count;
|
||||||
if (method.HasThis && instr.OpCode.Code != Code.Newobj)
|
if (method.HasThis && instr.OpCode.Code != Code.Newobj)
|
||||||
stack--;
|
pops++;
|
||||||
}
|
|
||||||
else
|
|
||||||
updateStack_nonCall(instr, ref stack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updateStack_nonCall(Instruction instr, ref int stack) {
|
// 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;
|
StackBehaviour stackBehavior;
|
||||||
|
|
||||||
|
pushes = 0;
|
||||||
|
pops = 0;
|
||||||
|
|
||||||
stackBehavior = instr.OpCode.StackBehaviourPush;
|
stackBehavior = instr.OpCode.StackBehaviourPush;
|
||||||
switch (stackBehavior) {
|
switch (stackBehavior) {
|
||||||
case StackBehaviour.Push0:
|
case StackBehaviour.Push0:
|
||||||
|
@ -514,11 +532,11 @@ namespace de4dot.blocks {
|
||||||
case StackBehaviour.Pushr4:
|
case StackBehaviour.Pushr4:
|
||||||
case StackBehaviour.Pushr8:
|
case StackBehaviour.Pushr8:
|
||||||
case StackBehaviour.Pushref:
|
case StackBehaviour.Pushref:
|
||||||
stack++;
|
pushes++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StackBehaviour.Push1_push1:
|
case StackBehaviour.Push1_push1:
|
||||||
stack += 2;
|
pushes += 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StackBehaviour.Varpush: // only call, calli, callvirt which are handled elsewhere
|
case StackBehaviour.Varpush: // only call, calli, callvirt which are handled elsewhere
|
||||||
|
@ -534,7 +552,7 @@ namespace de4dot.blocks {
|
||||||
case StackBehaviour.Pop1:
|
case StackBehaviour.Pop1:
|
||||||
case StackBehaviour.Popi:
|
case StackBehaviour.Popi:
|
||||||
case StackBehaviour.Popref:
|
case StackBehaviour.Popref:
|
||||||
stack--;
|
pops++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StackBehaviour.Pop1_pop1:
|
case StackBehaviour.Pop1_pop1:
|
||||||
|
@ -545,7 +563,7 @@ namespace de4dot.blocks {
|
||||||
case StackBehaviour.Popi_popr8:
|
case StackBehaviour.Popi_popr8:
|
||||||
case StackBehaviour.Popref_pop1:
|
case StackBehaviour.Popref_pop1:
|
||||||
case StackBehaviour.Popref_popi:
|
case StackBehaviour.Popref_popi:
|
||||||
stack -= 2;
|
pops += 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StackBehaviour.Popi_popi_popi:
|
case StackBehaviour.Popi_popi_popi:
|
||||||
|
@ -554,16 +572,16 @@ namespace de4dot.blocks {
|
||||||
case StackBehaviour.Popref_popi_popr4:
|
case StackBehaviour.Popref_popi_popr4:
|
||||||
case StackBehaviour.Popref_popi_popr8:
|
case StackBehaviour.Popref_popi_popr8:
|
||||||
case StackBehaviour.Popref_popi_popref:
|
case StackBehaviour.Popref_popi_popref:
|
||||||
stack -= 3;
|
pops += 3;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StackBehaviour.PopAll:
|
case StackBehaviour.PopAll:
|
||||||
stack = 0;
|
pops = -1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StackBehaviour.Varpop: // call, calli, callvirt, newobj (all handled elsewhere), and ret
|
case StackBehaviour.Varpop: // call, calli, callvirt, newobj (all handled elsewhere), and ret
|
||||||
// It's RET. Ignore decrementing stack once if method returns something
|
if (methodHasReturnValue)
|
||||||
// since it's not important and we don't know whether it returns anything.
|
pops++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -583,8 +601,14 @@ namespace de4dot.blocks {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string getFullAssemblyName(IMetadataScope scope) {
|
public static string getFullAssemblyName(IMetadataScope scope) {
|
||||||
|
//TODO: Returning scope.Name is probably best since the method could fail.
|
||||||
var asmRef = getAssemblyNameReference(scope);
|
var asmRef = getAssemblyNameReference(scope);
|
||||||
return asmRef.FullName;
|
return asmRef.FullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool isAssembly(IMetadataScope scope, string assemblySimpleName) {
|
||||||
|
return scope.Name == assemblySimpleName ||
|
||||||
|
scope.Name.StartsWith(assemblySimpleName + ",", StringComparison.Ordinal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace de4dot.blocks {
|
||||||
|
|
||||||
int stack = stackStart;
|
int stack = stackStart;
|
||||||
foreach (var instr in block.Instructions)
|
foreach (var instr in block.Instructions)
|
||||||
DotNetUtils.updateStack(instr.Instruction, ref stack);
|
DotNetUtils.updateStack(instr.Instruction, ref stack, false);
|
||||||
stackEnd = stack;
|
stackEnd = stack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,14 @@
|
||||||
<Compile Include="Block.cs" />
|
<Compile Include="Block.cs" />
|
||||||
<Compile Include="Blocks.cs" />
|
<Compile Include="Blocks.cs" />
|
||||||
<Compile Include="BlocksSorter.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="CodeGenerator.cs" />
|
||||||
<Compile Include="CondBranchDeobfuscator.cs" />
|
<Compile Include="CondBranchDeobfuscator.cs" />
|
||||||
|
<Compile Include="cflow\BlocksControlFlowDeobfuscator.cs" />
|
||||||
|
<Compile Include="DeadBlocksRemover.cs" />
|
||||||
<Compile Include="DotNetUtils.cs" />
|
<Compile Include="DotNetUtils.cs" />
|
||||||
<Compile Include="FilterHandlerBlock.cs" />
|
<Compile Include="FilterHandlerBlock.cs" />
|
||||||
<Compile Include="ForwardScanOrder.cs" />
|
<Compile Include="ForwardScanOrder.cs" />
|
||||||
|
|
564
blocks/cflow/BlockControlFlowDeobfuscator.cs
Normal file
564
blocks/cflow/BlockControlFlowDeobfuscator.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
blocks/cflow/BlocksControlFlowDeobfuscator.cs
Normal file
67
blocks/cflow/BlocksControlFlowDeobfuscator.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1336
blocks/cflow/InstructionEmulator.cs
Normal file
1336
blocks/cflow/InstructionEmulator.cs
Normal file
File diff suppressed because it is too large
Load Diff
126
blocks/cflow/Value.cs
Normal file
126
blocks/cflow/Value.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
96
blocks/cflow/ValueStack.cs
Normal file
96
blocks/cflow/ValueStack.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
using de4dot.deobfuscators;
|
using de4dot.deobfuscators;
|
||||||
using de4dot.blocks;
|
using de4dot.blocks;
|
||||||
|
using de4dot.blocks.cflow;
|
||||||
using de4dot.AssemblyClient;
|
using de4dot.AssemblyClient;
|
||||||
|
|
||||||
namespace de4dot {
|
namespace de4dot {
|
||||||
|
@ -441,6 +442,7 @@ namespace de4dot {
|
||||||
|
|
||||||
Log.v("Deobfuscating methods");
|
Log.v("Deobfuscating methods");
|
||||||
var methodPrinter = new MethodPrinter();
|
var methodPrinter = new MethodPrinter();
|
||||||
|
var cflowObfuscator = new BlocksControlFlowDeobfuscator();
|
||||||
foreach (var method in allMethods) {
|
foreach (var method in allMethods) {
|
||||||
Log.v("Deobfuscating {0} ({1:X8})", method, method.MetadataToken.ToUInt32());
|
Log.v("Deobfuscating {0} ({1:X8})", method, method.MetadataToken.ToUInt32());
|
||||||
Log.indent();
|
Log.indent();
|
||||||
|
@ -450,7 +452,9 @@ namespace de4dot {
|
||||||
|
|
||||||
deob.deobfuscateMethodBegin(blocks);
|
deob.deobfuscateMethodBegin(blocks);
|
||||||
if (options.ControlFlowDeobfuscation) {
|
if (options.ControlFlowDeobfuscation) {
|
||||||
int numDeadBlocks = blocks.deobfuscate();
|
cflowObfuscator.init(blocks);
|
||||||
|
cflowObfuscator.deobfuscate();
|
||||||
|
int numDeadBlocks = cflowObfuscator.NumberOfRemovedDeadBlocks;
|
||||||
if (numDeadBlocks > 0)
|
if (numDeadBlocks > 0)
|
||||||
Log.v("Removed {0} dead block(s)", numDeadBlocks);
|
Log.v("Removed {0} dead block(s)", numDeadBlocks);
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,7 +218,7 @@ namespace de4dot.deobfuscators {
|
||||||
if (stack <= 0)
|
if (stack <= 0)
|
||||||
return null;
|
return null;
|
||||||
var instr = instrs[i];
|
var instr = instrs[i];
|
||||||
DotNetUtils.updateStack(instr.Instruction, ref stack);
|
DotNetUtils.updateStack(instr.Instruction, ref stack, false);
|
||||||
if (stack < 0)
|
if (stack < 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user