2012-02-28 06:43:45 +08:00
|
|
|
|
/*
|
2014-03-12 05:15:43 +08:00
|
|
|
|
Copyright (C) 2011-2014 de4dot@gmail.com
|
2012-02-28 06:43:45 +08:00
|
|
|
|
|
|
|
|
|
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;
|
2012-02-29 03:44:54 +08:00
|
|
|
|
using System.Text.RegularExpressions;
|
2012-12-20 09:06:09 +08:00
|
|
|
|
using dnlib.DotNet;
|
|
|
|
|
using dnlib.DotNet.Emit;
|
2012-02-28 06:43:45 +08:00
|
|
|
|
using de4dot.blocks;
|
|
|
|
|
|
|
|
|
|
namespace de4dot.code.deobfuscators.Eazfuscator_NET {
|
|
|
|
|
class AssemblyResolver {
|
2012-11-18 08:09:07 +08:00
|
|
|
|
ModuleDefMD module;
|
2012-02-28 06:43:45 +08:00
|
|
|
|
DecrypterType decrypterType;
|
2012-11-02 22:57:11 +08:00
|
|
|
|
TypeDef resolverType;
|
|
|
|
|
MethodDef initMethod;
|
|
|
|
|
MethodDef handlerMethod;
|
|
|
|
|
MethodDef decryptMethod;
|
|
|
|
|
TypeDef otherType;
|
2012-02-28 06:43:45 +08:00
|
|
|
|
List<AssemblyInfo> assemblyInfos = new List<AssemblyInfo>();
|
2012-03-17 06:22:24 +08:00
|
|
|
|
FrameworkType frameworkType;
|
2012-02-28 06:43:45 +08:00
|
|
|
|
byte[] decryptKey;
|
2012-04-29 10:03:10 +08:00
|
|
|
|
CodeCompilerMethodCallRestorer codeCompilerMethodCallRestorer;
|
2012-02-28 06:43:45 +08:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
public TypeDef Type {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
get { return resolverType; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
public TypeDef OtherType {
|
2012-02-29 03:44:54 +08:00
|
|
|
|
get { return otherType; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
public MethodDef InitMethod {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
get { return initMethod; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerable<AssemblyInfo> AssemblyInfos {
|
|
|
|
|
get { return assemblyInfos; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Detected {
|
|
|
|
|
get { return resolverType != null; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-18 08:09:07 +08:00
|
|
|
|
public AssemblyResolver(ModuleDefMD module, DecrypterType decrypterType) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
this.module = module;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
this.frameworkType = DotNetUtils.GetFrameworkType(module);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
this.decrypterType = decrypterType;
|
2012-04-29 10:03:10 +08:00
|
|
|
|
this.codeCompilerMethodCallRestorer = new CodeCompilerMethodCallRestorer(module);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void Find() {
|
|
|
|
|
CheckCalledMethods(DotNetUtils.GetModuleTypeCctor(module));
|
2012-02-28 06:43:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
bool CheckCalledMethods(MethodDef method) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (method == null || method.Body == null)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
foreach (var instr in method.Body.Instructions) {
|
|
|
|
|
if (instr.OpCode.Code != Code.Call)
|
|
|
|
|
continue;
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
var calledMethod = instr.Operand as MethodDef;
|
2012-03-17 06:22:24 +08:00
|
|
|
|
if (calledMethod == null || !calledMethod.IsStatic || calledMethod.Body == null)
|
2012-03-17 18:18:52 +08:00
|
|
|
|
continue;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!DotNetUtils.IsMethod(calledMethod, "System.Void", "()"))
|
2012-03-17 18:18:52 +08:00
|
|
|
|
continue;
|
2012-03-17 06:22:24 +08:00
|
|
|
|
|
|
|
|
|
if (frameworkType == FrameworkType.Silverlight) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!CheckInitMethodSilverlight(calledMethod))
|
2012-03-17 06:22:24 +08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!CheckInitMethod(calledMethod))
|
2012-03-17 06:22:24 +08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
decryptMethod = GetDecryptMethod();
|
|
|
|
|
UpdateDecrypterType();
|
|
|
|
|
FindCodeDomMethods();
|
2012-02-28 06:43:45 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
bool CheckInitMethodSilverlight(MethodDef method) {
|
2012-03-17 06:22:24 +08:00
|
|
|
|
var type = method.DeclaringType;
|
|
|
|
|
if (type.NestedTypes.Count != 2)
|
2012-02-28 06:43:45 +08:00
|
|
|
|
return false;
|
2012-03-17 06:22:24 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
var resolveHandler = GetResolveMethodSilverlight(method);
|
2012-03-17 06:22:24 +08:00
|
|
|
|
if (resolveHandler == null)
|
2012-02-28 06:43:45 +08:00
|
|
|
|
return false;
|
2012-03-17 06:22:24 +08:00
|
|
|
|
|
|
|
|
|
initMethod = method;
|
|
|
|
|
resolverType = type;
|
|
|
|
|
handlerMethod = resolveHandler;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
static MethodDef GetResolveMethodSilverlight(MethodDef initMethod) {
|
2012-03-17 06:22:24 +08:00
|
|
|
|
foreach (var instr in initMethod.Body.Instructions) {
|
|
|
|
|
if (instr.OpCode.Code != Code.Call)
|
|
|
|
|
continue;
|
2012-11-02 22:57:11 +08:00
|
|
|
|
var calledMethod = instr.Operand as MethodDef;
|
2012-03-17 06:22:24 +08:00
|
|
|
|
if (calledMethod == null)
|
|
|
|
|
continue;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!DotNetUtils.IsMethod(calledMethod, "System.Void", "()"))
|
2012-03-17 06:22:24 +08:00
|
|
|
|
continue;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!DeobUtils.HasInteger(calledMethod, ',') ||
|
|
|
|
|
!DeobUtils.HasInteger(calledMethod, '|'))
|
2012-03-17 06:22:24 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return calledMethod;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
bool CheckInitMethod(MethodDef method) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
var type = method.DeclaringType;
|
2012-04-29 10:03:10 +08:00
|
|
|
|
if (type.NestedTypes.Count < 2 || type.NestedTypes.Count > 6)
|
2012-02-28 06:43:45 +08:00
|
|
|
|
return false;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (DotNetUtils.GetPInvokeMethod(type, "kernel32", "MoveFileEx") == null)
|
2012-02-28 06:43:45 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
var resolveHandler = DeobUtils.GetResolveMethod(method);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (resolveHandler == null)
|
|
|
|
|
return false;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!DeobUtils.HasInteger(resolveHandler, ',') ||
|
|
|
|
|
!DeobUtils.HasInteger(resolveHandler, '|'))
|
2012-02-28 06:43:45 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
initMethod = method;
|
|
|
|
|
resolverType = type;
|
|
|
|
|
handlerMethod = resolveHandler;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
MethodDef GetDecryptMethod() {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
foreach (var method in resolverType.Methods) {
|
|
|
|
|
if (!method.IsStatic || method.Body == null)
|
|
|
|
|
continue;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!DotNetUtils.IsMethod(method, "System.Byte[]", "(System.Byte[])"))
|
2012-02-28 06:43:45 +08:00
|
|
|
|
continue;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!DeobUtils.HasInteger(method, 32) ||
|
|
|
|
|
!DeobUtils.HasInteger(method, 121))
|
2012-02-28 06:43:45 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return method;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-24 14:07:05 +08:00
|
|
|
|
return null;
|
2012-02-28 06:43:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void UpdateDecrypterType() {
|
|
|
|
|
var theDecrypterType = GetDecrypterType(decryptMethod);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (theDecrypterType == null)
|
|
|
|
|
return;
|
|
|
|
|
decrypterType.Type = theDecrypterType;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!decrypterType.Initialize())
|
2012-02-28 06:43:45 +08:00
|
|
|
|
throw new ApplicationException("Could not initialize decrypterType");
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
TypeDef GetDecrypterType(MethodDef method) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (method == null || method.Body == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
foreach (var instr in method.Body.Instructions) {
|
|
|
|
|
if (instr.OpCode.Code != Code.Call)
|
|
|
|
|
continue;
|
2012-11-02 22:57:11 +08:00
|
|
|
|
var calledMethod = instr.Operand as MethodDef;
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (calledMethod == null || !calledMethod.IsStatic || calledMethod.DeclaringType == resolverType)
|
|
|
|
|
continue;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!DotNetUtils.IsMethod(calledMethod, "System.Void", "(System.Byte[])"))
|
2012-02-28 06:43:45 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return calledMethod.DeclaringType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void Initialize(ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (handlerMethod == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
FindOtherType();
|
2012-02-29 03:44:54 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
simpleDeobfuscator.Deobfuscate(handlerMethod);
|
|
|
|
|
simpleDeobfuscator.DecryptStrings(handlerMethod, deob);
|
|
|
|
|
if (!CreateAssemblyInfos())
|
2012-02-28 06:43:45 +08:00
|
|
|
|
throw new ApplicationException("Could not initialize assembly infos");
|
|
|
|
|
|
2013-11-24 14:07:05 +08:00
|
|
|
|
if (decryptMethod != null) {
|
|
|
|
|
simpleDeobfuscator.Deobfuscate(decryptMethod);
|
|
|
|
|
simpleDeobfuscator.DecryptStrings(decryptMethod, deob);
|
|
|
|
|
if (!CreateDecryptKey())
|
|
|
|
|
throw new ApplicationException("Could not initialize decryption key");
|
|
|
|
|
}
|
2012-02-28 06:43:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void FindOtherType() {
|
2012-02-29 03:44:54 +08:00
|
|
|
|
foreach (var type in module.Types) {
|
2012-02-29 10:24:23 +08:00
|
|
|
|
// This type is added by EF 3.1+. The last number seems to be an int32 hash of
|
2012-02-29 03:44:54 +08:00
|
|
|
|
// the assembly name, but - replaced with _.
|
|
|
|
|
if (!Regex.IsMatch(type.FullName, @"^pc1eOx2WJVV[_0-9]+$"))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
otherType = type;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
bool CreateDecryptKey() {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (decryptMethod == null)
|
|
|
|
|
return false;
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
foreach (var s in DotNetUtils.GetCodeStrings(decryptMethod)) {
|
|
|
|
|
decryptKey = DecodeBase64(s);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (decryptKey == null || decryptKey.Length == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (decrypterType.Detected) {
|
|
|
|
|
var data = new byte[8];
|
2013-01-19 20:03:57 +08:00
|
|
|
|
ulong magic = decrypterType.GetMagic();
|
2012-02-28 06:43:45 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-24 14:07:05 +08:00
|
|
|
|
decryptKey = null;
|
2012-02-28 06:43:45 +08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
static byte[] DecodeBase64(string s) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
try {
|
|
|
|
|
return Convert.FromBase64String(s);
|
|
|
|
|
}
|
|
|
|
|
catch (FormatException) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
bool CreateAssemblyInfos() {
|
|
|
|
|
int numElements = DeobUtils.HasInteger(handlerMethod, 3) ? 3 : 2;
|
|
|
|
|
foreach (var s in DotNetUtils.GetCodeStrings(handlerMethod)) {
|
|
|
|
|
var infos = CreateAssemblyInfos(s, numElements);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (infos == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
assemblyInfos = infos;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
List<AssemblyInfo> CreateAssemblyInfos(string s, int numElements) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
try {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
return TryCreateAssemblyInfos(s, numElements);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
}
|
|
|
|
|
catch (FormatException) {
|
|
|
|
|
return null; // Convert.FromBase64String() failed
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
List<AssemblyInfo> TryCreateAssemblyInfos(string s, int numElements) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
var ary = s.Split(',');
|
2012-02-29 02:42:19 +08:00
|
|
|
|
if (ary.Length == 0 || ary.Length % numElements != 0)
|
2012-02-28 06:43:45 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
var infos = new List<AssemblyInfo>();
|
2012-02-29 02:42:19 +08:00
|
|
|
|
for (int i = 0; i < ary.Length; i += numElements) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
var info = new AssemblyInfo();
|
|
|
|
|
|
|
|
|
|
info.AssemblyFullName = Encoding.UTF8.GetString(Convert.FromBase64String(ary[i]));
|
|
|
|
|
info.ResourceName = ary[i + 1];
|
2012-02-29 02:42:19 +08:00
|
|
|
|
if (numElements >= 3)
|
|
|
|
|
info.Filename = Encoding.UTF8.GetString(Convert.FromBase64String(ary[i + 2]));
|
|
|
|
|
else
|
2013-01-19 20:03:57 +08:00
|
|
|
|
info.Filename = Utils.GetAssemblySimpleName(info.AssemblyFullName) + ".dll";
|
2012-02-28 06:43:45 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void InitializeEmbeddedFiles() {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
foreach (var info in assemblyInfos) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
info.Resource = DotNetUtils.GetResource(module, info.ResourceName) as EmbeddedResource;
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (info.Resource == null)
|
2013-01-19 20:03:57 +08:00
|
|
|
|
throw new ApplicationException(string.Format("Could not find resource {0}", Utils.ToCsharpString(info.ResourceName)));
|
2012-02-28 06:43:45 +08:00
|
|
|
|
|
|
|
|
|
info.Data = info.Resource.GetResourceData();
|
|
|
|
|
if (info.IsEncrypted)
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Decrypt(info.Data);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
if (info.IsCompressed)
|
2013-01-19 20:03:57 +08:00
|
|
|
|
info.Data = Decompress(info.Data);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
InitializeNameAndExtension(info);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
static void InitializeNameAndExtension(AssemblyInfo info) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
try {
|
2012-11-18 08:09:07 +08:00
|
|
|
|
var mod = ModuleDefMD.Load(info.Data);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
info.AssemblyFullName = mod.Assembly.FullName;
|
2012-11-18 08:09:07 +08:00
|
|
|
|
info.SimpleName = mod.Assembly.Name.String;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
info.Extension = DeobUtils.GetExtension(mod.Kind);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
}
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Logger.w("Could not load assembly from decrypted resource {0}", Utils.ToCsharpString(info.ResourceName));
|
2012-02-28 06:43:45 +08:00
|
|
|
|
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 };
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void Decrypt(byte[] encryptedData) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
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]);
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Swap(indexes, i, i2);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte val = 0;
|
|
|
|
|
for (int i = 0; i < encryptedData.Length; i++) {
|
|
|
|
|
if ((i & 0x1F) == 0) {
|
|
|
|
|
i2 += indexes[++i1];
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Swap(indexes, i1, i2);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
val = indexes[(byte)(indexes[i1] + indexes[i2])];
|
|
|
|
|
}
|
|
|
|
|
encryptedData[i] ^= (byte)(val ^ key2[(i >> 2) & 3] ^ key2[(i + 1) & 3]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
static void Swap(byte[] data, int i, int j) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
byte tmp = data[i];
|
|
|
|
|
data[i] = data[j];
|
|
|
|
|
data[j] = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
byte[] Decompress(byte[] compressedData) {
|
2012-02-28 06:43:45 +08:00
|
|
|
|
// First dword is sig: 0x9B728BC7
|
|
|
|
|
// Second dword is decompressed length
|
2013-01-19 20:03:57 +08:00
|
|
|
|
return DeobUtils.Inflate(compressedData, 8, compressedData.Length - 8, true);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public AssemblyInfo Get(string asmFullName) {
|
|
|
|
|
var simpleName = Utils.GetAssemblySimpleName(asmFullName);
|
2012-02-28 06:43:45 +08:00
|
|
|
|
for (int i = 0; i < assemblyInfos.Count; i++) {
|
|
|
|
|
var info = assemblyInfos[i];
|
|
|
|
|
if (info.SimpleName != simpleName)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
assemblyInfos.RemoveAt(i);
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2012-04-29 10:03:10 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void Deobfuscate(Blocks blocks) {
|
|
|
|
|
codeCompilerMethodCallRestorer.Deobfuscate(blocks);
|
2012-04-29 10:03:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void FindCodeDomMethods() {
|
2012-04-29 10:03:10 +08:00
|
|
|
|
if (resolverType == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
foreach (var nestedType in resolverType.NestedTypes) {
|
|
|
|
|
if (nestedType.Fields.Count != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
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[]");
|
2012-04-29 10:03:10 +08:00
|
|
|
|
|
|
|
|
|
if (CompileAssemblyFromDom1 == null && CompileAssemblyFromFile1 == null &&
|
|
|
|
|
CompileAssemblyFromSource1 == null && CompileAssemblyFromDom2 == null &&
|
|
|
|
|
CompileAssemblyFromDomBatch2 == null && CompileAssemblyFromFile2 == null &&
|
|
|
|
|
CompileAssemblyFromFileBatch2 == null && CompileAssemblyFromSource2 == null &&
|
|
|
|
|
CompileAssemblyFromSourceBatch2 == null) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
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);
|
2012-04-29 10:03:10 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
static MethodDef GetTheOnlyMethod(TypeDef type, string typeName, string methodName, string returnType, string parameters) {
|
2012-11-02 22:57:11 +08:00
|
|
|
|
MethodDef foundMethod = null;
|
2012-04-29 10:03:10 +08:00
|
|
|
|
|
|
|
|
|
foreach (var method in type.Methods) {
|
|
|
|
|
if (!method.IsStatic || method.Body == null || method.HasGenericParameters)
|
|
|
|
|
continue;
|
|
|
|
|
if (method.IsPrivate)
|
|
|
|
|
continue;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!DotNetUtils.IsMethod(method, returnType, "(" + typeName + "," + parameters + ")"))
|
2012-04-29 10:03:10 +08:00
|
|
|
|
continue;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!DotNetUtils.CallsMethod(method, returnType + " " + typeName + "::" + methodName + "(" + parameters + ")"))
|
2012-04-29 10:03:10 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (foundMethod != null)
|
|
|
|
|
return null;
|
|
|
|
|
foundMethod = method;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return foundMethod;
|
|
|
|
|
}
|
2012-02-28 06:43:45 +08:00
|
|
|
|
}
|
|
|
|
|
}
|