Find and remove resource decrypter types
This commit is contained in:
parent
c18bed7d69
commit
91f7d2cb51
|
@ -21,6 +21,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using Mono.Cecil;
|
||||
using Mono.MyStuff;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.code.deobfuscators.CodeVeil {
|
||||
public class DeobfuscatorInfo : DeobfuscatorInfoBase {
|
||||
|
@ -62,6 +63,7 @@ namespace de4dot.code.deobfuscators.CodeVeil {
|
|||
StringDecrypter stringDecrypter;
|
||||
AssemblyResolver assemblyResolver;
|
||||
TypeDefinition killType;
|
||||
ResourceDecrypter resourceDecrypter;
|
||||
|
||||
internal class Options : OptionsBase {
|
||||
}
|
||||
|
@ -206,7 +208,17 @@ namespace de4dot.code.deobfuscators.CodeVeil {
|
|||
proxyDelegateFinder.initialize();
|
||||
proxyDelegateFinder.find();
|
||||
|
||||
new ResourceDecrypter(module).decrypt();
|
||||
resourceDecrypter = new ResourceDecrypter(module);
|
||||
resourceDecrypter.initialize();
|
||||
resourceDecrypter.decrypt();
|
||||
if (resourceDecrypter.CanRemoveTypes) {
|
||||
addTypeToBeRemoved(resourceDecrypter.ResourceFlagsType, "Obfuscator ResourceFlags type");
|
||||
addTypeToBeRemoved(resourceDecrypter.ResType, "Obfuscator Res type");
|
||||
addTypeToBeRemoved(resourceDecrypter.ResourceEnumeratorType, "Obfuscator ResourceEnumerator type");
|
||||
addTypeToBeRemoved(resourceDecrypter.EncryptedResourceReaderType, "Obfuscator EncryptedResourceReader type");
|
||||
addTypeToBeRemoved(resourceDecrypter.EncryptedResourceSetType, "Obfuscator EncryptedResourceSet type");
|
||||
addTypeToBeRemoved(resourceDecrypter.EncryptedResourceStreamType, "Obfuscator EncryptedResourceStream type");
|
||||
}
|
||||
}
|
||||
|
||||
void removeTamperDetection() {
|
||||
|
@ -224,13 +236,14 @@ namespace de4dot.code.deobfuscators.CodeVeil {
|
|||
addResourceToBeRemoved(assemblyResolver.BundleXmlFileResource, "Embedded assemblies XML file resource");
|
||||
}
|
||||
|
||||
public override void deobfuscateMethodBegin(blocks.Blocks blocks) {
|
||||
public override void deobfuscateMethodBegin(Blocks blocks) {
|
||||
proxyDelegateFinder.deobfuscate(blocks);
|
||||
base.deobfuscateMethodBegin(blocks);
|
||||
}
|
||||
|
||||
public override void deobfuscateMethodEnd(blocks.Blocks blocks) {
|
||||
public override void deobfuscateMethodEnd(Blocks blocks) {
|
||||
mainType.removeInitCall(blocks);
|
||||
resourceDecrypter.deobfuscate(blocks);
|
||||
base.deobfuscateMethodEnd(blocks);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,15 +20,301 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Cecil.Metadata;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.code.deobfuscators.CodeVeil {
|
||||
class ResourceDecrypter {
|
||||
ModuleDefinition module;
|
||||
TypeDefinition encryptedResourceStreamType;
|
||||
MethodDefinition getManifestResourceStreamMethod1;
|
||||
MethodDefinition getManifestResourceStreamMethod2;
|
||||
TypeDefinition encryptedResourceSetType;
|
||||
MethodDefinition encryptedResourceSet_GetDefaultReader;
|
||||
TypeDefinition encryptedResourceReaderType;
|
||||
GenericInstanceType encryptedResourceReaderTypeDict;
|
||||
TypeDefinition resType;
|
||||
MethodDefinition resTypeCtor;
|
||||
TypeDefinition resourceFlagsType;
|
||||
TypeDefinition resourceEnumeratorType;
|
||||
MethodReference Assembly_GetManifestResourceStream1;
|
||||
MethodReference Assembly_GetManifestResourceStream2;
|
||||
|
||||
public bool CanRemoveTypes {
|
||||
get {
|
||||
return EncryptedResourceStreamType != null &&
|
||||
EncryptedResourceSetType != null &&
|
||||
EncryptedResourceReaderType != null &&
|
||||
ResType != null &&
|
||||
ResourceFlagsType != null &&
|
||||
ResourceEnumeratorType != null;
|
||||
}
|
||||
}
|
||||
|
||||
public TypeDefinition EncryptedResourceStreamType {
|
||||
get { return encryptedResourceStreamType; }
|
||||
}
|
||||
|
||||
public TypeDefinition EncryptedResourceSetType {
|
||||
get { return encryptedResourceSetType; }
|
||||
}
|
||||
|
||||
public TypeDefinition EncryptedResourceReaderType {
|
||||
get { return encryptedResourceReaderType; }
|
||||
}
|
||||
|
||||
public TypeDefinition ResType {
|
||||
get { return resType; }
|
||||
}
|
||||
|
||||
public TypeDefinition ResourceFlagsType {
|
||||
get { return resourceFlagsType; }
|
||||
}
|
||||
|
||||
public TypeDefinition ResourceEnumeratorType {
|
||||
get { return resourceEnumeratorType; }
|
||||
}
|
||||
|
||||
public ResourceDecrypter(ModuleDefinition module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
initGetManifestMethods();
|
||||
findEncryptedResourceStreamType();
|
||||
findEncryptedResourceSet();
|
||||
findEncryptedResourceReader();
|
||||
findResType();
|
||||
findResourceFlags();
|
||||
findResourceEnumerator();
|
||||
}
|
||||
|
||||
void initGetManifestMethods() {
|
||||
var assemblyType = new TypeReference("System.Reflection", "Assembly", module, module.TypeSystem.Corlib);
|
||||
var typeType = new TypeReference("System", "Type", module, module.TypeSystem.Corlib);
|
||||
var streamType = new TypeReference("System.IO", "Stream", module, module.TypeSystem.Corlib);
|
||||
Assembly_GetManifestResourceStream1 = new MethodReference("GetManifestResourceStream", streamType, assemblyType);
|
||||
Assembly_GetManifestResourceStream1.HasThis = true;
|
||||
Assembly_GetManifestResourceStream1.Parameters.Add(new ParameterDefinition(module.TypeSystem.String));
|
||||
Assembly_GetManifestResourceStream2 = new MethodReference("GetManifestResourceStream", streamType, assemblyType);
|
||||
Assembly_GetManifestResourceStream2.HasThis = true;
|
||||
Assembly_GetManifestResourceStream2.Parameters.Add(new ParameterDefinition(typeType));
|
||||
Assembly_GetManifestResourceStream2.Parameters.Add(new ParameterDefinition(module.TypeSystem.String));
|
||||
}
|
||||
|
||||
void findResourceEnumerator() {
|
||||
if (encryptedResourceReaderType == null)
|
||||
return;
|
||||
|
||||
var resourceEnumeratorType_fields = new string[] {
|
||||
"System.Collections.DictionaryEntry",
|
||||
"System.Collections.IDictionaryEnumerator",
|
||||
"System.Boolean",
|
||||
encryptedResourceReaderType.FullName,
|
||||
};
|
||||
foreach (var type in module.Types) {
|
||||
if (type.Namespace != "")
|
||||
continue;
|
||||
if (type.BaseType == null || type.BaseType.EType != ElementType.Object)
|
||||
continue;
|
||||
if (!hasInterface(type, "System.Collections.IDictionaryEnumerator"))
|
||||
continue;
|
||||
if (!new FieldTypes(type).all(resourceEnumeratorType_fields))
|
||||
continue;
|
||||
var ctor = DotNetUtils.getMethod(type, ".ctor");
|
||||
if (ctor == null)
|
||||
continue;
|
||||
if (ctor.Parameters.Count != 1)
|
||||
continue;
|
||||
if (ctor.Parameters[0].ParameterType != encryptedResourceReaderType)
|
||||
continue;
|
||||
|
||||
resourceEnumeratorType = type;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void findResourceFlags() {
|
||||
if (resTypeCtor == null || resTypeCtor.Parameters.Count != 4)
|
||||
return;
|
||||
var type = resTypeCtor.Parameters[2].ParameterType as TypeDefinition;
|
||||
if (type == null || !type.IsEnum)
|
||||
return;
|
||||
|
||||
resourceFlagsType = type;
|
||||
}
|
||||
|
||||
static string[] resType_fields = new string[] {
|
||||
"System.Int32",
|
||||
"System.Object",
|
||||
"System.String",
|
||||
};
|
||||
void findResType() {
|
||||
if (encryptedResourceReaderTypeDict == null)
|
||||
return;
|
||||
var type = encryptedResourceReaderTypeDict.GenericArguments[1] as TypeDefinition;
|
||||
if (type == null)
|
||||
return;
|
||||
if (type.BaseType == null || type.BaseType.EType != ElementType.Object)
|
||||
return;
|
||||
var ctor = DotNetUtils.getMethod(type, ".ctor");
|
||||
if (ctor == null || ctor.Parameters.Count != 4)
|
||||
return;
|
||||
|
||||
resTypeCtor = ctor;
|
||||
resType = type;
|
||||
}
|
||||
|
||||
static string[] encryptedResourceReaderType_fields = new string[] {
|
||||
"System.Boolean",
|
||||
"System.Int32",
|
||||
"System.Int64",
|
||||
"System.IO.BinaryReader",
|
||||
"System.Runtime.Serialization.Formatters.Binary.BinaryFormatter",
|
||||
};
|
||||
void findEncryptedResourceReader() {
|
||||
var type = getTypeFromCode(encryptedResourceSet_GetDefaultReader);
|
||||
if (type == null)
|
||||
return;
|
||||
if (type.BaseType == null || !hasInterface(type, "System.Resources.IResourceReader"))
|
||||
return;
|
||||
if (!new FieldTypes(type).all(encryptedResourceReaderType_fields))
|
||||
return;
|
||||
var dictType = getDlxResDict(type);
|
||||
if (dictType == null)
|
||||
return;
|
||||
if (findXxteaMethod(type) == null)
|
||||
return;
|
||||
|
||||
encryptedResourceReaderType = type;
|
||||
encryptedResourceReaderTypeDict = dictType;
|
||||
}
|
||||
|
||||
static bool hasInterface(TypeDefinition type, string interfaceFullName) {
|
||||
foreach (var iface in type.Interfaces) {
|
||||
if (iface.FullName == interfaceFullName)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static GenericInstanceType getDlxResDict(TypeDefinition type) {
|
||||
foreach (var field in type.Fields) {
|
||||
var fieldType = field.FieldType as GenericInstanceType;
|
||||
if (fieldType == null)
|
||||
continue;
|
||||
if (fieldType.ElementType.FullName != "System.Collections.Generic.Dictionary`2")
|
||||
continue;
|
||||
if (fieldType.GenericArguments.Count != 2)
|
||||
continue;
|
||||
if (fieldType.GenericArguments[0].FullName != "System.String")
|
||||
continue;
|
||||
if (!(fieldType.GenericArguments[1] is TypeDefinition))
|
||||
continue;
|
||||
return fieldType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static TypeDefinition getTypeFromCode(MethodDefinition method) {
|
||||
if (method == null || method.Body == null)
|
||||
return null;
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code != Code.Ldtoken)
|
||||
continue;
|
||||
var type = instr.Operand as TypeDefinition;
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void findEncryptedResourceSet() {
|
||||
foreach (var type in module.Types) {
|
||||
if (type.Namespace != "")
|
||||
continue;
|
||||
if (type.BaseType == null || type.BaseType.FullName != "System.Resources.ResourceSet")
|
||||
continue;
|
||||
var ctor = DotNetUtils.getMethod(type, ".ctor");
|
||||
if (!DotNetUtils.isMethod(ctor, "System.Void", "(System.Resources.IResourceReader)"))
|
||||
continue;
|
||||
var method = DotNetUtils.getMethod(type, "GetDefaultReader");
|
||||
if (!DotNetUtils.isMethod(method, "System.Type", "()"))
|
||||
continue;
|
||||
if (method.Body == null || method.IsStatic || !method.IsVirtual)
|
||||
continue;
|
||||
|
||||
encryptedResourceSet_GetDefaultReader = method;
|
||||
encryptedResourceSetType = type;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static string[] encryptedResourceStreamType_fields = new string[] {
|
||||
"System.Byte",
|
||||
"System.Byte[]",
|
||||
"System.Boolean",
|
||||
"System.Int32",
|
||||
"System.Int64",
|
||||
"System.IO.MemoryStream",
|
||||
"System.IO.Stream",
|
||||
"System.UInt32[]",
|
||||
};
|
||||
void findEncryptedResourceStreamType() {
|
||||
foreach (var type in module.Types) {
|
||||
if (type.Namespace != "")
|
||||
continue;
|
||||
if (type.BaseType == null || type.BaseType.FullName != "System.IO.Stream")
|
||||
continue;
|
||||
var ctor = DotNetUtils.getMethod(type, ".ctor");
|
||||
if (!DotNetUtils.isMethod(ctor, "System.Void", "(System.IO.Stream)"))
|
||||
continue;
|
||||
if (!new FieldTypes(type).all(encryptedResourceStreamType_fields))
|
||||
continue;
|
||||
if (findXxteaMethod(type) == null)
|
||||
continue;
|
||||
|
||||
MethodDefinition getManifestResourceStreamMethodTmp1, getManifestResourceStreamMethodTmp2;
|
||||
if (!findManifestResourceStreamMethods(type, out getManifestResourceStreamMethodTmp1, out getManifestResourceStreamMethodTmp2))
|
||||
continue;
|
||||
|
||||
getManifestResourceStreamMethod1 = getManifestResourceStreamMethodTmp1;
|
||||
getManifestResourceStreamMethod2 = getManifestResourceStreamMethodTmp2;
|
||||
encryptedResourceStreamType = type;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static MethodDefinition findXxteaMethod(TypeDefinition type) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (!method.IsPrivate || method.IsStatic || method.Body == null)
|
||||
continue;
|
||||
if (!DotNetUtils.isMethod(method, "System.Void", "(System.UInt32[],System.UInt32[])"))
|
||||
continue;
|
||||
if (!DeobUtils.hasInteger(method, 0x9E3779B9))
|
||||
continue;
|
||||
if (!DeobUtils.hasInteger(method, 52))
|
||||
continue;
|
||||
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool findManifestResourceStreamMethods(TypeDefinition type, out MethodDefinition getManifestResourceStreamMethodTmp1, out MethodDefinition getManifestResourceStreamMethodTmp2) {
|
||||
getManifestResourceStreamMethodTmp1 = null;
|
||||
getManifestResourceStreamMethodTmp2 = null;
|
||||
foreach (var method in type.Methods) {
|
||||
if (DotNetUtils.isMethod(method, "System.IO.Stream", "(System.Reflection.Assembly,System.String)"))
|
||||
getManifestResourceStreamMethodTmp1 = method;
|
||||
else if (DotNetUtils.isMethod(method, "System.IO.Stream", "(System.Reflection.Assembly,System.Type,System.String)"))
|
||||
getManifestResourceStreamMethodTmp2 = method;
|
||||
}
|
||||
return getManifestResourceStreamMethodTmp1 != null && getManifestResourceStreamMethodTmp2 != null;
|
||||
}
|
||||
|
||||
public void decrypt() {
|
||||
for (int i = 0; i < module.Resources.Count; i++) {
|
||||
var resource = module.Resources[i] as EmbeddedResource;
|
||||
|
@ -61,7 +347,7 @@ namespace de4dot.code.deobfuscators.CodeVeil {
|
|||
return null;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Log.w("Got an exception when decrypting resources: {0}", ex.GetType());
|
||||
Log.w("Got an exception when decrypting resources: {0} - {1}", ex.GetType(), ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -70,5 +356,29 @@ namespace de4dot.code.deobfuscators.CodeVeil {
|
|||
var resourceReader = new ResourceReader(reader);
|
||||
return new ResourceConverter(module, resourceReader.read()).convert();
|
||||
}
|
||||
|
||||
public void deobfuscate(Blocks blocks) {
|
||||
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
|
||||
var instrs = block.Instructions;
|
||||
for (int i = 0; i < instrs.Count; i++) {
|
||||
var call = instrs[i];
|
||||
if (call.OpCode.Code != Code.Call)
|
||||
continue;
|
||||
var calledMethod = call.Operand as MethodDefinition;
|
||||
if (calledMethod == null)
|
||||
continue;
|
||||
|
||||
MethodReference newMethod = null;
|
||||
if (calledMethod == getManifestResourceStreamMethod1)
|
||||
newMethod = Assembly_GetManifestResourceStream1;
|
||||
else if (calledMethod == getManifestResourceStreamMethod2)
|
||||
newMethod = Assembly_GetManifestResourceStream2;
|
||||
if (newMethod == null)
|
||||
continue;
|
||||
|
||||
instrs[i] = new Instr(Instruction.Create(OpCodes.Callvirt, newMethod));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user