diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index a56d2163..ce7eb2d1 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -66,6 +66,7 @@
+
diff --git a/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs b/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs
index 391869e3..96d06c58 100644
--- a/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs
@@ -29,7 +29,7 @@ namespace de4dot.code.deobfuscators.Agile_NET {
TypeDef cliSecureRtType;
MethodDef postInitializeMethod;
MethodDef initializeMethod;
- MethodDef stringDecrypterMethod;
+ Dictionary stringDecrypterInfos = new Dictionary();
MethodDef loadMethod;
bool foundSig;
@@ -41,8 +41,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
get { return cliSecureRtType; }
}
- public MethodDef StringDecrypterMethod {
- get { return stringDecrypterMethod; }
+ public IEnumerable StringDecrypterInfos {
+ get { return stringDecrypterInfos.Keys; }
}
public MethodDef PostInitializeMethod {
@@ -66,7 +66,11 @@ namespace de4dot.code.deobfuscators.Agile_NET {
cliSecureRtType = Lookup(oldOne.cliSecureRtType, "Could not find CliSecureRt type");
postInitializeMethod = Lookup(oldOne.postInitializeMethod, "Could not find postInitializeMethod method");
initializeMethod = Lookup(oldOne.initializeMethod, "Could not find initializeMethod method");
- stringDecrypterMethod = Lookup(oldOne.stringDecrypterMethod, "Could not find stringDecrypterMethod method");
+ foreach (var info in oldOne.stringDecrypterInfos.Keys) {
+ var m = Lookup(info.Method, "Could not find string decrypter method");
+ var f = Lookup(info.Field, "Could not find string decrypter field");
+ stringDecrypterInfos[new StringDecrypterInfo(m, f)] = true;
+ }
loadMethod = Lookup(oldOne.loadMethod, "Could not find loadMethod method");
foundSig = oldOne.foundSig;
}
@@ -100,11 +104,11 @@ namespace de4dot.code.deobfuscators.Agile_NET {
if (!HasInitializeMethod(type, "_Initialize") && !HasInitializeMethod(type, "_Initialize64"))
continue;
- stringDecrypterMethod = FindStringDecrypterMethod(type);
initializeMethod = calledMethod;
postInitializeMethod = FindMethod(type, "System.Void", "PostInitialize", "()");
loadMethod = FindMethod(type, "System.IntPtr", "Load", "()");
cliSecureRtType = type;
+ FindStringDecrypters();
return true;
}
}
@@ -112,6 +116,15 @@ namespace de4dot.code.deobfuscators.Agile_NET {
return false;
}
+ void FindStringDecrypters() {
+ AddStringDecrypterMethod(FindStringDecrypterMethod(cliSecureRtType));
+ }
+
+ void AddStringDecrypterMethod(MethodDef method) {
+ if (method != null)
+ stringDecrypterInfos[new StringDecrypterInfo(method)] = true;
+ }
+
static string[] requiredFields6 = new string[] {
"System.Byte[]",
};
@@ -134,7 +147,7 @@ namespace de4dot.code.deobfuscators.Agile_NET {
if (cs == null)
continue;
- stringDecrypterMethod = cs;
+ AddStringDecrypterMethod(cs);
cliSecureRtType = type;
return true;
}
@@ -208,7 +221,7 @@ namespace de4dot.code.deobfuscators.Agile_NET {
continue;
cliSecureRtType = type;
- stringDecrypterMethod = cs;
+ AddStringDecrypterMethod(cs);
return;
}
}
diff --git a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs
index fd577753..a3e6a3f9 100644
--- a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs
@@ -180,7 +180,7 @@ namespace de4dot.code.deobfuscators.Agile_NET {
FindCliSecureAttribute();
cliSecureRtType = new CliSecureRtType(module);
cliSecureRtType.Find(ModuleBytes);
- stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterMethod);
+ stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterInfos);
stringDecrypter.Find();
resourceDecrypter = new ResourceDecrypter(module);
resourceDecrypter.Find();
@@ -246,7 +246,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
base.DeobfuscateBegin();
cliSecureRtType.FindStringDecrypterMethod();
- stringDecrypter.Method = cliSecureRtType.StringDecrypterMethod;
+ stringDecrypter.AddDecrypterInfos(cliSecureRtType.StringDecrypterInfos);
+ stringDecrypter.Initialize();
AddAttributesToBeRemoved(cliSecureAttributes, "Obfuscator attribute");
@@ -265,7 +266,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
proxyCallFixer.Find();
- staticStringInliner.Add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0]));
+ foreach (var info in stringDecrypter.StringDecrypterInfos)
+ staticStringInliner.Add(info.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0]));
DeobfuscatedFile.StringDecryptersAdded();
if (options.DecryptMethods) {
@@ -295,6 +297,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
}
public override void DeobfuscateMethodEnd(Blocks blocks) {
+ if (Operations.DecryptStrings != OpDecryptString.None)
+ stringDecrypter.Deobfuscate(blocks);
proxyCallFixer.Deobfuscate(blocks);
RemoveStackFrameHelperCode(blocks);
base.DeobfuscateMethodEnd(blocks);
@@ -310,8 +314,14 @@ namespace de4dot.code.deobfuscators.Agile_NET {
}
if (CanRemoveStringDecrypterType) {
AddTypeToBeRemoved(stringDecrypter.Type, "String decrypter type");
+ foreach (var info in stringDecrypter.StringDecrypterInfos) {
+ if (info.Method.DeclaringType != cliSecureRtType.Type)
+ AddMethodToBeRemoved(info.Method, "String decrypter method");
+ if (info.Field != null && info.Field.DeclaringType != stringDecrypter.Type)
+ AddFieldToBeRemoved(info.Field, "String decrypter field");
+ }
if (options.DecryptMethods)
- AddTypeToBeRemoved(cliSecureRtType.Type, "Obfuscator type");
+ AddTypeToBeRemoved(cliSecureRtType.Type ?? stringDecrypter.KeyArrayFieldType, "Obfuscator type");
}
if (options.DecryptMethods) {
AddResources("Obfuscator protection files");
@@ -327,8 +337,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
public override IEnumerable GetStringDecrypterMethods() {
var list = new List();
- if (stringDecrypter.Method != null)
- list.Add(stringDecrypter.Method.MDToken.ToInt32());
+ foreach (var info in stringDecrypter.StringDecrypterInfos)
+ list.Add(info.Method.MDToken.ToInt32());
return list;
}
diff --git a/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs
index 91bf919f..2bab0efe 100644
--- a/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/StringDecrypter.cs
@@ -18,38 +18,53 @@
*/
using System;
+using System.Collections.Generic;
using System.Text;
using dnlib.DotNet;
+using dnlib.DotNet.Emit;
+using de4dot.blocks;
namespace de4dot.code.deobfuscators.Agile_NET {
class StringDecrypter {
ModuleDefMD module;
TypeDef stringDecrypterType;
- MethodDef stringDecrypterMethod;
+ FieldDef keyInitField;
+ FieldDef keyArrayField;
+ Dictionary stringDecrypterInfos = new Dictionary();
byte[] stringDecrypterKey;
public bool Detected {
- get { return stringDecrypterMethod != null; }
+ get { return stringDecrypterInfos.Count != 0; }
}
public TypeDef Type {
get { return stringDecrypterType; }
}
- public MethodDef Method {
- get { return stringDecrypterMethod; }
- set { stringDecrypterMethod = value; }
+ public TypeDef KeyArrayFieldType {
+ get { return keyArrayField == null ? null : keyArrayField.DeclaringType; }
}
- public StringDecrypter(ModuleDefMD module, MethodDef stringDecrypterMethod) {
+ public IEnumerable StringDecrypterInfos {
+ get { return stringDecrypterInfos.Keys; }
+ }
+
+ public StringDecrypter(ModuleDefMD module, IEnumerable stringDecrypterMethods) {
this.module = module;
- this.stringDecrypterMethod = stringDecrypterMethod;
+ foreach (var sdm in stringDecrypterMethods)
+ this.stringDecrypterInfos[sdm] = true;
}
public StringDecrypter(ModuleDefMD module, StringDecrypter oldOne) {
this.module = module;
stringDecrypterType = Lookup(oldOne.stringDecrypterType, "Could not find stringDecrypterType");
- stringDecrypterMethod = Lookup(oldOne.stringDecrypterMethod, "Could not find stringDecrypterMethod");
+ keyInitField = Lookup(oldOne.keyInitField, "Could not find key init field");
+ keyArrayField = Lookup(oldOne.keyArrayField, "Could not find key array field");
+ foreach (var info in oldOne.stringDecrypterInfos.Keys) {
+ var m = Lookup(info.Method, "Could not find string decrypter method");
+ var f = Lookup(info.Field, "Could not find string decrypter field");
+ stringDecrypterInfos[new StringDecrypterInfo(m, f)] = true;
+ }
stringDecrypterKey = oldOne.stringDecrypterKey;
}
@@ -57,6 +72,11 @@ namespace de4dot.code.deobfuscators.Agile_NET {
return DeobUtils.Lookup(module, def, errorMessage);
}
+ public void AddDecrypterInfos(IEnumerable infos) {
+ foreach (var info in infos)
+ stringDecrypterInfos[info] = true;
+ }
+
public void Find() {
stringDecrypterKey = new byte[1] { 0xFF };
foreach (var type in module.Types) {
@@ -64,6 +84,7 @@ namespace de4dot.code.deobfuscators.Agile_NET {
stringDecrypterType = type;
foreach (var field in type.Fields) {
if (field.FullName == " ::345" || field.FullName == "/D234 ::345") {
+ keyInitField = field;
stringDecrypterKey = field.InitialValue;
break;
}
@@ -73,6 +94,119 @@ namespace de4dot.code.deobfuscators.Agile_NET {
}
}
+ public void Initialize() {
+ if (keyInitField == null)
+ return;
+
+ foreach (var type in module.Types) {
+ var cctor = type.FindStaticConstructor();
+ if (cctor == null)
+ continue;
+ keyArrayField = GetKeyArrayField(cctor, keyInitField);
+ if (keyArrayField != null)
+ break;
+ }
+ if (keyArrayField == null)
+ return;
+
+ foreach (var type in module.GetTypes()) {
+ FieldDef field;
+ var method = FindStringDecrypters(type, keyArrayField, out field);
+ if (method == null)
+ continue;
+
+ stringDecrypterInfos[new StringDecrypterInfo(method, field)] = true;
+ }
+ }
+
+ static FieldDef GetKeyArrayField(MethodDef method, FieldDef keyInitField) {
+ if (method == null || method.Body == null)
+ return null;
+ var instrs = method.Body.Instructions;
+ for (int i = 0; i < instrs.Count; i++) {
+ if (instrs[i].OpCode.Code != Code.Ldtoken)
+ continue;
+
+ i++;
+ for (; i < instrs.Count; i++) {
+ var instr = instrs[i];
+ if (instr.OpCode.Code != Code.Stsfld)
+ continue;
+ var field = instr.Operand as FieldDef;
+ if (field == null || !field.IsStatic || field.DeclaringType != method.DeclaringType)
+ continue;
+ if (field.FieldSig.GetFieldType().GetFullName() != "System.Byte[]")
+ continue;
+ return field;
+ }
+ }
+ return null;
+ }
+
+ static MethodDef FindStringDecrypters(TypeDef type, FieldDef keyArrayField, out FieldDef field) {
+ FieldDef foundField = null;
+ foreach (var method in type.Methods) {
+ if (!method.IsAssembly || !method.IsStatic)
+ continue;
+ if (!DotNetUtils.IsMethod(method, "System.String", "(System.String)"))
+ continue;
+ if (!method.HasBody)
+ continue;
+
+ bool accessedArrayField = false;
+ foreach (var instr in method.Body.Instructions) {
+ var f = instr.Operand as FieldDef;
+ accessedArrayField |= f == keyArrayField;
+ if (f == null || f == keyArrayField || f == foundField)
+ continue;
+ if (f.FieldSig.GetFieldType().GetFullName() != "System.Collections.Hashtable" ||
+ foundField != null)
+ goto exit;
+ foundField = f;
+ }
+ if (!accessedArrayField)
+ continue;
+
+ field = foundField;
+ return method;
+ }
+
+exit: ;
+ field = null;
+ return null;
+ }
+
+ public void Deobfuscate(Blocks blocks) {
+ if (!blocks.Method.IsStaticConstructor)
+ return;
+
+ var decrypterFields = new Dictionary(stringDecrypterInfos.Count);
+ foreach (var info in stringDecrypterInfos.Keys) {
+ if (info.Field != null)
+ decrypterFields[info.Field] = true;
+ }
+
+ foreach (var block in blocks.MethodBlocks.GetAllBlocks()) {
+ var instrs = block.Instructions;
+ for (int i = instrs.Count - 2; i >= 0; i--) {
+ var newobj = instrs[i];
+ if (newobj.OpCode.Code != Code.Newobj)
+ continue;
+ var ctor = newobj.Operand as IMethod;
+ if (ctor == null || ctor.FullName != "System.Void System.Collections.Hashtable::.ctor()")
+ continue;
+ var stsfld = instrs[i + 1];
+ if (stsfld.OpCode.Code != Code.Stsfld)
+ continue;
+ var field = stsfld.Operand as FieldDef;
+ if (field == null || !decrypterFields.ContainsKey(field))
+ continue;
+
+ block.Remove(i, 2);
+ }
+ }
+ }
+
public string Decrypt(string es) {
if (stringDecrypterKey == null)
throw new ApplicationException("Trying to decrypt strings when stringDecrypterKey is null (could not find it!)");
diff --git a/de4dot.code/deobfuscators/Agile_NET/StringDecrypterInfo.cs b/de4dot.code/deobfuscators/Agile_NET/StringDecrypterInfo.cs
new file mode 100644
index 00000000..52dac2fb
--- /dev/null
+++ b/de4dot.code/deobfuscators/Agile_NET/StringDecrypterInfo.cs
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2011-2014 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 dnlib.DotNet;
+
+namespace de4dot.code.deobfuscators.Agile_NET {
+ class StringDecrypterInfo {
+ public readonly MethodDef Method;
+ public readonly FieldDef Field;
+
+ public StringDecrypterInfo(MethodDef method)
+ : this(method, null) {
+ }
+
+ public StringDecrypterInfo(MethodDef method, FieldDef field) {
+ this.Method = method;
+ this.Field = field;
+ }
+
+ public override int GetHashCode() {
+ int hash = 0;
+ if (Method != null)
+ hash ^= Method.GetHashCode();
+ if (Field != null)
+ hash ^= Field.GetHashCode();
+ return hash;
+ }
+
+ public override bool Equals(object obj) {
+ var other = obj as StringDecrypterInfo;
+ return other != null &&
+ Method == other.Method &&
+ Field == other.Field;
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs
index a6431820..ccb40008 100644
--- a/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs
+++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs
@@ -33,11 +33,13 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
public MyDeobfuscator(ModuleDefMD module) {
cliSecureRtType = new CliSecureRtType(module);
cliSecureRtType.Find(null);
- stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterMethod);
+ stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterInfos);
stringDecrypter.Find();
cliSecureRtType.FindStringDecrypterMethod();
- stringDecrypter.Method = cliSecureRtType.StringDecrypterMethod;
- staticStringInliner.Add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0]));
+ stringDecrypter.AddDecrypterInfos(cliSecureRtType.StringDecrypterInfos);
+ stringDecrypter.Initialize();
+ foreach (var info in stringDecrypter.StringDecrypterInfos)
+ staticStringInliner.Add(info.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0]));
}
void RestoreMethod(Blocks blocks) {