2012-05-26 23:36:26 +08:00
|
|
|
|
/*
|
2014-03-12 05:15:43 +08:00
|
|
|
|
Copyright (C) 2011-2014 de4dot@gmail.com
|
2012-05-26 23:36:26 +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.Security.Cryptography;
|
|
|
|
|
using System.Text;
|
2012-12-20 09:06:09 +08:00
|
|
|
|
using dnlib.DotNet;
|
|
|
|
|
using dnlib.DotNet.Emit;
|
2012-05-26 23:36:26 +08:00
|
|
|
|
using de4dot.blocks;
|
|
|
|
|
using de4dot.code.resources;
|
|
|
|
|
|
|
|
|
|
namespace de4dot.code.deobfuscators.CodeWall {
|
|
|
|
|
class AssemblyDecrypter {
|
2012-11-08 17:40:58 +08:00
|
|
|
|
ModuleDefMD module;
|
2012-05-26 23:36:26 +08:00
|
|
|
|
ISimpleDeobfuscator simpleDeobfuscator;
|
|
|
|
|
IDeobfuscator deob;
|
|
|
|
|
List<AssemblyInfo> assemblyInfos = new List<AssemblyInfo>();
|
|
|
|
|
string entryPointAssemblyKey;
|
|
|
|
|
string resourcePassword;
|
|
|
|
|
string resourceSalt;
|
|
|
|
|
EmbeddedResource assemblyResource;
|
2012-11-08 17:40:58 +08:00
|
|
|
|
ModuleDefMD resourceModule;
|
2012-05-26 23:36:26 +08:00
|
|
|
|
|
|
|
|
|
public class AssemblyInfo {
|
|
|
|
|
public readonly byte[] data;
|
|
|
|
|
public readonly string extension;
|
|
|
|
|
public readonly string assemblyFullName;
|
|
|
|
|
public readonly string assemblySimpleName;
|
|
|
|
|
public readonly bool isEntryPointAssembly;
|
|
|
|
|
|
|
|
|
|
public AssemblyInfo(byte[] data, string extension, string assemblyFullName, string assemblySimpleName, bool isEntryPointAssembly) {
|
|
|
|
|
this.data = data;
|
|
|
|
|
this.extension = extension;
|
|
|
|
|
this.assemblyFullName = assemblyFullName;
|
|
|
|
|
this.assemblySimpleName = assemblySimpleName;
|
|
|
|
|
this.isEntryPointAssembly = isEntryPointAssembly;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToString() {
|
|
|
|
|
return assemblyFullName;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerable<AssemblyInfo> AssemblyInfos {
|
|
|
|
|
get { return assemblyInfos; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-08 17:40:58 +08:00
|
|
|
|
public AssemblyDecrypter(ModuleDefMD module, ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob) {
|
2012-05-26 23:36:26 +08:00
|
|
|
|
this.module = module;
|
|
|
|
|
this.simpleDeobfuscator = simpleDeobfuscator;
|
|
|
|
|
this.deob = deob;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void Find() {
|
2012-05-26 23:36:26 +08:00
|
|
|
|
var method = module.EntryPoint;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!CheckEntryPoint(method))
|
2012-05-26 23:36:26 +08:00
|
|
|
|
return;
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
MethodDef decryptAssemblyMethod;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
var mainKey = GetMainResourceKey(method, out decryptAssemblyMethod);
|
2012-05-26 23:36:26 +08:00
|
|
|
|
if (mainKey == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
DeobfuscateAll(decryptAssemblyMethod);
|
2012-11-08 17:40:58 +08:00
|
|
|
|
ModuleDefMD theResourceModule;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
var resource = GetResource(decryptAssemblyMethod, out theResourceModule);
|
2012-05-26 23:36:26 +08:00
|
|
|
|
if (resource == null)
|
|
|
|
|
return;
|
|
|
|
|
string password, salt;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!GetPassword(decryptAssemblyMethod, out password, out salt))
|
2012-05-26 23:36:26 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
entryPointAssemblyKey = mainKey;
|
|
|
|
|
resourcePassword = password;
|
|
|
|
|
resourceSalt = salt;
|
|
|
|
|
assemblyResource = resource;
|
2012-05-27 08:31:24 +08:00
|
|
|
|
resourceModule = theResourceModule;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
DecryptAllAssemblies();
|
2012-05-26 23:36:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-05-27 08:31:24 +08:00
|
|
|
|
static readonly string[] requiredLocals = new string[] {
|
|
|
|
|
"System.AppDomain",
|
|
|
|
|
"System.DateTime",
|
|
|
|
|
};
|
2013-01-19 20:03:57 +08:00
|
|
|
|
bool CheckEntryPoint(MethodDef method) {
|
2012-05-26 23:36:26 +08:00
|
|
|
|
if (method == null)
|
|
|
|
|
return false;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!new LocalTypes(method).All(requiredLocals))
|
2012-05-26 23:36:26 +08:00
|
|
|
|
return false;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
var handlers = DeobUtils.GetAllResolveHandlers(method);
|
2012-05-26 23:36:26 +08:00
|
|
|
|
if (handlers.Count != 1)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void DeobfuscateAll(MethodDef method) {
|
|
|
|
|
simpleDeobfuscator.Deobfuscate(method);
|
|
|
|
|
simpleDeobfuscator.DecryptStrings(method, deob);
|
2012-05-26 23:36:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
string GetMainResourceKey(MethodDef method, out MethodDef decryptAssemblyMethod) {
|
|
|
|
|
foreach (var calledMethod in DotNetUtils.GetCalledMethods(module, method)) {
|
2012-05-26 23:36:26 +08:00
|
|
|
|
if (!calledMethod.IsStatic || calledMethod.Body == null)
|
|
|
|
|
continue;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!DotNetUtils.IsMethod(calledMethod, "System.Void", "(System.String[])"))
|
2012-05-26 23:36:26 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
DeobfuscateAll(calledMethod);
|
|
|
|
|
string keyInfo = GetMainResourceKeyInfo(calledMethod, out decryptAssemblyMethod);
|
2012-05-26 23:36:26 +08:00
|
|
|
|
if (keyInfo == null)
|
|
|
|
|
continue;
|
|
|
|
|
return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(new ASCIIEncoding().GetBytes(keyInfo))).Replace("-", "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decryptAssemblyMethod = null;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
string GetMainResourceKeyInfo(MethodDef method, out MethodDef decryptAssemblyMethod) {
|
2012-05-26 23:36:26 +08:00
|
|
|
|
var instrs = method.Body.Instructions;
|
|
|
|
|
for (int i = 0; i < instrs.Count - 1; i++) {
|
|
|
|
|
var ldstr = instrs[i];
|
|
|
|
|
if (ldstr.OpCode.Code != Code.Ldstr)
|
|
|
|
|
continue;
|
|
|
|
|
var call = instrs[i + 1];
|
|
|
|
|
if (call.OpCode.Code != Code.Call)
|
|
|
|
|
continue;
|
2012-11-02 22:57:11 +08:00
|
|
|
|
var calledMethod = call.Operand as MethodDef;
|
2012-05-26 23:36:26 +08:00
|
|
|
|
if (calledMethod == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
decryptAssemblyMethod = calledMethod;
|
|
|
|
|
return (string)ldstr.Operand;
|
|
|
|
|
}
|
|
|
|
|
decryptAssemblyMethod = null;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
EmbeddedResource GetResource(MethodDef method, out ModuleDefMD theResourceModule) {
|
2012-05-27 08:31:24 +08:00
|
|
|
|
string resourceDllFileName = null;
|
|
|
|
|
theResourceModule = module;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
foreach (var s in DotNetUtils.GetCodeStrings(method)) {
|
2012-05-27 08:31:24 +08:00
|
|
|
|
if (s.Length > 0 && s[0] == '\\')
|
|
|
|
|
resourceDllFileName = s;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
var resource = DotNetUtils.GetResource(theResourceModule, s + ".resources") as EmbeddedResource;
|
2012-05-27 08:31:24 +08:00
|
|
|
|
if (resource != null)
|
|
|
|
|
return resource;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (resourceDllFileName == null)
|
|
|
|
|
return null;
|
|
|
|
|
// Here if CW 2.x
|
2013-01-19 20:03:57 +08:00
|
|
|
|
theResourceModule = GetResourceModule(resourceDllFileName);
|
2012-05-27 08:31:24 +08:00
|
|
|
|
if (theResourceModule == null)
|
|
|
|
|
return null;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
foreach (var s in DotNetUtils.GetCodeStrings(method)) {
|
|
|
|
|
var resource = DotNetUtils.GetResource(theResourceModule, s + ".resources") as EmbeddedResource;
|
2012-05-26 23:36:26 +08:00
|
|
|
|
if (resource != null)
|
|
|
|
|
return resource;
|
|
|
|
|
}
|
2012-05-27 08:31:24 +08:00
|
|
|
|
|
|
|
|
|
theResourceModule = null;
|
2012-05-26 23:36:26 +08:00
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
ModuleDefMD GetResourceModule(string name) {
|
2012-05-27 08:31:24 +08:00
|
|
|
|
try {
|
2012-11-08 17:40:58 +08:00
|
|
|
|
var resourceDllFileName = Path.Combine(Path.GetDirectoryName(module.Location), name.Substring(1));
|
|
|
|
|
return ModuleDefMD.Load(resourceDllFileName);
|
2012-05-27 08:31:24 +08:00
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
bool GetPassword(MethodDef method, out string password, out string salt) {
|
2012-05-26 23:36:26 +08:00
|
|
|
|
var instrs = method.Body.Instructions;
|
|
|
|
|
for (int i = 0; i < instrs.Count - 1; i++) {
|
|
|
|
|
var ldstr1 = instrs[i];
|
|
|
|
|
if (ldstr1.OpCode.Code != Code.Ldstr)
|
|
|
|
|
continue;
|
|
|
|
|
var ldstr2 = instrs[i + 1];
|
|
|
|
|
if (ldstr2.OpCode.Code != Code.Ldstr)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
password = (string)ldstr1.Operand;
|
|
|
|
|
salt = (string)ldstr2.Operand;
|
|
|
|
|
if (password == null || salt == null)
|
|
|
|
|
continue;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
password = null;
|
|
|
|
|
salt = null;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void DecryptAllAssemblies() {
|
2012-05-26 23:36:26 +08:00
|
|
|
|
if (assemblyResource == null)
|
|
|
|
|
return;
|
2012-11-20 08:15:27 +08:00
|
|
|
|
assemblyResource.Data.Position = 0;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
var resourceSet = ResourceReader.Read(resourceModule, assemblyResource.Data);
|
2012-05-26 23:36:26 +08:00
|
|
|
|
foreach (var resourceElement in resourceSet.ResourceElements) {
|
|
|
|
|
if (resourceElement.ResourceData.Code != ResourceTypeCode.ByteArray)
|
|
|
|
|
throw new ApplicationException("Invalid resource");
|
|
|
|
|
var resourceData = (BuiltInResourceData)resourceElement.ResourceData;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
var assemblyData = Decrypt((byte[])resourceData.Data);
|
2012-11-08 17:40:58 +08:00
|
|
|
|
var theModule = ModuleDefMD.Load(assemblyData);
|
2012-05-26 23:36:26 +08:00
|
|
|
|
bool isMain = resourceElement.Name == entryPointAssemblyKey;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
assemblyInfos.Add(new AssemblyInfo(assemblyData, DeobUtils.GetExtension(theModule.Kind), theModule.Assembly.FullName, theModule.Assembly.Name.String, isMain));
|
2012-05-26 23:36:26 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
byte[] Decrypt(byte[] encrypted) {
|
2012-05-26 23:36:26 +08:00
|
|
|
|
var keyGenerator = new PasswordDeriveBytes(resourcePassword, Encoding.ASCII.GetBytes(resourceSalt));
|
2013-01-19 20:03:57 +08:00
|
|
|
|
return DeobUtils.Inflate(DeobUtils.AesDecrypt(encrypted, keyGenerator.GetBytes(32), keyGenerator.GetBytes(16)), false);
|
2012-05-26 23:36:26 +08:00
|
|
|
|
}
|
2012-07-24 23:37:34 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public AssemblyInfo FindMain(string asmFullName) {
|
2012-07-24 23:37:34 +08:00
|
|
|
|
foreach (var asmInfo in assemblyInfos) {
|
|
|
|
|
if (asmInfo.isEntryPointAssembly && asmInfo.assemblyFullName == asmFullName)
|
|
|
|
|
return asmInfo;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public AssemblyInfo FindMain() {
|
2012-07-25 01:05:50 +08:00
|
|
|
|
foreach (var asmInfo in assemblyInfos) {
|
|
|
|
|
if (asmInfo.isEntryPointAssembly)
|
|
|
|
|
return asmInfo;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void Remove(AssemblyInfo asmInfo) {
|
2012-07-24 23:37:34 +08:00
|
|
|
|
assemblyInfos.Remove(asmInfo);
|
|
|
|
|
}
|
2012-05-26 23:36:26 +08:00
|
|
|
|
}
|
|
|
|
|
}
|