de4dot-cex/de4dot.code/deobfuscators/Eazfuscator_NET/AssemblyResolver.cs
2015-10-29 22:45:26 +01:00

495 lines
16 KiB
C#

/*
Copyright (C) 2011-2015 de4dot@gmail.com
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Eazfuscator_NET {
class AssemblyResolver {
ModuleDefMD module;
DecrypterType decrypterType;
TypeDef resolverType;
MethodDef initMethod;
MethodDef handlerMethod;
MethodDef decryptMethod;
TypeDef otherType;
List<AssemblyInfo> assemblyInfos = new List<AssemblyInfo>();
FrameworkType frameworkType;
byte[] decryptKey;
CodeCompilerMethodCallRestorer codeCompilerMethodCallRestorer;
public class AssemblyInfo {
public bool IsEncrypted { get; set; }
public bool IsCompressed { get; set; }
public string ResourceName { get; set; }
public string Filename { get; set; }
public string AssemblyFullName { get; set; }
public string SimpleName { get; set; }
public string Extension { get; set; }
public EmbeddedResource Resource { get; set; }
public byte[] Data { get; set; }
public override string ToString() {
return AssemblyFullName ?? Filename;
}
}
public TypeDef Type {
get { return resolverType; }
}
public TypeDef OtherType {
get { return otherType; }
}
public MethodDef InitMethod {
get { return initMethod; }
}
public IEnumerable<AssemblyInfo> AssemblyInfos {
get { return assemblyInfos; }
}
public bool Detected {
get { return resolverType != null; }
}
public AssemblyResolver(ModuleDefMD module, DecrypterType decrypterType) {
this.module = module;
this.frameworkType = DotNetUtils.GetFrameworkType(module);
this.decrypterType = decrypterType;
this.codeCompilerMethodCallRestorer = new CodeCompilerMethodCallRestorer(module);
}
public void Find() {
CheckCalledMethods(DotNetUtils.GetModuleTypeCctor(module));
}
bool CheckCalledMethods(MethodDef method) {
if (method == null || method.Body == null)
return false;
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code != Code.Call)
continue;
var calledMethod = instr.Operand as MethodDef;
if (calledMethod == null || !calledMethod.IsStatic || calledMethod.Body == null)
continue;
if (!DotNetUtils.IsMethod(calledMethod, "System.Void", "()"))
continue;
if (frameworkType == FrameworkType.Silverlight) {
if (!CheckInitMethodSilverlight(calledMethod))
continue;
}
else {
if (!CheckInitMethod(calledMethod))
continue;
}
decryptMethod = GetDecryptMethod();
UpdateDecrypterType();
FindCodeDomMethods();
return true;
}
return false;
}
bool CheckInitMethodSilverlight(MethodDef method) {
var type = method.DeclaringType;
if (type.NestedTypes.Count != 2)
return false;
var resolveHandler = GetResolveMethodSilverlight(method);
if (resolveHandler == null)
return false;
initMethod = method;
resolverType = type;
handlerMethod = resolveHandler;
return true;
}
static MethodDef GetResolveMethodSilverlight(MethodDef initMethod) {
foreach (var instr in initMethod.Body.Instructions) {
if (instr.OpCode.Code != Code.Call)
continue;
var calledMethod = instr.Operand as MethodDef;
if (calledMethod == null)
continue;
if (!DotNetUtils.IsMethod(calledMethod, "System.Void", "()"))
continue;
if (!DeobUtils.HasInteger(calledMethod, ',') ||
!DeobUtils.HasInteger(calledMethod, '|'))
continue;
return calledMethod;
}
return null;
}
bool CheckInitMethod(MethodDef method) {
var type = method.DeclaringType;
if (type.NestedTypes.Count < 2 || type.NestedTypes.Count > 6)
return false;
if (DotNetUtils.GetPInvokeMethod(type, "kernel32", "MoveFileEx") == null)
return false;
var resolveHandler = DeobUtils.GetResolveMethod(method);
if (resolveHandler == null)
return false;
if (!DeobUtils.HasInteger(resolveHandler, ',') ||
!DeobUtils.HasInteger(resolveHandler, '|'))
return false;
initMethod = method;
resolverType = type;
handlerMethod = resolveHandler;
return true;
}
MethodDef GetDecryptMethod() {
foreach (var method in resolverType.Methods) {
if (!method.IsStatic || method.Body == null)
continue;
if (!DotNetUtils.IsMethod(method, "System.Byte[]", "(System.Byte[])"))
continue;
if (!DeobUtils.HasInteger(method, 32) ||
!DeobUtils.HasInteger(method, 121))
continue;
return method;
}
return null;
}
void UpdateDecrypterType() {
var theDecrypterType = GetDecrypterType(decryptMethod);
if (theDecrypterType == null)
return;
decrypterType.Type = theDecrypterType;
if (!decrypterType.Initialize())
throw new ApplicationException("Could not initialize decrypterType");
}
TypeDef GetDecrypterType(MethodDef method) {
if (method == null || method.Body == null)
return null;
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code != Code.Call)
continue;
var calledMethod = instr.Operand as MethodDef;
if (calledMethod == null || !calledMethod.IsStatic || calledMethod.DeclaringType == resolverType)
continue;
if (!DotNetUtils.IsMethod(calledMethod, "System.Void", "(System.Byte[])"))
continue;
return calledMethod.DeclaringType;
}
return null;
}
public void Initialize(ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob) {
if (handlerMethod == null)
return;
FindOtherType();
simpleDeobfuscator.Deobfuscate(handlerMethod);
simpleDeobfuscator.DecryptStrings(handlerMethod, deob);
if (!CreateAssemblyInfos())
throw new ApplicationException("Could not initialize assembly infos");
if (decryptMethod != null) {
simpleDeobfuscator.Deobfuscate(decryptMethod);
simpleDeobfuscator.DecryptStrings(decryptMethod, deob);
if (!CreateDecryptKey())
throw new ApplicationException("Could not initialize decryption key");
}
}
void FindOtherType() {
foreach (var type in module.Types) {
// This type is added by EF 3.1+. The last number seems to be an int32 hash of
// the assembly name, but - replaced with _.
if (!Regex.IsMatch(type.FullName, @"^pc1eOx2WJVV[_0-9]+$"))
continue;
otherType = type;
break;
}
}
bool CreateDecryptKey() {
if (decryptMethod == null)
return false;
foreach (var s in DotNetUtils.GetCodeStrings(decryptMethod)) {
decryptKey = DecodeBase64(s);
if (decryptKey == null || decryptKey.Length == 0)
continue;
if (decrypterType.Detected) {
var data = new byte[8];
ulong magic = decrypterType.GetMagic();
data[0] = (byte)magic;
data[7] = (byte)(magic >> 8);
data[6] = (byte)(magic >> 16);
data[5] = (byte)(magic >> 24);
data[4] = (byte)(magic >> 32);
data[1] = (byte)(magic >> 40);
data[3] = (byte)(magic >> 48);
data[2] = (byte)(magic >> 56);
for (int i = 0; i < decryptKey.Length; i++)
decryptKey[i] ^= (byte)(i + data[i % data.Length]);
}
return true;
}
decryptKey = null;
return false;
}
static byte[] DecodeBase64(string s) {
try {
return Convert.FromBase64String(s);
}
catch (FormatException) {
return null;
}
}
bool CreateAssemblyInfos() {
int numElements = DeobUtils.HasInteger(handlerMethod, 3) ? 3 : 2;
foreach (var s in DotNetUtils.GetCodeStrings(handlerMethod)) {
var infos = CreateAssemblyInfos(s, numElements);
if (infos == null)
continue;
assemblyInfos = infos;
return true;
}
return false;
}
List<AssemblyInfo> CreateAssemblyInfos(string s, int numElements) {
try {
return TryCreateAssemblyInfos(s, numElements);
}
catch (FormatException) {
return null; // Convert.FromBase64String() failed
}
}
List<AssemblyInfo> TryCreateAssemblyInfos(string s, int numElements) {
var ary = s.Split(',');
if (ary.Length == 0 || ary.Length % numElements != 0)
return null;
var infos = new List<AssemblyInfo>();
for (int i = 0; i < ary.Length; i += numElements) {
var info = new AssemblyInfo();
info.AssemblyFullName = Encoding.UTF8.GetString(Convert.FromBase64String(ary[i]));
info.ResourceName = ary[i + 1];
if (numElements >= 3)
info.Filename = Encoding.UTF8.GetString(Convert.FromBase64String(ary[i + 2]));
else
info.Filename = Utils.GetAssemblySimpleName(info.AssemblyFullName) + ".dll";
int index = info.ResourceName.IndexOf('|');
if (index >= 0) {
var flags = info.ResourceName.Substring(0, index);
info.ResourceName = info.ResourceName.Substring(index + 1);
info.IsEncrypted = flags.IndexOf('a') >= 0;
info.IsCompressed = flags.IndexOf('b') >= 0;
}
infos.Add(info);
}
return infos;
}
public void InitializeEmbeddedFiles() {
foreach (var info in assemblyInfos) {
info.Resource = DotNetUtils.GetResource(module, info.ResourceName) as EmbeddedResource;
if (info.Resource == null)
throw new ApplicationException(string.Format("Could not find resource {0}", Utils.ToCsharpString(info.ResourceName)));
info.Data = info.Resource.GetResourceData();
if (info.IsEncrypted)
Decrypt(info.Data);
if (info.IsCompressed)
info.Data = Decompress(info.Data);
InitializeNameAndExtension(info);
}
}
static void InitializeNameAndExtension(AssemblyInfo info) {
try {
var mod = ModuleDefMD.Load(info.Data);
info.AssemblyFullName = mod.Assembly.FullName;
info.SimpleName = mod.Assembly.Name.String;
info.Extension = DeobUtils.GetExtension(mod.Kind);
return;
}
catch {
}
Logger.w("Could not load assembly from decrypted resource {0}", Utils.ToCsharpString(info.ResourceName));
int index = info.Filename.LastIndexOf('.');
if (index < 0) {
info.SimpleName = info.Filename;
info.Extension = "";
}
else {
info.SimpleName = info.Filename.Substring(0, index);
info.Extension = info.Filename.Substring(index);
}
}
static readonly byte[] key2 = new byte[] { 148, 68, 208, 52 };
void Decrypt(byte[] encryptedData) {
var indexes = new byte[256];
for (int i = 0; i < indexes.Length; i++)
indexes[i] = (byte)i;
byte i1 = 0, i2 = 0;
for (int i = 0; i < indexes.Length; i++) {
i2 += (byte)(decryptKey[i % decryptKey.Length] + indexes[i]);
Swap(indexes, i, i2);
}
byte val = 0;
for (int i = 0; i < encryptedData.Length; i++) {
if ((i & 0x1F) == 0) {
i2 += indexes[++i1];
Swap(indexes, i1, i2);
val = indexes[(byte)(indexes[i1] + indexes[i2])];
}
encryptedData[i] ^= (byte)(val ^ key2[(i >> 2) & 3] ^ key2[(i + 1) & 3]);
}
}
static void Swap(byte[] data, int i, int j) {
byte tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
byte[] Decompress(byte[] compressedData) {
// First dword is sig: 0x9B728BC7
// Second dword is decompressed length
return DeobUtils.Inflate(compressedData, 8, compressedData.Length - 8, true);
}
public AssemblyInfo Get(string asmFullName) {
var simpleName = Utils.GetAssemblySimpleName(asmFullName);
for (int i = 0; i < assemblyInfos.Count; i++) {
var info = assemblyInfos[i];
if (info.SimpleName != simpleName)
continue;
assemblyInfos.RemoveAt(i);
return info;
}
return null;
}
public void Deobfuscate(Blocks blocks) {
codeCompilerMethodCallRestorer.Deobfuscate(blocks);
}
void FindCodeDomMethods() {
if (resolverType == null)
return;
foreach (var nestedType in resolverType.NestedTypes) {
if (nestedType.Fields.Count != 0)
continue;
var CompileAssemblyFromDom1 = GetTheOnlyMethod(nestedType, "System.CodeDom.Compiler.CodeDomProvider", "CompileAssemblyFromDom", "System.CodeDom.Compiler.CompilerResults", "System.CodeDom.Compiler.CompilerParameters,System.CodeDom.CodeCompileUnit[]");
var CompileAssemblyFromFile1 = GetTheOnlyMethod(nestedType, "System.CodeDom.Compiler.CodeDomProvider", "CompileAssemblyFromFile", "System.CodeDom.Compiler.CompilerResults", "System.CodeDom.Compiler.CompilerParameters,System.String[]");
var CompileAssemblyFromSource1 = GetTheOnlyMethod(nestedType, "System.CodeDom.Compiler.CodeDomProvider", "CompileAssemblyFromSource", "System.CodeDom.Compiler.CompilerResults", "System.CodeDom.Compiler.CompilerParameters,System.String[]");
var CompileAssemblyFromDom2 = GetTheOnlyMethod(nestedType, "System.CodeDom.Compiler.ICodeCompiler", "CompileAssemblyFromDom", "System.CodeDom.Compiler.CompilerResults", "System.CodeDom.Compiler.CompilerParameters,System.CodeDom.CodeCompileUnit");
var CompileAssemblyFromDomBatch2 = GetTheOnlyMethod(nestedType, "System.CodeDom.Compiler.ICodeCompiler", "CompileAssemblyFromDomBatch", "System.CodeDom.Compiler.CompilerResults", "System.CodeDom.Compiler.CompilerParameters,System.CodeDom.CodeCompileUnit[]");
var CompileAssemblyFromFile2 = GetTheOnlyMethod(nestedType, "System.CodeDom.Compiler.ICodeCompiler", "CompileAssemblyFromFile", "System.CodeDom.Compiler.CompilerResults", "System.CodeDom.Compiler.CompilerParameters,System.String");
var CompileAssemblyFromFileBatch2 = GetTheOnlyMethod(nestedType, "System.CodeDom.Compiler.ICodeCompiler", "CompileAssemblyFromFileBatch", "System.CodeDom.Compiler.CompilerResults", "System.CodeDom.Compiler.CompilerParameters,System.String[]");
var CompileAssemblyFromSource2 = GetTheOnlyMethod(nestedType, "System.CodeDom.Compiler.ICodeCompiler", "CompileAssemblyFromSource", "System.CodeDom.Compiler.CompilerResults", "System.CodeDom.Compiler.CompilerParameters,System.String");
var CompileAssemblyFromSourceBatch2 = GetTheOnlyMethod(nestedType, "System.CodeDom.Compiler.ICodeCompiler", "CompileAssemblyFromSourceBatch", "System.CodeDom.Compiler.CompilerResults", "System.CodeDom.Compiler.CompilerParameters,System.String[]");
if (CompileAssemblyFromDom1 == null && CompileAssemblyFromFile1 == null &&
CompileAssemblyFromSource1 == null && CompileAssemblyFromDom2 == null &&
CompileAssemblyFromDomBatch2 == null && CompileAssemblyFromFile2 == null &&
CompileAssemblyFromFileBatch2 == null && CompileAssemblyFromSource2 == null &&
CompileAssemblyFromSourceBatch2 == null) {
continue;
}
codeCompilerMethodCallRestorer.Add_CodeDomProvider_CompileAssemblyFromDom(CompileAssemblyFromDom1);
codeCompilerMethodCallRestorer.Add_CodeDomProvider_CompileAssemblyFromFile(CompileAssemblyFromFile1);
codeCompilerMethodCallRestorer.Add_CodeDomProvider_CompileAssemblyFromSource(CompileAssemblyFromSource1);
codeCompilerMethodCallRestorer.Add_ICodeCompiler_CompileAssemblyFromDom(CompileAssemblyFromDom2);
codeCompilerMethodCallRestorer.Add_ICodeCompiler_CompileAssemblyFromDomBatch(CompileAssemblyFromDomBatch2);
codeCompilerMethodCallRestorer.Add_ICodeCompiler_CompileAssemblyFromFile(CompileAssemblyFromFile2);
codeCompilerMethodCallRestorer.Add_ICodeCompiler_CompileAssemblyFromFileBatch(CompileAssemblyFromFileBatch2);
codeCompilerMethodCallRestorer.Add_ICodeCompiler_CompileAssemblyFromSource(CompileAssemblyFromSource2);
codeCompilerMethodCallRestorer.Add_ICodeCompiler_CompileAssemblyFromSourceBatch(CompileAssemblyFromSourceBatch2);
break;
}
}
static MethodDef GetTheOnlyMethod(TypeDef type, string typeName, string methodName, string returnType, string parameters) {
MethodDef foundMethod = null;
foreach (var method in type.Methods) {
if (!method.IsStatic || method.Body == null || method.HasGenericParameters)
continue;
if (method.IsPrivate)
continue;
if (!DotNetUtils.IsMethod(method, returnType, "(" + typeName + "," + parameters + ")"))
continue;
if (!DotNetUtils.CallsMethod(method, returnType + " " + typeName + "::" + methodName + "(" + parameters + ")"))
continue;
if (foundMethod != null)
return null;
foundMethod = method;
}
return foundMethod;
}
}
}