Support SA 1.x-3.x

This commit is contained in:
de4dot 2011-12-22 05:41:28 +01:00
parent 222132f43b
commit a38781c1d0
6 changed files with 331 additions and 50 deletions

View File

@ -17,6 +17,7 @@
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
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>();
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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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)));
}
}
}
}

View File

@ -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<MethodDefinition>(DotNetUtils.findMethods(stringsEncodingClass.Methods, "System.String", new string[] { "System.Int32" }));
if (methods.Count != 1)
return false;

View File

@ -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<System.Int32,System.String>", true) == null) {
return false;
var fields = new FieldTypes(type);
if (fields.exists("System.Collections.Hashtable") ||
fields.exists("System.Collections.Generic.Dictionary`2<System.Int32,System.String>") ||
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<MethodDefinition>(DotNetUtils.getNormalMethods(type));