/*
Copyright (C) 2011 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 .
*/
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.SmartAssembly {
class AutomatedErrorReportingFinder {
ModuleDefinition module;
ExceptionLoggerRemover exceptionLoggerRemover = new ExceptionLoggerRemover();
TypeDefinition automatedErrorReportingType;
int constantArgs;
AerVersion aerVersion;
enum AerVersion {
V0,
V1,
V2,
V3,
}
public ExceptionLoggerRemover ExceptionLoggerRemover {
get { return exceptionLoggerRemover; }
}
public TypeDefinition Type {
get { return automatedErrorReportingType; }
}
public bool Detected {
get { return automatedErrorReportingType != null; }
}
public AutomatedErrorReportingFinder(ModuleDefinition module) {
this.module = module;
}
public void find() {
foreach (var type in module.Types) {
if (detectAutomatedErrorReportingType(type))
break;
}
}
bool detectAutomatedErrorReportingType(TypeDefinition type) {
if (automatedErrorReportingType != null)
return false;
const int MIN_HELPER_METHODS = 6;
constantArgs = 0;
var methods = new List();
MethodDefinition mainMethod = null;
foreach (var method in type.Methods) {
if (isAutomatedErrorReportingMethodHelper(method))
methods.Add(method);
else if (isAutomatedErrorReportingMethod(method))
mainMethod = method;
else
continue;
initializeConstantArgs(method);
}
if (mainMethod == null)
return false;
if (isV0(type)) {
foreach (var method in type.Methods) {
if (method.IsStatic)
exceptionLoggerRemover.add(method);
}
}
else {
if (methods.Count < MIN_HELPER_METHODS)
return false;
if (isV1(mainMethod))
aerVersion = AerVersion.V1;
else if (isV2(mainMethod))
aerVersion = AerVersion.V2;
else
aerVersion = AerVersion.V3;
methods.Sort((a, b) => Utils.compareInt32(a.Parameters.Count, b.Parameters.Count));
for (int i = 0; i < methods.Count; i++) {
var method = methods[i];
if (method.Parameters.Count != i + constantArgs)
return false;
var methodCalls = DotNetUtils.getMethodCallCounts(method);
if (methodCalls.count(mainMethod.FullName) != 1)
return false;
}
}
exceptionLoggerRemover.add(mainMethod);
foreach (var method in methods)
exceptionLoggerRemover.add(method);
automatedErrorReportingType = type;
if (aerVersion == AerVersion.V1) {
foreach (var method in type.Methods) {
if (DotNetUtils.isMethod(method, "System.Exception", "(System.Int32,System.Object[])"))
exceptionLoggerRemover.add(method);
}
}
initUnhandledExceptionFilterMethods();
return true;
}
void initializeConstantArgs(MethodDefinition method) {
if (constantArgs > 0)
return;
constantArgs = getConstantArgs(method);
}
static int getConstantArgs(MethodDefinition method) {
if (method.Parameters.Count >= 2) {
if (isV1(method) || isV2(method))
return 2;
}
return 1;
}
static string[] v0Fields = new string[] {
"System.Int32",
"System.Object[]",
};
static bool isV0(TypeDefinition type) {
if (!new FieldTypes(type).exactly(v0Fields))
return false;
if (type.Methods.Count != 3)
return false;
if (type.HasEvents || type.HasProperties)
return false;
MethodDefinition ctor = null, meth1 = null, meth2 = null;
foreach (var method in type.Methods) {
if (method.Name == ".ctor") {
ctor = method;
continue;
}
if (!method.IsStatic)
return false;
if (DotNetUtils.isMethod(method, "System.Exception", "(System.Int32,System.Object[])"))
meth1 = method;
else if (DotNetUtils.isMethod(method, "System.Exception", "(System.Int32,System.Exception,System.Object[])"))
meth2 = method;
else
return false;
}
return ctor != null && meth1 != null && meth2 != null;
}
static bool isV1(MethodDefinition method) {
if (method.Parameters.Count < 2)
return false;
var p0 = method.Parameters[0].ParameterType.FullName;
var p1 = method.Parameters[1].ParameterType.FullName;
return p0 == "System.Int32" && p1 == "System.Exception";
}
static bool isV2(MethodDefinition method) {
if (method.Parameters.Count < 2)
return false;
var p0 = method.Parameters[0].ParameterType.FullName;
var p1 = method.Parameters[1].ParameterType.FullName;
return p0 == "System.Exception" && p1 == "System.Int32";
}
bool isAutomatedErrorReportingMethodHelper(MethodDefinition method) {
if (!method.HasBody || !method.IsStatic || method.Name == ".ctor")
return false;
if (DotNetUtils.hasReturnValue(method) && method.MethodReturnType.ReturnType.FullName != "System.Exception")
return false;
if (method.Parameters.Count == 0)
return false;
if (!isV1(method) && !isV2(method) && method.Parameters[0].ParameterType.FullName != "System.Exception")
return false;
for (int i = getConstantArgs(method); i < method.Parameters.Count; i++) {
if (method.Parameters[i].ParameterType.FullName != "System.Object")
return false;
}
return true;
}
bool isAutomatedErrorReportingMethod(MethodDefinition method) {
if (!method.HasBody || !method.IsStatic || method.Name == ".ctor")
return false;
return DotNetUtils.isMethod(method, "System.Void", "(System.Exception,System.Object[])") ||
DotNetUtils.isMethod(method, "System.Exception", "(System.Exception,System.Object[])") ||
// 2.x
DotNetUtils.isMethod(method, "System.Exception", "(System.Exception,System.Int32,System.Object[])") ||
// 1.x
DotNetUtils.isMethod(method, "System.Exception", "(System.Int32,System.Exception,System.Object[])");
}
void initUnhandledExceptionFilterMethods() {
var main = module.EntryPoint;
if (main == null || !main.HasBody)
return;
if (!main.Body.HasExceptionHandlers)
return;
MethodDefinition mainExceptionHandlerMethod = null;
var instructions = main.Body.Instructions;
for (int i = 0; i < instructions.Count; i++) {
var call = instructions[i];
if (call.OpCode != OpCodes.Call)
continue;
var method = getMainExceptionHandlerMethod(call.Operand as MethodReference);
if (method == null)
continue;
mainExceptionHandlerMethod = method; // Use the last one we find
}
if (mainExceptionHandlerMethod != null)
exceptionLoggerRemover.add(mainExceptionHandlerMethod);
}
MethodDefinition getMainExceptionHandlerMethod(MethodReference methodReference) {
var type = DotNetUtils.getType(module, methodReference.DeclaringType);
var method = DotNetUtils.getMethod(type, methodReference);
if (method == null || !method.IsStatic)
return null;
if (DotNetUtils.hasReturnValue(method))
return null;
switch (aerVersion) {
case AerVersion.V0:
case AerVersion.V1:
if (!DotNetUtils.isMethod(method, "System.Void", "(System.Int32,System.Object[])"))
return null;
break;
case AerVersion.V2:
if (method.Parameters.Count < 2)
return null;
if (method.Parameters[0].ParameterType.ToString() != "System.Exception")
return null;
if (method.Parameters[1].ParameterType.ToString() != "System.Int32")
return null;
if (method.Parameters[method.Parameters.Count - 1].ParameterType.ToString() != "System.Object[]")
return null;
break;
case AerVersion.V3:
if (method.Parameters.Count < 1)
return null;
if (method.Parameters[0].ParameterType.ToString() != "System.Exception")
return null;
if (method.Parameters[method.Parameters.Count - 1].ParameterType.ToString() != "System.Object[]")
return null;
break;
default:
throw new ApplicationException("Invalid AER version");
}
return method;
}
}
}