7adf818194
Move BeaEngine.dll to /bin/ Make sure BeaEngine.dll is loaded when the working directory is different Disable file deobfuscation exception handler Don't remove LZMA methods by default Trim version read from ConfuserAttribute Minor refactoring
294 lines
9.8 KiB
C#
294 lines
9.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
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)
|
|
{
|
|
return IsArithmetical(instr) || instr.IsConv() || IsLdc(instr) || IsLoc(instr) ||
|
|
instr.OpCode == OpCodes.Dup;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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())
|
|
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;
|
|
if (!sources[0].IsDup() || !sources[1].IsDup()) //TODO: Case without DUP?
|
|
return false;
|
|
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)
|
|
{
|
|
var ternaryPredicates = new List<Block>();
|
|
|
|
foreach (var preBlock in switchCaseBlocks)
|
|
if (IsTernary(preBlock)) // switchCaseBlock -> 2x sourceBlock -> ternaryPredicateBlock
|
|
ternaryPredicates.Add(preBlock.Sources[0].Sources[0]);
|
|
|
|
return ternaryPredicates;
|
|
}
|
|
|
|
public static Block GetTernaryPredicateMainBlock(this Block ternaryPredicateBlock)
|
|
{
|
|
return ternaryPredicateBlock.FallThrough.FallThrough;
|
|
}
|
|
}
|
|
} |