diff --git a/de4dot.code/deobfuscators/SmartAssembly/AutomatedErrorReportingFinder.cs b/de4dot.code/deobfuscators/SmartAssembly/AutomatedErrorReportingFinder.cs
index 03542b30..68614084 100644
--- a/de4dot.code/deobfuscators/SmartAssembly/AutomatedErrorReportingFinder.cs
+++ b/de4dot.code/deobfuscators/SmartAssembly/AutomatedErrorReportingFinder.cs
@@ -17,6 +17,7 @@
along with de4dot. If not, see .
*/
+using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
@@ -27,6 +28,15 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
ModuleDefinition module;
ExceptionLoggerRemover exceptionLoggerRemover = new ExceptionLoggerRemover();
TypeDefinition automatedErrorReportingType;
+ int constantArgs;
+ AerVersion aerVersion;
+
+ enum AerVersion {
+ V0,
+ V1,
+ V2,
+ V3,
+ }
public ExceptionLoggerRemover ExceptionLoggerRemover {
get { return exceptionLoggerRemover; }
@@ -57,6 +67,7 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
const int MIN_HELPER_METHODS = 6;
+ constantArgs = 0;
var methods = new List();
MethodDefinition mainMethod = null;
foreach (var method in type.Methods) {
@@ -64,18 +75,39 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
methods.Add(method);
else if (isAutomatedErrorReportingMethod(method))
mainMethod = method;
+ else
+ continue;
+ initializeConstantArgs(method);
}
- if (mainMethod == null || methods.Count < MIN_HELPER_METHODS)
+ if (mainMethod == null)
return false;
- 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 + 1)
- return false;
- var methodCalls = DotNetUtils.getMethodCallCounts(method);
- if (methodCalls.count(mainMethod.FullName) != 1)
+ 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);
@@ -83,11 +115,78 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
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;
@@ -95,9 +194,9 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
return false;
if (method.Parameters.Count == 0)
return false;
- if (method.Parameters[0].ParameterType.FullName != "System.Exception")
+ if (!isV1(method) && !isV2(method) && method.Parameters[0].ParameterType.FullName != "System.Exception")
return false;
- for (int i = 1; i < method.Parameters.Count; i++) {
+ for (int i = getConstantArgs(method); i < method.Parameters.Count; i++) {
if (method.Parameters[i].ParameterType.FullName != "System.Object")
return false;
}
@@ -108,7 +207,11 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
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[])");
+ 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() {
@@ -141,14 +244,39 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
if (method == null || !method.IsStatic)
return null;
- if (method.Parameters.Count < 2)
- return null;
if (DotNetUtils.hasReturnValue(method))
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;
+
+ 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;
}
diff --git a/de4dot.code/deobfuscators/SmartAssembly/Deobfuscator.cs b/de4dot.code/deobfuscators/SmartAssembly/Deobfuscator.cs
index 0fb26ab3..902cba2b 100644
--- a/de4dot.code/deobfuscators/SmartAssembly/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/SmartAssembly/Deobfuscator.cs
@@ -318,7 +318,9 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
resourceResolverInfo.findTypes();
addModuleCctorInitCallToBeRemoved(assemblyResolverInfo.CallResolverMethod);
+ addCallToBeRemoved(module.EntryPoint, assemblyResolverInfo.CallResolverMethod);
addModuleCctorInitCallToBeRemoved(resourceResolverInfo.CallResolverMethod);
+ addCallToBeRemoved(module.EntryPoint, resourceResolverInfo.CallResolverMethod);
resourceDecrypterInfo.setSimpleZipType(getGlobalSimpleZipType(), DeobfuscatedFile);
diff --git a/de4dot.code/deobfuscators/SmartAssembly/ResolverInfoBase.cs b/de4dot.code/deobfuscators/SmartAssembly/ResolverInfoBase.cs
index 47b80cba..e77e8040 100644
--- a/de4dot.code/deobfuscators/SmartAssembly/ResolverInfoBase.cs
+++ b/de4dot.code/deobfuscators/SmartAssembly/ResolverInfoBase.cs
@@ -53,11 +53,18 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
if (resolverType != null)
return true;
- var cctor = DotNetUtils.getMethod(DotNetUtils.getModuleType(module), ".cctor");
- if (cctor == null)
- return false;
+ if (findTypes(DotNetUtils.getMethod(DotNetUtils.getModuleType(module), ".cctor")))
+ return true;
+ if (findTypes(module.EntryPoint))
+ return true;
- foreach (var tuple in DotNetUtils.getCalledMethods(module, cctor)) {
+ return false;
+ }
+
+ bool findTypes(MethodDefinition initMethod) {
+ if (initMethod == null)
+ return false;
+ foreach (var tuple in DotNetUtils.getCalledMethods(module, initMethod)) {
var method = tuple.Item2;
if (method.Name == ".cctor" || method.Name == ".ctor")
continue;
@@ -98,16 +105,50 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
return true;
}
+ if (hasLdftn(attachAppMethod)) {
+ simpleDeobfuscator.deobfuscate(attachAppMethod);
+ foreach (var resolverHandler in getResolverHandlers(type, attachAppMethod)) {
+ if (!resolverHandler.HasBody)
+ continue;
+ if (!checkResolverType2(resolverHandler.DeclaringType))
+ continue;
+ deobfuscate(resolverHandler);
+ if (checkHandlerMethod(resolverHandler)) {
+ callResolverMethod = attachAppMethod;
+ callResolverType = type;
+ resolverType = resolverHandler.DeclaringType;
+ return true;
+ }
+ }
+ }
+
return false;
}
+ static bool hasLdftn(MethodDefinition method) {
+ if (method == null || method.Body == null)
+ return false;
+ foreach (var instr in method.Body.Instructions) {
+ if (instr.OpCode.Code == Code.Ldftn)
+ return true;
+ }
+ return false;
+ }
+
+ bool checkResolverType2(TypeDefinition type) {
+ if (type.Properties.Count > 1 || type.Events.Count > 0)
+ return false;
+ if (!checkResolverType(type))
+ return false;
+
+ return true;
+ }
+
bool checkResolverType(TypeDefinition type, MethodDefinition initMethod) {
resolverType = null;
if (!initMethod.HasBody)
return false;
- if (type.Properties.Count > 1 || type.Events.Count > 0)
- return false;
- if (!checkResolverType(type))
+ if (!checkResolverType2(type))
return false;
deobfuscate(initMethod);
diff --git a/de4dot.code/deobfuscators/SmartAssembly/StringDecrypter.cs b/de4dot.code/deobfuscators/SmartAssembly/StringDecrypter.cs
index 1b7ddc9b..7e35ed46 100644
--- a/de4dot.code/deobfuscators/SmartAssembly/StringDecrypter.cs
+++ b/de4dot.code/deobfuscators/SmartAssembly/StringDecrypter.cs
@@ -24,6 +24,7 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
class StringDecrypter {
int stringOffset;
byte[] decryptedData;
+ StringDecrypterVersion stringDecrypterVersion;
public bool CanDecrypt {
get { return decryptedData != null; }
@@ -43,6 +44,8 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
stringOffset = stringDecrypterInfo.StringOffset;
decryptedData = stringDecrypterInfo.decrypt();
}
+
+ stringDecrypterVersion = StringDecrypterInfo.DecrypterVersion;
}
}
@@ -65,8 +68,20 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
decryptedData[index++];
}
- var decodedData = Convert.FromBase64String(Encoding.UTF8.GetString(decryptedData, index, len));
- return Encoding.UTF8.GetString(decodedData, 0, decodedData.Length);
+ switch (StringDecrypterInfo.DecrypterVersion) {
+ case StringDecrypterVersion.V1:
+ // Some weird problem with 1.x decrypted strings. They all have a \x01 char at the end.
+ var buf = Convert.FromBase64String(Encoding.ASCII.GetString(decryptedData, index, len));
+ if (buf.Length % 2 != 0)
+ Array.Resize(ref buf, buf.Length - 1);
+ return Encoding.Unicode.GetString(buf);
+
+ case StringDecrypterVersion.V2:
+ return Encoding.UTF8.GetString(Convert.FromBase64String(Encoding.ASCII.GetString(decryptedData, index, len)));
+
+ default:
+ return Encoding.UTF8.GetString(Convert.FromBase64String(Encoding.UTF8.GetString(decryptedData, index, len)));
+ }
}
}
}
diff --git a/de4dot.code/deobfuscators/SmartAssembly/StringDecrypterInfo.cs b/de4dot.code/deobfuscators/SmartAssembly/StringDecrypterInfo.cs
index d39b9ea5..f32f1571 100644
--- a/de4dot.code/deobfuscators/SmartAssembly/StringDecrypterInfo.cs
+++ b/de4dot.code/deobfuscators/SmartAssembly/StringDecrypterInfo.cs
@@ -24,6 +24,14 @@ using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.SmartAssembly {
+ enum StringDecrypterVersion {
+ V1,
+ V2,
+ V3,
+ V4,
+ Unknown,
+ }
+
class StringDecrypterInfo {
ModuleDefinition module;
ResourceDecrypter resourceDecrypter;
@@ -32,6 +40,11 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
int stringOffset;
TypeDefinition simpleZipType;
MethodDefinition stringDecrypterMethod;
+ StringDecrypterVersion decrypterVersion;
+
+ public StringDecrypterVersion DecrypterVersion {
+ get { return decrypterVersion; }
+ }
public TypeDefinition GetStringDelegate { get; set; }
public TypeDefinition StringsType { get; set; }
@@ -70,35 +83,100 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
this.stringsEncodingClass = stringsEncodingClass;
}
+ static string[] fields2x = new string[] {
+ "System.IO.Stream",
+ "System.Int32",
+ };
+ static string[] fields3x = new string[] {
+ "System.Byte[]",
+ "System.Int32",
+ };
+ StringDecrypterVersion guessVersion(MethodDefinition cctor) {
+ if (cctor == null)
+ return StringDecrypterVersion.V1;
+ var fieldTypes = new FieldTypes(stringsEncodingClass);
+ if (fieldTypes.exactly(fields2x))
+ return StringDecrypterVersion.V2;
+ if (fieldTypes.exactly(fields3x))
+ return StringDecrypterVersion.V3;
+ return StringDecrypterVersion.Unknown;
+ }
+
public bool init(IDeobfuscator deob, ISimpleDeobfuscator simpleDeobfuscator) {
var cctor = DotNetUtils.getMethod(stringsEncodingClass, ".cctor");
- if (cctor == null)
- throw new ApplicationException("Could not find .cctor");
- simpleDeobfuscator.deobfuscate(cctor);
+ if (cctor != null)
+ simpleDeobfuscator.deobfuscate(cctor);
- stringsResource = findStringResource(cctor);
- if (stringsResource == null) {
- simpleDeobfuscator.decryptStrings(cctor, deob);
- stringsResource = findStringResource(cctor);
- if (stringsResource == null)
- return false;
- }
-
- var offsetVal = findOffsetValue(cctor);
- if (offsetVal == null)
- throw new ApplicationException("Could not find string offset");
- stringOffset = offsetVal.Value;
+ decrypterVersion = guessVersion(cctor);
if (!findDecrypterMethod())
throw new ApplicationException("Could not find string decrypter method");
- simpleZipType = findSimpleZipType(cctor);
+ if (!findStringsResource(deob, simpleDeobfuscator, cctor ?? stringDecrypterMethod))
+ return false;
+
+ if (decrypterVersion <= StringDecrypterVersion.V3) {
+ MethodDefinition initMethod;
+ if (decrypterVersion == StringDecrypterVersion.V3)
+ initMethod = cctor;
+ else if (decrypterVersion == StringDecrypterVersion.V2)
+ initMethod = stringDecrypterMethod;
+ else
+ initMethod = stringDecrypterMethod;
+
+ stringOffset = 0;
+ if (decrypterVersion != StringDecrypterVersion.V1) {
+ var pkt = module.Assembly.Name.PublicKeyToken;
+ if (pkt != null) {
+ for (int i = 0; i < pkt.Length - 1; i += 2)
+ stringOffset ^= ((int)pkt[i] << 8) + pkt[i + 1];
+ }
+
+ if (DotNetUtils.findLdcI4Constant(initMethod, 0xFFFFFF) &&
+ DotNetUtils.findLdcI4Constant(initMethod, 0xFFFF)) {
+ stringOffset ^= ((stringDecrypterMethod.MetadataToken.ToInt32() & 0xFFFFFF) - 1) % 0xFFFF;
+ }
+ }
+ }
+ else {
+ var offsetVal = findOffsetValue(cctor);
+ if (offsetVal == null)
+ throw new ApplicationException("Could not find string offset");
+ stringOffset = offsetVal.Value;
+ decrypterVersion = StringDecrypterVersion.V4;
+ }
+
+ simpleZipType = cctor == null ? null : findSimpleZipType(cctor);
if (simpleZipType != null)
resourceDecrypter = new ResourceDecrypter(new ResourceDecrypterInfo(module, simpleZipType, simpleDeobfuscator));
return true;
}
+ bool findStringsResource(IDeobfuscator deob, ISimpleDeobfuscator simpleDeobfuscator, MethodDefinition initMethod) {
+ if (stringsResource != null)
+ return true;
+
+ if (decrypterVersion <= StringDecrypterVersion.V3) {
+ stringsResource = DotNetUtils.getResource(module, module.Mvid.ToString("B")) as EmbeddedResource;
+ if (stringsResource != null)
+ return true;
+ }
+
+ if (initMethod != null) {
+ stringsResource = findStringResource(initMethod);
+ if (stringsResource != null)
+ return true;
+
+ simpleDeobfuscator.decryptStrings(initMethod, deob);
+ stringsResource = findStringResource(initMethod);
+ if (stringsResource != null)
+ return true;
+ }
+
+ return false;
+ }
+
public byte[] decrypt() {
if (!CanDecrypt)
throw new ApplicationException("Can't decrypt strings");
@@ -107,10 +185,7 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
// Find the embedded resource where all the strings are encrypted
EmbeddedResource findStringResource(MethodDefinition method) {
- foreach (var ldstr in method.Body.Instructions) {
- if (ldstr.OpCode.Code != Code.Ldstr)
- continue;
- var s = ldstr.Operand as string;
+ foreach (var s in DotNetUtils.getCodeStrings(method)) {
if (s == null)
continue;
var resource = DotNetUtils.getResource(module, s) as EmbeddedResource;
@@ -181,6 +256,9 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
}
bool findDecrypterMethod() {
+ if (stringDecrypterMethod != null)
+ return true;
+
var methods = new List(DotNetUtils.findMethods(stringsEncodingClass.Methods, "System.String", new string[] { "System.Int32" }));
if (methods.Count != 1)
return false;
diff --git a/de4dot.code/deobfuscators/SmartAssembly/StringEncoderClassFinder.cs b/de4dot.code/deobfuscators/SmartAssembly/StringEncoderClassFinder.cs
index 0ce7b099..58e68202 100644
--- a/de4dot.code/deobfuscators/SmartAssembly/StringEncoderClassFinder.cs
+++ b/de4dot.code/deobfuscators/SmartAssembly/StringEncoderClassFinder.cs
@@ -170,12 +170,29 @@ namespace de4dot.code.deobfuscators.SmartAssembly {
}
}
+ static string[] fields1x = new string[] {
+ "System.IO.Stream",
+ };
+ static string[] fields2x = new string[] {
+ "System.IO.Stream",
+ "System.Int32",
+ };
+ static string[] fields3x = new string[] {
+ "System.Byte[]",
+ "System.Int32",
+ };
bool couldBeStringDecrypterClass(TypeDefinition type) {
- if (DotNetUtils.findFieldType(type, "System.Collections.Hashtable", true) == null &&
- DotNetUtils.findFieldType(type, "System.Collections.Generic.Dictionary`2", true) == null) {
- return false;
+ var fields = new FieldTypes(type);
+ if (fields.exists("System.Collections.Hashtable") ||
+ fields.exists("System.Collections.Generic.Dictionary`2") ||
+ fields.exactly(fields2x) ||
+ fields.exactly(fields3x)) {
+ if (DotNetUtils.getMethod(type, ".cctor") == null)
+ return false;
}
- if (DotNetUtils.getMethod(type, ".cctor") == null)
+ else if (fields.exactly(fields1x)) {
+ }
+ else
return false;
var methods = new List(DotNetUtils.getNormalMethods(type));