Add support for normal predicate control flow;
Add detection weight to ConfusedBy attribute
This commit is contained in:
parent
7adf818194
commit
5ec36c863c
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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" />
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
|
|
Loading…
Reference in New Issue