2017-08-08 18:27:21 +08:00
|
|
|
|
using System;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
using System.Collections.Generic;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
using System.Runtime.InteropServices;
|
2017-08-08 18:27:21 +08:00
|
|
|
|
using de4dot.blocks;
|
|
|
|
|
using dnlib.DotNet;
|
|
|
|
|
using dnlib.DotNet.Emit;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
using dnlib.DotNet.Writer;
|
|
|
|
|
using FieldAttributes = dnlib.DotNet.FieldAttributes;
|
|
|
|
|
using MethodAttributes = dnlib.DotNet.MethodAttributes;
|
|
|
|
|
using TypeAttributes = dnlib.DotNet.TypeAttributes;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
|
|
|
|
|
namespace de4dot.code.deobfuscators.ConfuserEx
|
|
|
|
|
{
|
|
|
|
|
public class ResourceDecrypter
|
|
|
|
|
{
|
2017-08-08 18:27:21 +08:00
|
|
|
|
private FieldDef _arrayField, _asmField;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
|
2017-08-08 18:27:21 +08:00
|
|
|
|
private byte[] _decryptedBytes;
|
|
|
|
|
private readonly ISimpleDeobfuscator _deobfuscator;
|
|
|
|
|
private readonly MethodDef _lzmaMethod;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
|
2017-08-08 18:27:21 +08:00
|
|
|
|
private readonly ModuleDef _module;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
|
|
|
|
|
public ResourceDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfuscator deobfsucator)
|
|
|
|
|
{
|
2017-08-08 18:27:21 +08:00
|
|
|
|
this._module = module;
|
|
|
|
|
this._lzmaMethod = lzmaMethod;
|
|
|
|
|
_deobfuscator = deobfsucator;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-20 21:25:25 +08:00
|
|
|
|
public bool CanRemoveLzma { get; private set; }
|
2017-08-08 18:27:21 +08:00
|
|
|
|
|
|
|
|
|
public TypeDef Type { get; private set; }
|
|
|
|
|
public MethodDef Method { get; private set; }
|
|
|
|
|
public MethodDef AssembyResolveMethod { get; private set; }
|
|
|
|
|
public List<FieldDef> Fields => new List<FieldDef> {_arrayField, _asmField};
|
|
|
|
|
|
|
|
|
|
public bool Detected => Method != null && _decryptedBytes != null && _arrayField != null && _asmField != null;
|
|
|
|
|
|
2017-02-13 17:14:22 +08:00
|
|
|
|
public void Find()
|
|
|
|
|
{
|
2017-08-08 18:27:21 +08:00
|
|
|
|
var moduleCctor = DotNetUtils.GetModuleTypeCctor(_module);
|
2017-02-13 17:14:22 +08:00
|
|
|
|
if (moduleCctor == null)
|
|
|
|
|
return;
|
|
|
|
|
foreach (var inst in moduleCctor.Body.Instructions)
|
|
|
|
|
{
|
|
|
|
|
if (inst.OpCode != OpCodes.Call)
|
|
|
|
|
continue;
|
|
|
|
|
if (!(inst.Operand is MethodDef))
|
|
|
|
|
continue;
|
|
|
|
|
var method = inst.Operand as MethodDef;
|
|
|
|
|
if (!method.HasBody || !method.IsStatic)
|
|
|
|
|
continue;
|
|
|
|
|
if (!DotNetUtils.IsMethod(method, "System.Void", "()"))
|
|
|
|
|
continue;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
|
2017-08-08 18:27:21 +08:00
|
|
|
|
_deobfuscator.Deobfuscate(method, SimpleDeobfuscatorFlags.Force);
|
2017-08-20 21:25:25 +08:00
|
|
|
|
|
|
|
|
|
if (!IsResDecryptInit(method, out FieldDef aField, out FieldDef asmField, out MethodDef mtd))
|
2017-02-13 17:14:22 +08:00
|
|
|
|
continue;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_decryptedBytes = DecryptArray(method, aField.InitialValue);
|
|
|
|
|
}
|
|
|
|
|
catch(Exception e)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(e.Message);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_arrayField = aField;
|
|
|
|
|
Type = DotNetUtils.GetType(_module, aField.FieldSig.Type);
|
|
|
|
|
_asmField = asmField;
|
|
|
|
|
AssembyResolveMethod = mtd;
|
|
|
|
|
Method = method;
|
|
|
|
|
CanRemoveLzma = true;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-20 21:25:25 +08:00
|
|
|
|
private bool IsResDecryptInit(MethodDef method, out FieldDef aField, out FieldDef asField, out MethodDef mtd)
|
2017-02-13 17:14:22 +08:00
|
|
|
|
{
|
2017-08-20 21:25:25 +08:00
|
|
|
|
aField = null;
|
|
|
|
|
asField = null;
|
|
|
|
|
mtd = null;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
var instr = method.Body.Instructions;
|
|
|
|
|
if (instr.Count < 15)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!instr[0].IsLdcI4())
|
|
|
|
|
return false;
|
2017-08-08 18:27:21 +08:00
|
|
|
|
if (!instr[1].IsStloc()) //uint num = 96u;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!instr[2].IsLdcI4())
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[0].GetLdcI4Value() != instr[2].GetLdcI4Value())
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[3].OpCode != OpCodes.Newarr)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[3].Operand.ToString() != "System.UInt32")
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[4].OpCode != OpCodes.Dup)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[5].OpCode != OpCodes.Ldtoken)
|
|
|
|
|
return false;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
aField = instr[5].Operand as FieldDef;
|
|
|
|
|
if (aField?.InitialValue == null)
|
2017-02-13 17:14:22 +08:00
|
|
|
|
return false;
|
|
|
|
|
if (aField.Attributes != (FieldAttributes.Assembly | FieldAttributes.Static | FieldAttributes.HasFieldRVA))
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[6].OpCode != OpCodes.Call)
|
|
|
|
|
return false;
|
2017-08-08 18:27:21 +08:00
|
|
|
|
if (instr[6].Operand.ToString() !=
|
|
|
|
|
"System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)"
|
|
|
|
|
)
|
2017-02-13 17:14:22 +08:00
|
|
|
|
return false;
|
2017-08-08 18:27:21 +08:00
|
|
|
|
if (!instr[7].IsStloc()) // uint[] array = new uint[] {.....};
|
2017-02-13 17:14:22 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
var l = instr.Count;
|
|
|
|
|
if (!instr[l - 10].IsLdloc())
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[l - 9].OpCode != OpCodes.Call)
|
|
|
|
|
return false;
|
2017-08-08 18:27:21 +08:00
|
|
|
|
if (instr[l - 9].Operand != _lzmaMethod)
|
2017-02-13 17:14:22 +08:00
|
|
|
|
return false;
|
|
|
|
|
if (instr[l - 8].OpCode != OpCodes.Call)
|
|
|
|
|
return false;
|
2017-08-08 18:27:21 +08:00
|
|
|
|
if (instr[l - 8].Operand.ToString() !=
|
|
|
|
|
"System.Reflection.Assembly System.Reflection.Assembly::Load(System.Byte[])")
|
2017-02-13 17:14:22 +08:00
|
|
|
|
return false;
|
2017-08-08 18:27:21 +08:00
|
|
|
|
if (instr[l - 7].OpCode != OpCodes.Stsfld) //<Module>.assembly_0 = Assembly.Load(array4);
|
2017-02-13 17:14:22 +08:00
|
|
|
|
return false;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
asField = instr[l - 7].Operand as FieldDef;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
if (asField == null)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (instr[l - 6].OpCode != OpCodes.Call)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[l - 6].Operand.ToString() != "System.AppDomain System.AppDomain::get_CurrentDomain()")
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[l - 5].OpCode != OpCodes.Ldnull)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[l - 4].OpCode != OpCodes.Ldftn)
|
|
|
|
|
return false;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
mtd = instr[l - 4].Operand as MethodDef;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
if (mtd == null)
|
|
|
|
|
return false;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
|
|
|
|
|
if (DotNetUtils.IsMethod(mtd, "", "()"))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
_deobfuscator.Deobfuscate(mtd, SimpleDeobfuscatorFlags.Force);
|
|
|
|
|
|
2017-02-13 17:14:22 +08:00
|
|
|
|
if (!IsAssembyResolveMethod(mtd, asField))
|
|
|
|
|
return false;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
|
2017-02-13 17:14:22 +08:00
|
|
|
|
if (instr[l - 3].OpCode != OpCodes.Newobj)
|
|
|
|
|
return false;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
if (instr[l - 2].OpCode != OpCodes.Callvirt)
|
|
|
|
|
//AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(<Module>.smethod_1);
|
2017-02-13 17:14:22 +08:00
|
|
|
|
return false;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
|
2017-02-13 17:14:22 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2017-08-08 18:27:21 +08:00
|
|
|
|
|
2017-08-20 21:25:25 +08:00
|
|
|
|
private byte[] DecryptArray(MethodDef method, byte[] encryptedArray)
|
2017-02-13 17:14:22 +08:00
|
|
|
|
{
|
2017-08-20 21:25:25 +08:00
|
|
|
|
ModuleDefUser tempModule = new ModuleDefUser("TempModule");
|
|
|
|
|
|
|
|
|
|
AssemblyDef tempAssembly = new AssemblyDefUser("TempAssembly");
|
|
|
|
|
tempAssembly.Modules.Add(tempModule);
|
|
|
|
|
|
|
|
|
|
var tempType = new TypeDefUser("", "TempType", tempModule.CorLibTypes.Object.TypeDefOrRef);
|
|
|
|
|
tempType.Attributes = TypeAttributes.Public | TypeAttributes.Class;
|
|
|
|
|
MethodDef tempMethod = Utils.Clone(method);
|
2017-08-08 18:27:21 +08:00
|
|
|
|
|
2017-08-20 21:25:25 +08:00
|
|
|
|
tempMethod.ReturnType = new SZArraySig(tempModule.CorLibTypes.Byte);
|
|
|
|
|
tempMethod.MethodSig.Params.Add(new SZArraySig(tempModule.CorLibTypes.Byte));
|
|
|
|
|
tempMethod.Attributes = MethodAttributes.Public | MethodAttributes.Static;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
|
|
|
tempMethod.Body.Instructions.RemoveAt(2); // read encrypted array from argument
|
|
|
|
|
tempMethod.Body.Instructions.Insert(2, OpCodes.Ldarg_0.ToInstruction());
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
|
tempMethod.Body.Instructions.RemoveAt(tempMethod.Body.Instructions.Count -
|
|
|
|
|
2); // make return decrypted array
|
|
|
|
|
|
|
|
|
|
tempType.Methods.Add(tempMethod);
|
|
|
|
|
tempModule.Types.Add(tempType);
|
|
|
|
|
|
|
|
|
|
using (MemoryStream memoryStream = new MemoryStream())
|
2017-02-13 17:14:22 +08:00
|
|
|
|
{
|
2017-08-20 21:25:25 +08:00
|
|
|
|
ModuleWriterOptions moduleWriterOptions = new ModuleWriterOptions();
|
|
|
|
|
moduleWriterOptions.MetaDataOptions = new MetaDataOptions();
|
|
|
|
|
|
|
|
|
|
tempModule.Write(memoryStream, moduleWriterOptions);
|
|
|
|
|
|
|
|
|
|
Assembly patchedAssembly = Assembly.Load(memoryStream.ToArray());
|
|
|
|
|
var type = patchedAssembly.ManifestModule.GetType("TempType");
|
|
|
|
|
var methods = type.GetMethods();
|
|
|
|
|
MethodInfo patchedMethod = methods.First(m => m.IsPublic && m.IsStatic);
|
|
|
|
|
byte[] decryptedBytes = (byte[]) patchedMethod.Invoke(null, new object[]{encryptedArray});
|
|
|
|
|
return Lzma.Decompress(decryptedBytes);
|
2017-02-13 17:14:22 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-20 21:25:25 +08:00
|
|
|
|
private bool IsAssembyResolveMethod(MethodDef method, FieldDef field)
|
2017-02-13 17:14:22 +08:00
|
|
|
|
{
|
|
|
|
|
var instr = method.Body.Instructions;
|
|
|
|
|
if (instr.Count != 10)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (instr[0].OpCode != OpCodes.Ldsfld)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[0].Operand != field)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[1].OpCode != OpCodes.Callvirt)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[1].Operand.ToString() != "System.String System.Reflection.Assembly::get_FullName()")
|
|
|
|
|
return false;
|
|
|
|
|
if (!instr[2].IsLdarg())
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[3].OpCode != OpCodes.Callvirt)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[3].Operand.ToString() != "System.String System.ResolveEventArgs::get_Name()")
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[4].OpCode != OpCodes.Call)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[4].Operand.ToString() != "System.Boolean System.String::op_Equality(System.String,System.String)")
|
|
|
|
|
return false;
|
|
|
|
|
if (!instr[5].IsBrfalse())
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[6].OpCode != OpCodes.Ldsfld)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[6].Operand != field)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[7].OpCode != OpCodes.Ret)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[8].OpCode != OpCodes.Ldnull)
|
|
|
|
|
return false;
|
|
|
|
|
if (instr[9].OpCode != OpCodes.Ret)
|
|
|
|
|
return false;
|
2017-08-20 21:25:25 +08:00
|
|
|
|
|
2017-02-13 17:14:22 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Fix()
|
|
|
|
|
{
|
|
|
|
|
ModuleDef newModule;
|
|
|
|
|
try
|
|
|
|
|
{
|
2017-08-08 18:27:21 +08:00
|
|
|
|
newModule = ModuleDefMD.Load(_decryptedBytes);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
CanRemoveLzma = false;
|
|
|
|
|
return;
|
2017-02-13 17:14:22 +08:00
|
|
|
|
}
|
2017-08-08 18:27:21 +08:00
|
|
|
|
var toRemove = new List<Resource>();
|
|
|
|
|
var toAdd = new List<Resource>();
|
|
|
|
|
foreach (var cryptedResource in _module.Resources)
|
|
|
|
|
foreach (var resource in newModule.Resources)
|
|
|
|
|
if (cryptedResource.Name == resource.Name)
|
|
|
|
|
{
|
|
|
|
|
toRemove.Add(cryptedResource);
|
|
|
|
|
toAdd.Add(resource);
|
|
|
|
|
}
|
2017-02-13 17:14:22 +08:00
|
|
|
|
|
|
|
|
|
foreach (var resToRemove in toRemove)
|
2017-08-08 18:27:21 +08:00
|
|
|
|
_module.Resources.Remove(resToRemove);
|
2017-02-13 17:14:22 +08:00
|
|
|
|
foreach (var resToAdd in toAdd)
|
2017-08-08 18:27:21 +08:00
|
|
|
|
_module.Resources.Add(resToAdd);
|
2017-02-13 17:14:22 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|