2012-02-08 22:40:11 +08:00
|
|
|
|
/*
|
2013-01-02 00:03:16 +08:00
|
|
|
|
Copyright (C) 2011-2013 de4dot@gmail.com
|
2012-02-08 22:40:11 +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.Collections.Generic;
|
2012-12-20 09:06:09 +08:00
|
|
|
|
using dnlib.DotNet;
|
|
|
|
|
using dnlib.DotNet.Emit;
|
2012-02-08 22:40:11 +08:00
|
|
|
|
using de4dot.blocks;
|
|
|
|
|
|
|
|
|
|
namespace de4dot.code.deobfuscators.CodeVeil {
|
|
|
|
|
// Detects the type CV adds to the assembly that gets called from <Module>::.cctor.
|
|
|
|
|
class MainType {
|
2012-11-08 16:48:05 +08:00
|
|
|
|
ModuleDefMD module;
|
2012-11-02 22:57:11 +08:00
|
|
|
|
TypeDef theType;
|
|
|
|
|
MethodDef initMethod;
|
|
|
|
|
MethodDef tamperCheckMethod;
|
2012-02-08 22:40:11 +08:00
|
|
|
|
ObfuscatorVersion obfuscatorVersion = ObfuscatorVersion.Unknown;
|
2012-11-08 16:48:05 +08:00
|
|
|
|
List<uint> rvas = new List<uint>(); // _stub and _executive
|
2012-11-02 22:57:11 +08:00
|
|
|
|
List<MethodDef> otherInitMethods = new List<MethodDef>();
|
2012-02-08 22:40:11 +08:00
|
|
|
|
|
|
|
|
|
public bool Detected {
|
|
|
|
|
get { return theType != null; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ObfuscatorVersion Version {
|
|
|
|
|
get { return obfuscatorVersion; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
public TypeDef Type {
|
2012-02-08 22:40:11 +08:00
|
|
|
|
get { return theType; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
public MethodDef InitMethod {
|
2012-02-08 22:40:11 +08:00
|
|
|
|
get { return initMethod; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
public List<MethodDef> OtherInitMethods {
|
2012-02-09 02:20:55 +08:00
|
|
|
|
get { return otherInitMethods; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
public MethodDef TamperCheckMethod {
|
2012-02-09 01:40:24 +08:00
|
|
|
|
get { return tamperCheckMethod; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-08 16:48:05 +08:00
|
|
|
|
public List<uint> Rvas {
|
2012-02-08 22:40:11 +08:00
|
|
|
|
get { return rvas; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-08 16:48:05 +08:00
|
|
|
|
public MainType(ModuleDefMD module) {
|
2012-02-08 22:40:11 +08:00
|
|
|
|
this.module = module;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-08 16:48:05 +08:00
|
|
|
|
public MainType(ModuleDefMD module, MainType oldOne) {
|
2012-02-08 22:40:11 +08:00
|
|
|
|
this.module = module;
|
|
|
|
|
this.theType = lookup(oldOne.theType, "Could not find main type");
|
|
|
|
|
this.initMethod = lookup(oldOne.initMethod, "Could not find main type init method");
|
2012-02-09 01:40:24 +08:00
|
|
|
|
this.tamperCheckMethod = lookup(oldOne.tamperCheckMethod, "Could not find tamper detection method");
|
2012-02-08 22:40:11 +08:00
|
|
|
|
this.obfuscatorVersion = oldOne.obfuscatorVersion;
|
|
|
|
|
this.rvas = oldOne.rvas;
|
2012-02-09 17:14:29 +08:00
|
|
|
|
foreach (var otherInitMethod in otherInitMethods)
|
|
|
|
|
otherInitMethods.Add(lookup(otherInitMethod, "Could not find otherInitMethod"));
|
2012-02-08 22:40:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-07 11:45:05 +08:00
|
|
|
|
T lookup<T>(T def, string errorMessage) where T : class, ICodedToken {
|
2012-02-08 22:40:11 +08:00
|
|
|
|
return DeobUtils.lookup(module, def, errorMessage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void find() {
|
|
|
|
|
var cctor = DotNetUtils.getModuleTypeCctor(module);
|
|
|
|
|
if (cctor == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var instrs = cctor.Body.Instructions;
|
|
|
|
|
for (int i = 0; i < instrs.Count - 2; i++) {
|
|
|
|
|
var ldci4_1 = instrs[i];
|
2012-11-08 16:48:05 +08:00
|
|
|
|
if (!ldci4_1.IsLdcI4())
|
2012-02-08 22:40:11 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var ldci4_2 = instrs[i + 1];
|
2012-11-08 16:48:05 +08:00
|
|
|
|
if (!ldci4_2.IsLdcI4())
|
2012-02-08 22:40:11 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var call = instrs[i + 2];
|
|
|
|
|
if (call.OpCode.Code != Code.Call)
|
|
|
|
|
continue;
|
2012-11-02 22:57:11 +08:00
|
|
|
|
var initMethodTmp = call.Operand as MethodDef;
|
2012-02-08 22:40:11 +08:00
|
|
|
|
ObfuscatorVersion obfuscatorVersionTmp;
|
|
|
|
|
if (!checkInitMethod(initMethodTmp, out obfuscatorVersionTmp))
|
|
|
|
|
continue;
|
|
|
|
|
if (!checkMethodsType(initMethodTmp.DeclaringType))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
obfuscatorVersion = obfuscatorVersionTmp;
|
|
|
|
|
theType = initMethodTmp.DeclaringType;
|
|
|
|
|
initMethod = initMethodTmp;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static string[] fieldTypesV5 = new string[] {
|
|
|
|
|
"System.Byte[]",
|
|
|
|
|
"System.Collections.Generic.List`1<System.Delegate>",
|
|
|
|
|
"System.Runtime.InteropServices.GCHandle",
|
|
|
|
|
};
|
2012-11-02 22:57:11 +08:00
|
|
|
|
bool checkInitMethod(MethodDef initMethod, out ObfuscatorVersion obfuscatorVersionTmp) {
|
2012-02-08 22:40:11 +08:00
|
|
|
|
obfuscatorVersionTmp = ObfuscatorVersion.Unknown;
|
|
|
|
|
|
|
|
|
|
if (initMethod == null)
|
|
|
|
|
return false;
|
|
|
|
|
if (initMethod.Body == null)
|
|
|
|
|
return false;
|
|
|
|
|
if (!initMethod.IsStatic)
|
|
|
|
|
return false;
|
|
|
|
|
if (!DotNetUtils.isMethod(initMethod, "System.Void", "(System.Boolean,System.Boolean)"))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (hasCodeString(initMethod, "E_FullTrust")) {
|
|
|
|
|
if (DotNetUtils.getPInvokeMethod(initMethod.DeclaringType, "user32", "CallWindowProcW") != null)
|
|
|
|
|
obfuscatorVersionTmp = ObfuscatorVersion.V4_1;
|
|
|
|
|
else
|
|
|
|
|
obfuscatorVersionTmp = ObfuscatorVersion.V4_0;
|
|
|
|
|
}
|
|
|
|
|
else if (hasCodeString(initMethod, "Full Trust Required"))
|
|
|
|
|
obfuscatorVersionTmp = ObfuscatorVersion.V3;
|
|
|
|
|
else if (initMethod.DeclaringType.HasNestedTypes && new FieldTypes(initMethod.DeclaringType).all(fieldTypesV5))
|
|
|
|
|
obfuscatorVersionTmp = ObfuscatorVersion.V5_0;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
static bool hasCodeString(MethodDef method, string str) {
|
2012-02-08 22:40:11 +08:00
|
|
|
|
foreach (var s in DotNetUtils.getCodeStrings(method)) {
|
|
|
|
|
if (s == str)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
bool checkMethodsType(TypeDef type) {
|
2012-11-08 16:48:05 +08:00
|
|
|
|
rvas = new List<uint>();
|
2012-02-08 23:14:07 +08:00
|
|
|
|
|
2012-02-08 22:40:11 +08:00
|
|
|
|
var fields = getRvaFields(type);
|
|
|
|
|
if (fields.Count < 2) // RVAs for executive and stub are always present if encrypted methods
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
foreach (var field in fields)
|
2012-11-08 16:48:05 +08:00
|
|
|
|
rvas.Add((uint)field.RVA);
|
2012-02-08 22:40:11 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
static List<FieldDef> getRvaFields(TypeDef type) {
|
|
|
|
|
var fields = new List<FieldDef>();
|
2012-02-08 22:40:11 +08:00
|
|
|
|
foreach (var field in type.Fields) {
|
2012-11-08 16:48:05 +08:00
|
|
|
|
var etype = field.FieldSig.GetFieldType().GetElementType();
|
|
|
|
|
if (etype != ElementType.U1 && etype != ElementType.U4)
|
2012-02-08 22:40:11 +08:00
|
|
|
|
continue;
|
|
|
|
|
if (field.RVA == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
fields.Add(field);
|
|
|
|
|
}
|
|
|
|
|
return fields;
|
|
|
|
|
}
|
2012-02-09 01:40:24 +08:00
|
|
|
|
|
|
|
|
|
public void initialize() {
|
2012-02-09 02:20:55 +08:00
|
|
|
|
if (theType == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2012-02-09 01:40:24 +08:00
|
|
|
|
tamperCheckMethod = findTamperCheckMethod();
|
2012-02-09 02:20:55 +08:00
|
|
|
|
otherInitMethods = findOtherInitMethods();
|
2012-02-09 01:40:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
MethodDef findTamperCheckMethod() {
|
2012-02-09 01:40:24 +08:00
|
|
|
|
foreach (var method in theType.Methods) {
|
|
|
|
|
if (!method.IsStatic || method.Body == null)
|
|
|
|
|
continue;
|
|
|
|
|
if (!DotNetUtils.isMethod(method, "System.Void", "(System.Reflection.Assembly,System.UInt64)"))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return method;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2012-02-09 01:58:06 +08:00
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
List<MethodDef> findOtherInitMethods() {
|
|
|
|
|
var list = new List<MethodDef>();
|
2012-02-09 02:20:55 +08:00
|
|
|
|
foreach (var method in theType.Methods) {
|
|
|
|
|
if (!method.IsStatic)
|
|
|
|
|
continue;
|
|
|
|
|
if (method.Name == ".cctor")
|
|
|
|
|
continue;
|
|
|
|
|
if (!DotNetUtils.isMethod(method, "System.Void", "()"))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
list.Add(method);
|
|
|
|
|
}
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
public MethodDef getInitStringDecrypterMethod(MethodDef stringDecrypterInitMethod) {
|
2012-02-09 01:58:06 +08:00
|
|
|
|
if (stringDecrypterInitMethod == null)
|
|
|
|
|
return null;
|
|
|
|
|
if (theType == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
foreach (var method in theType.Methods) {
|
|
|
|
|
if (!method.IsStatic || method.Body == null)
|
|
|
|
|
continue;
|
|
|
|
|
if (callsMethod(method, stringDecrypterInitMethod))
|
|
|
|
|
return method;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-02 22:57:11 +08:00
|
|
|
|
bool callsMethod(MethodDef methodToCheck, MethodDef calledMethod) {
|
2012-03-18 03:36:41 +08:00
|
|
|
|
foreach (var method in DotNetUtils.getCalledMethods(module, methodToCheck)) {
|
|
|
|
|
if (method == calledMethod)
|
2012-02-09 01:58:06 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-02-09 02:20:55 +08:00
|
|
|
|
|
|
|
|
|
public void removeInitCall(Blocks blocks) {
|
|
|
|
|
if (initMethod == null || theType == null)
|
|
|
|
|
return;
|
|
|
|
|
if (blocks.Method.Name != ".cctor")
|
|
|
|
|
return;
|
|
|
|
|
if (blocks.Method.DeclaringType != DotNetUtils.getModuleType(module))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
|
|
|
|
|
var instrs = block.Instructions;
|
|
|
|
|
for (int i = 0; i < instrs.Count - 2; i++) {
|
|
|
|
|
if (!instrs[i].isLdcI4())
|
|
|
|
|
continue;
|
|
|
|
|
if (!instrs[i + 1].isLdcI4())
|
|
|
|
|
continue;
|
|
|
|
|
var call = instrs[i + 2];
|
|
|
|
|
if (call.OpCode.Code != Code.Call)
|
|
|
|
|
continue;
|
|
|
|
|
if (call.Operand != initMethod)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
block.remove(i, 3);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-02-08 22:40:11 +08:00
|
|
|
|
}
|
|
|
|
|
}
|