Support more than one string decrypter method

This commit is contained in:
de4dot 2014-03-27 11:28:52 +01:00
parent 291b83e325
commit 43453f3863
6 changed files with 236 additions and 24 deletions

View File

@ -66,6 +66,7 @@
<Compile Include="deobfuscators\Agile_NET\ResourceDecrypter.cs" />
<Compile Include="deobfuscators\Agile_NET\StackFrameHelper.cs" />
<Compile Include="deobfuscators\Agile_NET\StringDecrypter.cs" />
<Compile Include="deobfuscators\Agile_NET\StringDecrypterInfo.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\CilOperandInstructionRestorer.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\CsvmDataReader.cs" />
<Compile Include="deobfuscators\Agile_NET\vm\CsvmMethodData.cs" />

View File

@ -29,7 +29,7 @@ namespace de4dot.code.deobfuscators.Agile_NET {
TypeDef cliSecureRtType;
MethodDef postInitializeMethod;
MethodDef initializeMethod;
MethodDef stringDecrypterMethod;
Dictionary<StringDecrypterInfo, bool> stringDecrypterInfos = new Dictionary<StringDecrypterInfo, bool>();
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<StringDecrypterInfo> 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;
}
}

View File

@ -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<int> GetStringDecrypterMethods() {
var list = new List<int>();
if (stringDecrypter.Method != null)
list.Add(stringDecrypter.Method.MDToken.ToInt32());
foreach (var info in stringDecrypter.StringDecrypterInfos)
list.Add(info.Method.MDToken.ToInt32());
return list;
}

View File

@ -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<StringDecrypterInfo, bool> stringDecrypterInfos = new Dictionary<StringDecrypterInfo, bool>();
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<StringDecrypterInfo> StringDecrypterInfos {
get { return stringDecrypterInfos.Keys; }
}
public StringDecrypter(ModuleDefMD module, IEnumerable<StringDecrypterInfo> 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<StringDecrypterInfo> 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 == "<D234> <D234>::345" || field.FullName == "<ClassD234>/D234 <ClassD234>::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<FieldDef, bool>(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!)");

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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;
}
}
}

View File

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