2017-08-20 21:25:25 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Runtime.InteropServices;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
using de4dot.blocks;
|
|
|
|
|
using dnlib.DotNet;
|
|
|
|
|
using dnlib.DotNet.Emit;
|
|
|
|
|
|
|
|
|
|
namespace de4dot.code.deobfuscators.ConfuserEx
|
|
|
|
|
{
|
|
|
|
|
public static class Utils
|
|
|
|
|
{
|
|
|
|
|
public static bool IsArithmetical(this Instr instr)
|
|
|
|
|
{
|
|
|
|
|
switch (instr.OpCode.Code)
|
|
|
|
|
{
|
|
|
|
|
case Code.Add:
|
|
|
|
|
case Code.Add_Ovf:
|
|
|
|
|
case Code.Add_Ovf_Un:
|
|
|
|
|
case Code.Div:
|
|
|
|
|
case Code.Div_Un:
|
|
|
|
|
case Code.Mul:
|
|
|
|
|
case Code.Mul_Ovf:
|
|
|
|
|
case Code.Mul_Ovf_Un:
|
|
|
|
|
case Code.Not:
|
|
|
|
|
case Code.Shl:
|
|
|
|
|
case Code.Shr:
|
|
|
|
|
case Code.Shr_Un:
|
|
|
|
|
case Code.Sub:
|
|
|
|
|
case Code.Sub_Ovf:
|
|
|
|
|
case Code.Sub_Ovf_Un:
|
|
|
|
|
case Code.Xor:
|
|
|
|
|
case Code.And:
|
|
|
|
|
case Code.Rem:
|
|
|
|
|
case Code.Rem_Un:
|
|
|
|
|
case Code.Ceq:
|
|
|
|
|
case Code.Cgt:
|
|
|
|
|
case Code.Cgt_Un:
|
|
|
|
|
case Code.Clt:
|
|
|
|
|
case Code.Clt_Un:
|
|
|
|
|
case Code.Neg:
|
|
|
|
|
case Code.Or:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsConv(this Instr instr)
|
|
|
|
|
{
|
|
|
|
|
switch (instr.OpCode.Code)
|
|
|
|
|
{
|
|
|
|
|
case Code.Conv_I1:
|
|
|
|
|
case Code.Conv_I2:
|
|
|
|
|
case Code.Conv_I4:
|
|
|
|
|
case Code.Conv_I8:
|
|
|
|
|
case Code.Conv_U1:
|
|
|
|
|
case Code.Conv_U2:
|
|
|
|
|
case Code.Conv_U4:
|
|
|
|
|
case Code.Conv_U8:
|
|
|
|
|
case Code.Conv_R4:
|
|
|
|
|
case Code.Conv_R8:
|
|
|
|
|
case Code.Conv_Ovf_I1:
|
|
|
|
|
case Code.Conv_Ovf_I1_Un:
|
|
|
|
|
case Code.Conv_Ovf_I2:
|
|
|
|
|
case Code.Conv_Ovf_I2_Un:
|
|
|
|
|
case Code.Conv_Ovf_I4:
|
|
|
|
|
case Code.Conv_Ovf_I4_Un:
|
|
|
|
|
case Code.Conv_Ovf_I8:
|
|
|
|
|
case Code.Conv_Ovf_I8_Un:
|
|
|
|
|
case Code.Conv_Ovf_U1:
|
|
|
|
|
case Code.Conv_Ovf_U1_Un:
|
|
|
|
|
case Code.Conv_Ovf_U2:
|
|
|
|
|
case Code.Conv_Ovf_U2_Un:
|
|
|
|
|
case Code.Conv_Ovf_U4:
|
|
|
|
|
case Code.Conv_Ovf_U4_Un:
|
|
|
|
|
case Code.Conv_Ovf_U8:
|
|
|
|
|
case Code.Conv_Ovf_U8_Un:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsLdc(this Instr instr)
|
|
|
|
|
{
|
|
|
|
|
switch (instr.OpCode.Code)
|
|
|
|
|
{
|
|
|
|
|
case Code.Ldc_I4:
|
|
|
|
|
case Code.Ldc_I4_S:
|
|
|
|
|
case Code.Ldc_I4_0:
|
|
|
|
|
case Code.Ldc_I4_1:
|
|
|
|
|
case Code.Ldc_I4_2:
|
|
|
|
|
case Code.Ldc_I4_3:
|
|
|
|
|
case Code.Ldc_I4_4:
|
|
|
|
|
case Code.Ldc_I4_5:
|
|
|
|
|
case Code.Ldc_I4_6:
|
|
|
|
|
case Code.Ldc_I4_7:
|
|
|
|
|
case Code.Ldc_I4_8:
|
|
|
|
|
case Code.Ldc_I4_M1:
|
|
|
|
|
case Code.Ldc_I8:
|
|
|
|
|
case Code.Ldc_R4:
|
|
|
|
|
case Code.Ldc_R8:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsLoc(this Instr instr)
|
|
|
|
|
{
|
|
|
|
|
switch (instr.OpCode.Code)
|
|
|
|
|
{
|
|
|
|
|
case Code.Ldloc:
|
|
|
|
|
case Code.Ldloc_S:
|
|
|
|
|
case Code.Ldloc_0:
|
|
|
|
|
case Code.Ldloc_1:
|
|
|
|
|
case Code.Ldloc_2:
|
|
|
|
|
case Code.Ldloc_3:
|
|
|
|
|
case Code.Ldloca:
|
|
|
|
|
case Code.Ldloca_S:
|
|
|
|
|
case Code.Stloc:
|
|
|
|
|
case Code.Stloc_S:
|
|
|
|
|
case Code.Stloc_0:
|
|
|
|
|
case Code.Stloc_1:
|
|
|
|
|
case Code.Stloc_2:
|
|
|
|
|
case Code.Stloc_3:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsValidInstr(this Instr instr)
|
|
|
|
|
{
|
2017-08-08 18:27:21 +08:00
|
|
|
|
return IsArithmetical(instr) || instr.IsConv() || IsLdc(instr) || IsLoc(instr) ||
|
|
|
|
|
instr.OpCode == OpCodes.Dup;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsDup(this Block block)
|
|
|
|
|
{
|
|
|
|
|
if (block.Sources.Count != 1)
|
|
|
|
|
return false;
|
|
|
|
|
if (block.Instructions.Count != 2)
|
|
|
|
|
return false;
|
|
|
|
|
if (!block.FirstInstr.IsLdcI4())
|
|
|
|
|
return false;
|
|
|
|
|
if (block.LastInstr.OpCode != OpCodes.Dup)
|
|
|
|
|
if (!block.LastInstr.IsLdcI4() || block.LastInstr.GetLdcI4Value() != block.FirstInstr.GetLdcI4Value())
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-08-20 21:25:25 +08:00
|
|
|
|
|
|
|
|
|
public static MethodDefUser Clone(MethodDef origin)
|
|
|
|
|
{
|
|
|
|
|
var ret = new MethodDefUser(origin.Name, origin.MethodSig, origin.ImplAttributes, origin.Attributes);
|
|
|
|
|
|
|
|
|
|
foreach (GenericParam genericParam in origin.GenericParameters)
|
|
|
|
|
ret.GenericParameters.Add(new GenericParamUser(genericParam.Number, genericParam.Flags, "-"));
|
|
|
|
|
|
|
|
|
|
ret.Body = origin.Body;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static T[] ConvertArray<T, T1>(T1[] array)
|
|
|
|
|
{
|
|
|
|
|
var l = Marshal.SizeOf(typeof(T));
|
|
|
|
|
var l1 = Marshal.SizeOf(typeof(T1));
|
|
|
|
|
var buffer = new T[array.Length * l1 / l];
|
|
|
|
|
Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1);
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
2017-02-13 17:14:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2017-08-08 18:27:21 +08:00
|
|
|
|
for (var i = switchData.IsKeyHardCoded ? 2 : 1; i < instr.Count - 1; i++)
|
2017-02-13 17:14:22 +08:00
|
|
|
|
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())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (ternaryPredicateBlock.CountTargets() > 2)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
var source1 = ternaryPredicateBlock.Targets[0];
|
|
|
|
|
var source2 = ternaryPredicateBlock.FallThrough;
|
|
|
|
|
|
|
|
|
|
//if (!IsDup(source1) || !IsDup(source2))
|
|
|
|
|
// return false;
|
|
|
|
|
|
|
|
|
|
if (source1.CountTargets() > 1 || source2.CountTargets() > 1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
var mainBlock = source1.FallThrough;
|
|
|
|
|
|
|
|
|
|
if (mainBlock != source2.FallThrough)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (mainBlock.Sources.Count != 2)
|
|
|
|
|
return false;
|
|
|
|
|
if (mainBlock.LastInstr.OpCode == OpCodes.Ret)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsTernary(this Block block)
|
|
|
|
|
{
|
|
|
|
|
var sources = block.Sources;
|
|
|
|
|
if (sources.Count != 2)
|
|
|
|
|
return false;
|
2017-08-08 18:27:21 +08:00
|
|
|
|
if (!sources[0].IsDup() || !sources[1].IsDup()) //TODO: Case without DUP?
|
|
|
|
|
return false;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
if (sources[0].CountTargets() > 1 || sources[1].CountTargets() > 1)
|
|
|
|
|
return false;
|
|
|
|
|
if (sources[0].FallThrough != block || sources[1].FallThrough != block)
|
|
|
|
|
return false;
|
|
|
|
|
if (sources[0].Sources[0] != sources[1].Sources[0])
|
|
|
|
|
return false;
|
|
|
|
|
if (!sources[0].Sources[0].IsConditionalBranch())
|
|
|
|
|
return false;
|
|
|
|
|
if (block.LastInstr.OpCode == OpCodes.Ret)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static List<Block> GetTernaryPredicates(this List<Block> switchCaseBlocks)
|
|
|
|
|
{
|
2017-08-08 18:27:21 +08:00
|
|
|
|
var ternaryPredicates = new List<Block>();
|
2017-02-13 17:14:22 +08:00
|
|
|
|
|
2017-08-08 18:27:21 +08:00
|
|
|
|
foreach (var preBlock in switchCaseBlocks)
|
|
|
|
|
if (IsTernary(preBlock)) // switchCaseBlock -> 2x sourceBlock -> ternaryPredicateBlock
|
2017-02-13 17:14:22 +08:00
|
|
|
|
ternaryPredicates.Add(preBlock.Sources[0].Sources[0]);
|
|
|
|
|
|
|
|
|
|
return ternaryPredicates;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Block GetTernaryPredicateMainBlock(this Block ternaryPredicateBlock)
|
|
|
|
|
{
|
|
|
|
|
return ternaryPredicateBlock.FallThrough.FallThrough;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-08 18:27:21 +08:00
|
|
|
|
}
|