Add support for normal predicate control flow;

Add detection weight to ConfusedBy attribute
This commit is contained in:
ViR Dash 2017-09-26 01:30:57 +01:00
parent 7adf818194
commit 5ec36c863c
9 changed files with 192 additions and 110 deletions

View File

@ -29,28 +29,17 @@ namespace de4dot.blocks {
SwitchCase
}
public class SwitchData
{
public readonly Block Block;
public SwitchData(Block switchBlock)
{
Block = switchBlock;
}
public int? Key = null;
public bool IsKeyHardCoded = false;
}
public class Block : BaseBlock
{
public Block()
{
public BlockType BlockType = BlockType.Normal;
public bool Processed = false;
public SwitchData SwitchData;
public Block()
{
SwitchData = new SwitchData(this);
}
public BlockType BlockType = BlockType.Normal;
public SwitchData SwitchData;
public bool Processed = false;
List<Instr> instructions = new List<Instr>();
// List of all explicit (non-fall-through) targets. It's just one if it's a normal

View File

@ -0,0 +1,20 @@
namespace de4dot.blocks
{
public class SwitchData
{
protected readonly Block _block;
public int? Key;
public bool IsKeyHardCoded;
public SwitchData(Block block)
{
_block = block;
}
public virtual bool Initialize()
{
return false;
}
}
}

View File

@ -78,6 +78,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ScopeBlock.cs" />
<Compile Include="StackTracePatcher.cs" />
<Compile Include="SwitchData.cs" />
<Compile Include="TryBlock.cs" />
<Compile Include="TryHandlerBlock.cs" />
<Compile Include="Utils.cs" />

View File

@ -163,6 +163,8 @@
<Compile Include="deobfuscators\ConfuserEx\LzmaFinder.cs" />
<Compile Include="deobfuscators\ConfuserEx\ProxyCallFixer.cs" />
<Compile Include="deobfuscators\ConfuserEx\ResourceDecrypter.cs" />
<Compile Include="deobfuscators\ConfuserEx\NativeSwitchData.cs" />
<Compile Include="deobfuscators\ConfuserEx\NormalSwitchData.cs" />
<Compile Include="deobfuscators\ConfuserEx\Utils.cs" />
<Compile Include="deobfuscators\ConfuserEx\x86\Bea\Constants.cs" />
<Compile Include="deobfuscators\ConfuserEx\x86\Bea\Engine.cs" />

View File

@ -16,30 +16,49 @@ namespace de4dot.code.deobfuscators.ConfuserEx
private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator();
private Blocks _blocks;
private X86Method _nativeMethod;
private Blocks _blocks;
private Local _switchKey;
private int? CalculateKey()
private int? CalculateKey(SwitchData switchData)
{
var popValue = _instructionEmulator.Peek();
if (popValue == null || !popValue.IsInt32() || !(popValue as Int32Value).AllBitsValid())
return null;
_instructionEmulator.Pop();
int result = _nativeMethod.Execute(((Int32Value)popValue).Value);
return result;
int num = ((Int32Value)popValue).Value;
if (switchData is NativeSwitchData)
{
var nativeSwitchData = (NativeSwitchData)switchData;
var nativeMethod = new X86Method(nativeSwitchData.NativeMethodDef, _blocks.Method.Module as ModuleDefMD); //TODO: Possible null
return nativeMethod.Execute(num);
}
if (switchData is NormalSwitchData)
{
var normalSwitchData = (NormalSwitchData)switchData;
return num ^ normalSwitchData.Key.Value;
}
return null;
}
private int CalculateSwitchCaseIndex(Block block, int nativeKey)
private int? CalculateSwitchCaseIndex(Block block, SwitchData switchData, int key)
{
_instructionEmulator.Push(new Int32Value(nativeKey));
_instructionEmulator.Emulate(block.Instructions, block.SwitchData.IsKeyHardCoded ? 2 : 1, block.Instructions.Count - 1);
if (switchData is NativeSwitchData)
{
_instructionEmulator.Push(new Int32Value(key));
_instructionEmulator.Emulate(block.Instructions, block.SwitchData.IsKeyHardCoded ? 2 : 1, block.Instructions.Count - 1);
var popValue = _instructionEmulator.Peek();
_instructionEmulator.Pop();
return ((Int32Value)popValue).Value;
var popValue = _instructionEmulator.Peek();
_instructionEmulator.Pop();
return ((Int32Value)popValue).Value;
}
if (switchData is NormalSwitchData)
{
var normalSwitchData = (NormalSwitchData)switchData;
return key % normalSwitchData.DivisionKey;
}
return null;
}
private void ProcessHardcodedSwitch(Block switchBlock) // a single-case switch
@ -47,15 +66,17 @@ namespace de4dot.code.deobfuscators.ConfuserEx
var targets = switchBlock.Targets;
_instructionEmulator.Push(new Int32Value(switchBlock.SwitchData.Key.Value));
int? key = CalculateKey();
int? key = CalculateKey(switchBlock.SwitchData);
if (!key.HasValue)
throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
int? switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, switchBlock.SwitchData, key.Value);
if (!switchCaseIndex.HasValue)
throw new Exception("CRITICAL ERROR: SWITCH CASE HAS NO VALUE");
if (targets.Count < switchCaseIndex)
throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
var targetBlock = targets[switchCaseIndex];
var targetBlock = targets[switchCaseIndex.Value];
targetBlock.SwitchData.Key = key;
switchBlock.Instructions.Clear();
@ -70,15 +91,17 @@ namespace de4dot.code.deobfuscators.ConfuserEx
if (_instructionEmulator.Peek().IsUnknown())
throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN");
int? key = CalculateKey();
int? key = CalculateKey(switchBlock.SwitchData);
if (!key.HasValue)
throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
int? switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, switchBlock.SwitchData, key.Value);
if (!switchCaseIndex.HasValue)
throw new Exception("CRITICAL ERROR: SWITCH CASE HAS NO VALUE");
if (targets.Count < switchCaseIndex)
throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
var targetBlock = targets[switchCaseIndex];
var targetBlock = targets[switchCaseIndex.Value];
targetBlock.SwitchData.Key = key;
block.Add(new Instr(OpCodes.Pop.ToInstruction())); // neutralize the arithmetics and leave de4dot to remove them
@ -105,21 +128,23 @@ namespace de4dot.code.deobfuscators.ConfuserEx
if (_instructionEmulator.Peek().IsUnknown())
throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN");
int? key = CalculateKey();
int? key = CalculateKey(switchBlock.SwitchData);
if (!key.HasValue)
throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
int? switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, switchBlock.SwitchData, key.Value);
if (!switchCaseIndex.HasValue)
throw new Exception("CRITICAL ERROR: SWITCH CASE HAS NO VALUE");
if (targets.Count < switchCaseIndex)
throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
var targetBlock = targets[switchCaseIndex];
var targetBlock = targets[switchCaseIndex.Value];
targetBlock.SwitchData.Key = key;
sourceBlock.Instructions[sourceBlock.Instructions.Count - 1] = new Instr(OpCodes.Pop.ToInstruction());
sourceBlock.ReplaceLastNonBranchWithBranch(0, targets[switchCaseIndex]);
sourceBlock.ReplaceLastNonBranchWithBranch(0, targets[switchCaseIndex.Value]);
ProcessFallThroughs(switchCaseBlocks, switchBlock, targets[switchCaseIndex], key.Value);
ProcessFallThroughs(switchCaseBlocks, switchBlock, targets[switchCaseIndex.Value], key.Value);
// the second source block now becomes the first one
}
@ -142,12 +167,6 @@ namespace de4dot.code.deobfuscators.ConfuserEx
foreach (Block switchBlock in switchBlocks)
{
if (!switchBlock.SwitchData.IsConfuserExSwitch())
{
Console.WriteLine("Unsupported switch block obfuscation!");
continue;
}
if (switchBlock.SwitchData.IsKeyHardCoded)
{
ProcessHardcodedSwitch(switchBlock);
@ -221,19 +240,40 @@ namespace de4dot.code.deobfuscators.ConfuserEx
}
public bool IsSwitchBlock(Block block)
public bool IsConfuserExSwitchBlock(Block block)
{
if (block.LastInstr.OpCode.Code != Code.Switch || ((Instruction[])block.LastInstr.Operand)?.Length == 0)
return false;
if (!block.SwitchData.IsNative())
var instructions = block.Instructions;
var lastIndex = instructions.Count - 1;
if (instructions.Count < 4)
return false;
if (!instructions[lastIndex - 3].IsStloc())
return false;
if (!instructions[lastIndex - 2].IsLdcI4())
return false;
if (instructions[lastIndex - 1].OpCode != OpCodes.Rem_Un)
return false;
MethodDef nativeMethod = block.SwitchData.GetNativeMethod();
_nativeMethod = new X86Method(nativeMethod, _blocks.Method.Module as ModuleDefMD); //TODO: Possible null
if (!NativeMethods.Contains(nativeMethod))
NativeMethods.Add(nativeMethod);
var nativeSwitchData = new NativeSwitchData(block);
if (nativeSwitchData.Initialize())
{
block.SwitchData = nativeSwitchData;
if (!NativeMethods.Contains(nativeSwitchData.NativeMethodDef)) // add for remove
NativeMethods.Add(nativeSwitchData.NativeMethodDef);
return true;
}
return true;
var normalSwitchData = new NormalSwitchData(block);
if (normalSwitchData.Initialize())
{
block.SwitchData = normalSwitchData;
return true;
}
return false;
}
public List<Block> GetSwitchBlocks(List<Block> blocks) // get the blocks which contain the switch statement
@ -241,7 +281,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx
List<Block> switchBlocks = new List<Block>();
foreach (Block block in blocks)
if (IsSwitchBlock(block))
if (IsConfuserExSwitchBlock(block))
switchBlocks.Add(block);
return switchBlocks;

View File

@ -94,7 +94,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx
protected override int DetectInternal()
{
var val = 0;
if (_detectedConfuserExAttribute) val += 0;
if (_detectedConfuserExAttribute) val += 2;
if (_lzmaFinder.FoundLzma) val += 10;
if (_constantDecrypter.Detected) val += 10;
if (_resourceDecrypter.Detected) val += 10;

View File

@ -0,0 +1,45 @@
using de4dot.blocks;
using de4dot.code.deobfuscators.ConfuserEx.x86;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
namespace de4dot.code.deobfuscators.ConfuserEx
{
public class NativeSwitchData : SwitchData
{
public NativeSwitchData(Block switchBlock) : base(switchBlock)
{
}
public MethodDef NativeMethodDef;
public override bool Initialize()
{
var instr = _block.Instructions;
if (instr.Count <= 4)
return false;
if (instr[0].IsLdcI4() && instr[1].OpCode == OpCodes.Call)
{
IsKeyHardCoded = true;
Key = instr[0].GetLdcI4Value();
}
if (!IsKeyHardCoded && instr[0].OpCode != OpCodes.Call)
return false;
var nativeMethodDef = _block.Instructions[IsKeyHardCoded ? 1 : 0].Operand as MethodDef;
if (nativeMethodDef == null || !nativeMethodDef.IsStatic || !nativeMethodDef.IsNative)
return false;
if (!DotNetUtils.IsMethod(nativeMethodDef, "System.Int32", "(System.Int32)"))
return false;
for (var i = IsKeyHardCoded ? 2 : 1; i < instr.Count - 1; i++)
if (!instr[i].IsValidInstr())
return false;
NativeMethodDef = nativeMethodDef;
return true;
}
}
}

View File

@ -0,0 +1,40 @@
using de4dot.blocks;
using dnlib.DotNet.Emit;
namespace de4dot.code.deobfuscators.ConfuserEx
{
public class NormalSwitchData : SwitchData
{
public readonly Block Block;
public NormalSwitchData(Block switchBlock) : base(switchBlock)
{
Block = switchBlock;
}
public int DivisionKey;
public override bool Initialize()
{
var instr = _block.Instructions;
if (instr.Count != 7)
return false;
if (!instr[0].IsLdcI4())
return false;
if (instr[1].OpCode != OpCodes.Xor)
return false;
if (instr[2].OpCode != OpCodes.Dup)
return false;
if (!instr[3].IsStloc())
return false;
if (!instr[4].IsLdcI4())
return false;
if (instr[5].OpCode != OpCodes.Rem_Un)
return false;
Key = instr[0].GetLdcI4Value();
DivisionKey = instr[4].GetLdcI4Value();
return true;
}
}
}

View File

@ -169,61 +169,6 @@ namespace de4dot.code.deobfuscators.ConfuserEx
public static class Extensions
{
public static bool IsConfuserExSwitch(this SwitchData switchData)
{
var instructions = switchData.Block.Instructions;
var lastIndex = instructions.Count - 1;
if (instructions.Count < 4)
return false;
if (!instructions[lastIndex - 3].IsStloc())
return false;
if (!instructions[lastIndex - 2].IsLdcI4())
return false;
if (instructions[lastIndex - 1].OpCode != OpCodes.Rem_Un)
return false;
return true;
}
public static bool IsNative(this SwitchData switchData)
{
var block = switchData.Block;
var instr = block.Instructions;
if (instr.Count <= 4)
return false;
if (instr[0].IsLdcI4() && instr[1].OpCode == OpCodes.Call)
{
switchData.IsKeyHardCoded = true;
block.SwitchData.Key = block.FirstInstr.GetLdcI4Value();
}
if (!switchData.IsKeyHardCoded && instr[0].OpCode != OpCodes.Call)
return false;
var method = block.Instructions[switchData.IsKeyHardCoded ? 1 : 0].Operand as MethodDef;
if (method == null || !method.IsStatic || !method.IsNative)
return false;
if (!DotNetUtils.IsMethod(method, "System.Int32", "(System.Int32)"))
return false;
for (var i = switchData.IsKeyHardCoded ? 2 : 1; i < instr.Count - 1; i++)
if (!instr[i].IsValidInstr())
return false;
return true;
}
public static MethodDef GetNativeMethod(this SwitchData switchData)
{
var block = switchData.Block;
var method = block.Instructions[switchData.IsKeyHardCoded ? 1 : 0].Operand as MethodDef;
return method;
}
public static bool IsTernaryPredicate(this Block ternaryPredicateBlock)
{
if (!ternaryPredicateBlock.LastInstr.IsConditionalBranch())