diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index 6ca20de8..edb82f59 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -70,6 +70,7 @@
+
diff --git a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypter.cs b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypter.cs
new file mode 100644
index 00000000..f5c226af
--- /dev/null
+++ b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypter.cs
@@ -0,0 +1,483 @@
+/*
+ Copyright (C) 2011-2012 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 System.IO;
+using System.Text;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Mono.Cecil.Metadata;
+using de4dot.blocks;
+using de4dot.PE;
+
+namespace de4dot.code.deobfuscators.Confuser {
+ class ConstantsDecrypter {
+ ModuleDefinition module;
+ byte[] fileData;
+ ISimpleDeobfuscator simpleDeobfuscator;
+ FieldDefinition dictField, dataField;
+ MethodDefinition installMethod;
+ MethodDefinitionAndDeclaringTypeDict decrypters = new MethodDefinitionAndDeclaringTypeDict();
+ uint key0, key0d;
+ MethodDefinition nativeMethod;
+ EmbeddedResource resource;
+ byte[] constants;
+
+ public class DecrypterInfo {
+ readonly ConstantsDecrypter constantsDecrypter;
+ public readonly MethodDefinition method;
+ public ulong key0l, key1l, key2l;
+ public uint key0, key0d;
+
+ public DecrypterInfo(ConstantsDecrypter constantsDecrypter, MethodDefinition method) {
+ this.constantsDecrypter = constantsDecrypter;
+ this.method = method;
+ }
+
+ public string decryptString(uint magic1, ulong magic2) {
+ return Encoding.UTF8.GetString(decrypt(magic1, magic2));
+ }
+
+ public int decryptInt32(uint magic1, ulong magic2) {
+ return BitConverter.ToInt32(decrypt(magic1, magic2), 0);
+ }
+
+ public long decryptInt64(uint magic1, ulong magic2) {
+ return BitConverter.ToInt64(decrypt(magic1, magic2), 0);
+ }
+
+ public float decryptSingle(uint magic1, ulong magic2) {
+ return BitConverter.ToSingle(decrypt(magic1, magic2), 0);
+ }
+
+ public double decryptDouble(uint magic1, ulong magic2) {
+ return BitConverter.ToDouble(decrypt(magic1, magic2), 0);
+ }
+
+ byte[] decrypt(uint magic1, ulong magic2) {
+ ulong info = hash(method.DeclaringType.MetadataToken.ToUInt32() * magic1) ^ magic2;
+ int offset = (int)(info >> 32);
+ int len = (int)info;
+ var decrypted = new byte[len];
+ byte[] key = BitConverter.GetBytes(method.MetadataToken.ToUInt32() ^ key0d);
+ for (int i = 0; i < len; i++)
+ decrypted[i] = (byte)(constantsDecrypter.constants[offset + i] ^ key[(offset + i) & 3]);
+ return decrypted;
+ }
+
+ ulong hash(uint magic) {
+ ulong h0 = key0l * magic;
+ ulong h1 = key1l;
+ ulong h2 = key2l;
+ h1 *= h0;
+ h2 *= h0;
+ h0 *= h0;
+ ulong hash = 14695981039346656037UL;
+ while (h0 != 0) {
+ hash *= 1099511628211UL;
+ hash = (hash ^ h0) + (h1 ^ h2) * key0;
+ h1 *= 0x811C9DC5;
+ h2 *= 0xA2CEBAB2;
+ h0 >>= 8;
+ }
+ return hash;
+ }
+ }
+
+ public IEnumerable Types {
+ get {
+ var types = new List();
+ foreach (var info in decrypters.getValues())
+ types.Add(info.method.DeclaringType);
+ return types;
+ }
+ }
+
+ public IEnumerable Fields {
+ get {
+ return new List {
+ dataField,
+ dictField,
+ };
+ }
+ }
+
+ public MethodDefinition NativeMethod {
+ get { return nativeMethod; }
+ }
+
+ public EmbeddedResource Resource {
+ get { return resource; }
+ }
+
+ public IEnumerable Decrypters {
+ get { return decrypters.getValues(); }
+ }
+
+ public bool Detected {
+ get { return installMethod != null; }
+ }
+
+ public ConstantsDecrypter(ModuleDefinition module, byte[] fileData, ISimpleDeobfuscator simpleDeobfuscator) {
+ this.module = module;
+ this.fileData = fileData;
+ this.simpleDeobfuscator = simpleDeobfuscator;
+ }
+
+ public void find() {
+ var cctor = DotNetUtils.getModuleTypeCctor(module);
+ if (cctor == null)
+ return;
+ simpleDeobfuscator.deobfuscate(cctor, true);
+
+ if ((dictField = findDictField(cctor, cctor.DeclaringType)) == null)
+ return;
+ if ((dataField = findDataField(cctor, cctor.DeclaringType)) == null)
+ return;
+
+ installMethod = cctor;
+ }
+
+ static FieldDefinition findDictField(MethodDefinition method, TypeDefinition declaringType) {
+ var instrs = method.Body.Instructions;
+ for (int i = 0; i < instrs.Count - 1; i++) {
+ var newobj = instrs[i];
+ if (newobj.OpCode.Code != Code.Newobj)
+ continue;
+ var ctor = newobj.Operand as MethodReference;
+ if (ctor == null || ctor.FullName != "System.Void System.Collections.Generic.Dictionary`2::.ctor()")
+ continue;
+
+ var stsfld = instrs[i + 1];
+ if (stsfld.OpCode.Code != Code.Stsfld)
+ continue;
+ var field = stsfld.Operand as FieldDefinition;
+ if (field == null || field.DeclaringType != declaringType)
+ continue;
+ if (field.FieldType.FullName != "System.Collections.Generic.Dictionary`2")
+ continue;
+
+ return field;
+ }
+ return null;
+ }
+
+ static FieldDefinition findDataField(MethodDefinition method, TypeDefinition declaringType) {
+ var instrs = method.Body.Instructions;
+ for (int i = 0; i < instrs.Count - 1; i++) {
+ var callvirt = instrs[i];
+ if (callvirt.OpCode.Code != Code.Callvirt)
+ continue;
+ var ctor = callvirt.Operand as MethodReference;
+ if (ctor == null || ctor.FullName != "System.Byte[] System.IO.MemoryStream::ToArray()")
+ continue;
+
+ var stsfld = instrs[i + 1];
+ if (stsfld.OpCode.Code != Code.Stsfld)
+ continue;
+ var field = stsfld.Operand as FieldDefinition;
+ if (field == null || field.DeclaringType != declaringType)
+ continue;
+ if (field.FieldType.FullName != "System.Byte[]")
+ continue;
+
+ return field;
+ }
+ return null;
+ }
+
+ public void initialize() {
+ if (installMethod == null)
+ return;
+
+ if (!findKeys())
+ throw new ApplicationException("Could not find keys");
+ nativeMethod = findNativeMethod(installMethod, installMethod.DeclaringType);
+ if (nativeMethod == null)
+ throw new ApplicationException("Could not find nativeMethod");
+
+ if ((resource = findResource(key0)) == null)
+ throw new ApplicationException("Could not find resource");
+ constants = decrypt(resource.GetResourceData());
+
+ findDecrypters();
+ }
+
+ EmbeddedResource findResource(uint magic) {
+ var name = Encoding.UTF8.GetString(BitConverter.GetBytes(magic));
+ return DotNetUtils.getResource(module, name) as EmbeddedResource;
+ }
+
+ bool findKeys() {
+ if (!findKey0(installMethod, out key0))
+ return false;
+ if (!findKey0d(installMethod, out key0d))
+ return false;
+
+ return true;
+ }
+
+ static bool findKey0(MethodDefinition method, out uint key) {
+ var instrs = method.Body.Instructions;
+ for (int i = 0; i < instrs.Count; i++) {
+ int index = ConfuserUtils.findCallMethod(instrs, i, Code.Call, "System.Text.Encoding System.Text.Encoding::get_UTF8()");
+ if (index < 0)
+ break;
+ int index2 = ConfuserUtils.findCallMethod(instrs, i, Code.Call, "System.Byte[] System.BitConverter::GetBytes(System.Int32)");
+ if (index2 - index != 2)
+ continue;
+ var ldci4 = instrs[index + 1];
+ if (!DotNetUtils.isLdcI4(ldci4))
+ continue;
+
+ key = (uint)DotNetUtils.getLdcI4Value(ldci4);
+ return true;
+ }
+
+ key = 0;
+ return false;
+ }
+
+ static bool findKey0d(MethodDefinition method, out uint key) {
+ var instrs = method.Body.Instructions;
+ for (int i = 0; i < instrs.Count; i++) {
+ int index = ConfuserUtils.findCallMethod(instrs, i, Code.Callvirt, "System.Reflection.Module System.Reflection.MemberInfo::get_Module()");
+ if (index < 0)
+ break;
+ int index2 = ConfuserUtils.findCallMethod(instrs, i, Code.Callvirt, "System.Int32 System.Reflection.MemberInfo::get_MetadataToken()");
+ if (index2 - index != 3)
+ continue;
+ var ldci4 = instrs[index + 1];
+ if (!DotNetUtils.isLdcI4(ldci4))
+ continue;
+ if (!DotNetUtils.isLdloc(instrs[index + 2]))
+ continue;
+
+ key = (uint)DotNetUtils.getLdcI4Value(ldci4);
+ return true;
+ }
+
+ key = 0;
+ return false;
+ }
+
+ static MethodDefinition findNativeMethod(MethodDefinition method, TypeDefinition declaringType) {
+ var instrs = method.Body.Instructions;
+ for (int i = 0; i < instrs.Count; i++) {
+ if (!DotNetUtils.isLdloc(instrs[i]))
+ continue;
+ var call = instrs[i + 1];
+ if (call.OpCode.Code != Code.Call)
+ continue;
+ var calledMethod = call.Operand as MethodDefinition;
+ if (calledMethod == null || !calledMethod.IsStatic || !calledMethod.IsNative)
+ continue;
+ if (!DotNetUtils.isMethod(calledMethod, "System.Int32", "(System.Int32)"))
+ continue;
+
+ return calledMethod;
+ }
+ return null;
+ }
+
+ void findDecrypters() {
+ foreach (var type in module.Types) {
+ foreach (var method in type.Methods) {
+ var info = createDecrypterInfo(method);
+ if (info != null)
+ decrypters.add(info.method, info);
+ }
+ }
+ }
+
+ DecrypterInfo createDecrypterInfo(MethodDefinition method) {
+ if (method == null || method.Body == null)
+ return null;
+ if (!method.IsStatic)
+ return null;
+ if (method.Parameters.Count != 2)
+ return null;
+ if (method.Parameters[0].ParameterType.EType != ElementType.U4)
+ return null;
+ if (method.Parameters[1].ParameterType.EType != ElementType.U8)
+ return null;
+ if (!(method.MethodReturnType.ReturnType is GenericParameter))
+ return null;
+ if (method.GenericParameters.Count != 1)
+ return null;
+
+ simpleDeobfuscator.deobfuscate(method);
+ var info = new DecrypterInfo(this, method);
+ if (!findLKeys(info))
+ return null;
+ if (!findKey0(info))
+ return null;
+ if (!findKey0d(info))
+ return null;
+
+ return info;
+ }
+
+ static bool findLKeys(DecrypterInfo info) {
+ var instrs = info.method.Body.Instructions;
+ for (int i = 0; i < instrs.Count - 8; i++) {
+ var ldci8_1 = instrs[i];
+ if (ldci8_1.OpCode.Code != Code.Ldc_I8)
+ continue;
+ if (!DotNetUtils.isLdloc(instrs[i + 1]))
+ continue;
+ if (instrs[i + 2].OpCode.Code != Code.Conv_U8)
+ continue;
+ if (instrs[i + 3].OpCode.Code != Code.Mul)
+ continue;
+ if (!DotNetUtils.isStloc(instrs[i + 4]))
+ continue;
+ var ldci8_2 = instrs[i + 5];
+ if (ldci8_2.OpCode.Code != Code.Ldc_I8)
+ continue;
+ if (!DotNetUtils.isStloc(instrs[i + 6]))
+ continue;
+ var ldci8_3 = instrs[i + 7];
+ if (ldci8_3.OpCode.Code != Code.Ldc_I8)
+ continue;
+ if (!DotNetUtils.isStloc(instrs[i + 8]))
+ continue;
+
+ info.key0l = (ulong)(long)ldci8_1.Operand;
+ info.key1l = (ulong)(long)ldci8_2.Operand;
+ info.key2l = (ulong)(long)ldci8_3.Operand;
+ return true;
+ }
+ return false;
+ }
+
+ static bool findKey0(DecrypterInfo info) {
+ var instrs = info.method.Body.Instructions;
+ for (int i = 0; i < instrs.Count - 4; i++) {
+ if (!DotNetUtils.isLdloc(instrs[i]))
+ continue;
+ if (instrs[i + 1].OpCode.Code != Code.Xor)
+ continue;
+ var ldci4 = instrs[i + 2];
+ if (!DotNetUtils.isLdcI4(ldci4))
+ continue;
+ if (instrs[i + 3].OpCode.Code != Code.Conv_U8)
+ continue;
+ if (instrs[i + 4].OpCode.Code != Code.Mul)
+ continue;
+
+ info.key0 = (uint)DotNetUtils.getLdcI4Value(ldci4);
+ return true;
+ }
+ return false;
+ }
+
+ static bool findKey0d(DecrypterInfo info) {
+ var instrs = info.method.Body.Instructions;
+ for (int i = 0; i < instrs.Count; i++) {
+ int index = ConfuserUtils.findCallMethod(instrs, i, Code.Callvirt, "System.Int32 System.Reflection.MemberInfo::get_MetadataToken()");
+ if (index < 0)
+ break;
+ int index2 = ConfuserUtils.findCallMethod(instrs, index, Code.Call, "System.Byte[] System.BitConverter::GetBytes(System.Int32)");
+ if (index2 < 0)
+ break;
+ if (index2 - index != 3)
+ continue;
+ var ldci4 = instrs[index + 1];
+ if (!DotNetUtils.isLdcI4(ldci4))
+ continue;
+ if (instrs[index + 2].OpCode.Code != Code.Xor)
+ continue;
+
+ info.key0d = (uint)DotNetUtils.getLdcI4Value(ldci4);
+ return true;
+ }
+ return false;
+ }
+
+ byte[] decrypt(byte[] encrypted) {
+ uint sigToken = key0d ^ installMethod.MetadataToken.ToUInt32();
+ if ((sigToken & 0xFF000000) != 0x11000000)
+ throw new ApplicationException("Invalid sig token");
+ var key = module.GetSignatureBlob(sigToken);
+
+ var decrypted = DeobUtils.aesDecrypt(encrypted, key, DeobUtils.md5Sum(key));
+ decrypted = DeobUtils.inflate(decrypted, true);
+
+ var x86Emu = new x86Emulator(new PeImage(fileData));
+ var reader = new BinaryReader(new MemoryStream(decrypted));
+ var result = new MemoryStream();
+ var writer = new BinaryWriter(result);
+ while (reader.BaseStream.Position < reader.BaseStream.Length) {
+ uint magic = Utils.readEncodedUInt32(reader);
+ writer.Write((byte)x86Emu.emulate((uint)nativeMethod.RVA, magic));
+ }
+
+ return result.ToArray();
+ }
+
+ static bool verifyGenericArg(GenericInstanceMethod gim, ElementType etype) {
+ if (gim == null || gim.GenericArguments.Count != 1)
+ return false;
+ return gim.GenericArguments[0].EType == etype;
+ }
+
+ public string decryptString(MethodDefinition method, GenericInstanceMethod gim, uint magic1, ulong magic2) {
+ if (!verifyGenericArg(gim, ElementType.String))
+ return null;
+ var info = decrypters.find(method);
+ return info.decryptString(magic1, magic2);
+ }
+
+ public object decryptInt32(MethodDefinition method, GenericInstanceMethod gim, uint magic1, ulong magic2) {
+ if (!verifyGenericArg(gim, ElementType.I4))
+ return null;
+ var info = decrypters.find(method);
+ return info.decryptInt32(magic1, magic2);
+ }
+
+ public object decryptInt64(MethodDefinition method, GenericInstanceMethod gim, uint magic1, ulong magic2) {
+ if (!verifyGenericArg(gim, ElementType.I8))
+ return null;
+ var info = decrypters.find(method);
+ return info.decryptInt64(magic1, magic2);
+ }
+
+ public object decryptSingle(MethodDefinition method, GenericInstanceMethod gim, uint magic1, ulong magic2) {
+ if (!verifyGenericArg(gim, ElementType.R4))
+ return null;
+ var info = decrypters.find(method);
+ return info.decryptSingle(magic1, magic2);
+ }
+
+ public object decryptDouble(MethodDefinition method, GenericInstanceMethod gim, uint magic1, ulong magic2) {
+ if (!verifyGenericArg(gim, ElementType.R8))
+ return null;
+ var info = decrypters.find(method);
+ return info.decryptDouble(magic1, magic2);
+ }
+
+ public void cleanUp() {
+ //TODO: Only remove its code
+ installMethod.Body.Instructions.Clear();
+ installMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs b/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs
index 615d6052..0e62f0a6 100644
--- a/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs
@@ -71,6 +71,11 @@ namespace de4dot.code.deobfuscators.Confuser {
AntiDebugger antiDebugger;
AntiDumping antiDumping;
ResourceDecrypter resourceDecrypter;
+ ConstantsDecrypter constantsDecrypter;
+ Int32ValueInliner int32ValueInliner;
+ Int64ValueInliner int64ValueInliner;
+ SingleValueInliner singleValueInliner;
+ DoubleValueInliner doubleValueInliner;
internal class Options : OptionsBase {
public bool RemoveAntiDebug { get; set; }
@@ -100,6 +105,7 @@ namespace de4dot.code.deobfuscators.Confuser {
public Deobfuscator(Options options)
: base(options) {
this.options = options;
+ StringFeatures = StringFeatures.AllowStaticDecryption | StringFeatures.AllowDynamicDecryption;
}
protected override int detectInternal() {
@@ -109,7 +115,8 @@ namespace de4dot.code.deobfuscators.Confuser {
toInt32(proxyCallFixer != null ? proxyCallFixer.Detected : false) +
toInt32(antiDebugger != null ? antiDebugger.Detected : false) +
toInt32(antiDumping != null ? antiDumping.Detected : false) +
- toInt32(resourceDecrypter != null ? resourceDecrypter.Detected : false);
+ toInt32(resourceDecrypter != null ? resourceDecrypter.Detected : false) +
+ toInt32(constantsDecrypter != null ? constantsDecrypter.Detected : false);
if (sum > 0)
val += 100 + 10 * (sum - 1);
@@ -133,6 +140,8 @@ namespace de4dot.code.deobfuscators.Confuser {
antiDumping.find();
resourceDecrypter = new ResourceDecrypter(module, DeobfuscatedFile);
resourceDecrypter.find();
+ constantsDecrypter = new ConstantsDecrypter(module, getFileData(), DeobfuscatedFile);
+ constantsDecrypter.find();
}
byte[] getFileData() {
@@ -192,6 +201,24 @@ namespace de4dot.code.deobfuscators.Confuser {
addTypeToBeRemoved(antiDumping.Type, "Anti dumping type");
}
+ constantsDecrypter.initialize();
+ int32ValueInliner = new Int32ValueInliner();
+ int64ValueInliner = new Int64ValueInliner();
+ singleValueInliner = new SingleValueInliner();
+ doubleValueInliner = new DoubleValueInliner();
+ foreach (var info in constantsDecrypter.Decrypters) {
+ staticStringInliner.add(info.method, (method, gim, args) => constantsDecrypter.decryptString(method, gim, (uint)args[0], (ulong)args[1]));
+ int32ValueInliner.add(info.method, (method, gim, args) => constantsDecrypter.decryptInt32(method, gim, (uint)args[0], (ulong)args[1]));
+ int64ValueInliner.add(info.method, (method, gim, args) => constantsDecrypter.decryptInt64(method, gim, (uint)args[0], (ulong)args[1]));
+ singleValueInliner.add(info.method, (method, gim, args) => constantsDecrypter.decryptSingle(method, gim, (uint)args[0], (ulong)args[1]));
+ doubleValueInliner.add(info.method, (method, gim, args) => constantsDecrypter.decryptDouble(method, gim, (uint)args[0], (ulong)args[1]));
+ }
+ DeobfuscatedFile.stringDecryptersAdded();
+ addTypesToBeRemoved(constantsDecrypter.Types, "Constants decrypter type");
+ addFieldsToBeRemoved(constantsDecrypter.Fields, "Constants decrypter field");
+ addMethodToBeRemoved(constantsDecrypter.NativeMethod, "Constants decrypter native method");
+ addResourceToBeRemoved(constantsDecrypter.Resource, "Encrypted constants");
+
proxyCallFixer.find();
}
@@ -214,11 +241,16 @@ namespace de4dot.code.deobfuscators.Confuser {
public override void deobfuscateMethodEnd(Blocks blocks) {
proxyCallFixer.deobfuscate(blocks);
resourceDecrypter.deobfuscate(blocks);
+ int32ValueInliner.decrypt(blocks);
+ int64ValueInliner.decrypt(blocks);
+ singleValueInliner.decrypt(blocks);
+ doubleValueInliner.decrypt(blocks);
base.deobfuscateMethodEnd(blocks);
}
public override void deobfuscateEnd() {
removeProxyDelegates(proxyCallFixer);
+ constantsDecrypter.cleanUp();
base.deobfuscateEnd();
}