diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index a1c6b5df..3817a0cf 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -75,6 +75,7 @@ + diff --git a/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs b/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs index 8bc4ea9e..9a7d8d95 100644 --- a/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs +++ b/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs @@ -121,7 +121,7 @@ namespace de4dot.code.deobfuscators.Confuser { ushort _c = (ushort)seed; ushort m = _c; ushort c = _m; for (int i = 0; i < decrypted.Length; i++) { - decrypted[i] = (byte)(encrypted[i] ^ ((seed * m + c) & 0xFF)); + decrypted[i] = (byte)(encrypted[i] ^ (seed * m + c)); m = (ushort)(seed * m + _m); c = (ushort)(seed * c + _c); } diff --git a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterBase.cs b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterBase.cs index 1a351f2f..454c7993 100644 --- a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterBase.cs +++ b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterBase.cs @@ -24,12 +24,14 @@ using System.Text; using Mono.Cecil; using Mono.Cecil.Cil; using de4dot.blocks; +using de4dot.PE; namespace de4dot.code.deobfuscators.Confuser { abstract class ConstantsDecrypterBase { protected ModuleDefinition module; protected byte[] fileData; protected ISimpleDeobfuscator simpleDeobfuscator; + protected MethodDefinition nativeMethod; MethodDefinitionAndDeclaringTypeDict methodToDecrypterInfo = new MethodDefinitionAndDeclaringTypeDict(); FieldDefinitionAndDeclaringTypeDict fields = new FieldDefinitionAndDeclaringTypeDict(); protected EmbeddedResource resource; @@ -37,7 +39,6 @@ namespace de4dot.code.deobfuscators.Confuser { public class DecrypterInfo { public MethodDefinition decryptMethod; - public MethodDefinition nativeMethod; public uint key0, key1, key2, key3; public byte doubleType, singleType, int32Type, int64Type, stringType; @@ -48,7 +49,7 @@ namespace de4dot.code.deobfuscators.Confuser { throw new ApplicationException("Could not find all type codes"); } - bool initializeKeys() { + protected virtual bool initializeKeys() { if (!findKey0(decryptMethod, out key0)) return false; if (!findKey1(decryptMethod, out key1)) @@ -59,7 +60,7 @@ namespace de4dot.code.deobfuscators.Confuser { return true; } - static bool findKey0(MethodDefinition method, out uint key) { + protected static bool findKey0(MethodDefinition method, out uint key) { var instrs = method.Body.Instructions; for (int i = 0; i < instrs.Count - 5; i++) { if (!DotNetUtils.isLdloc(instrs[i])) @@ -104,7 +105,7 @@ namespace de4dot.code.deobfuscators.Confuser { return false; } - static bool findKey2Key3(MethodDefinition method, out uint key2, out uint key3) { + protected static bool findKey2Key3(MethodDefinition method, out uint key2, out uint key3) { var instrs = method.Body.Instructions; for (int i = 0; i < instrs.Count - 3; i++) { var ldci4_1 = instrs[i]; @@ -231,6 +232,10 @@ namespace de4dot.code.deobfuscators.Confuser { public abstract bool Detected { get; } + public MethodDefinition NativeMethod { + get { return nativeMethod; } + } + public EmbeddedResource Resource { get { return resource; } } @@ -273,6 +278,129 @@ namespace de4dot.code.deobfuscators.Confuser { } } + protected void setConstantsData(byte[] constants) { + reader = new BinaryReader(new MemoryStream(constants)); + } + + protected EmbeddedResource findResource(MethodDefinition method) { + return DotNetUtils.getResource(module, DotNetUtils.getCodeStrings(method)) as EmbeddedResource; + } + + protected static MethodDefinition findNativeMethod(MethodDefinition method) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count; i++) { + var call = instrs[i]; + 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; + } + + static VariableDefinition getDynamicLocal_v17_r73740(MethodDefinition method) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count; i++) { + i = ConfuserUtils.findCallMethod(instrs, i, Code.Callvirt, "System.Byte System.IO.BinaryReader::ReadByte()"); + if (i < 0 || i + 5 >= instrs.Count) + break; + if (!DotNetUtils.isStloc(instrs[i + 1])) + continue; + var ldloc = instrs[i + 2]; + if (!DotNetUtils.isLdloc(ldloc)) + continue; + if (!DotNetUtils.isLdloc(instrs[i + 3])) + continue; + var ldci4 = instrs[i + 4]; + if (!DotNetUtils.isLdcI4(ldci4) || DotNetUtils.getLdcI4Value(ldci4) != 0x7F) + continue; + if (instrs[i + 5].OpCode.Code != Code.And) + continue; + + return DotNetUtils.getLocalVar(method.Body.Variables, ldloc); + } + return null; + } + + static int getDynamicEndIndex_v17_r73740(MethodDefinition method, VariableDefinition local) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 5; i++) { + var stloc = instrs[i]; + if (!DotNetUtils.isStloc(stloc) || DotNetUtils.getLocalVar(method.Body.Variables, stloc) != local) + continue; + if (!DotNetUtils.isLdloc(instrs[i + 1])) + continue; + if (!DotNetUtils.isLdloc(instrs[i + 2])) + continue; + var ldloc = instrs[i + 3]; + if (!DotNetUtils.isLdloc(ldloc) || DotNetUtils.getLocalVar(method.Body.Variables, ldloc) != local) + continue; + if (instrs[i + 4].OpCode.Code != Code.Conv_U1) + continue; + if (instrs[i + 5].OpCode.Code != Code.Stelem_I1) + continue; + + return i; + } + return -1; + } + + static int getDynamicStartIndex_v17_r73740(MethodDefinition method, int endIndex) { + if (endIndex < 0) + return -1; + var instrs = method.Body.Instructions; + for (int i = endIndex; i >= 0; i--) { + if (i == 0) + return i == endIndex ? -1 : i + 1; + if (instrs[i].OpCode.FlowControl == FlowControl.Next) + continue; + + return i + 1; + } + return -1; + } + + protected byte[] decryptConstant_v17_r73740_dynamic(DecrypterInfo info, byte[] encrypted, uint offs, uint key) { + var local = getDynamicLocal_v17_r73740(info.decryptMethod); + if (local == null) + throw new ApplicationException("Could not find local"); + + int endIndex = getDynamicEndIndex_v17_r73740(info.decryptMethod, local); + int startIndex = getDynamicStartIndex_v17_r73740(info.decryptMethod, endIndex); + if (startIndex < 0) + throw new ApplicationException("Could not find start/end index"); + + var constReader = new ConstantsReader(info.decryptMethod); + return decrypt(encrypted, key, magic => { + constReader.setConstantInt32(local, magic); + int index = startIndex, result; + if (!constReader.getNextInt32(ref index, out result) || index != endIndex) + throw new ApplicationException("Could not decrypt integer"); + return (byte)result; + }); + } + + protected byte[] decryptConstant_v17_r73764_native(DecrypterInfo info, byte[] encrypted, uint offs, uint key) { + var x86Emu = new x86Emulator(new PeImage(fileData)); + return decrypt(encrypted, key, magic => (byte)x86Emu.emulate((uint)nativeMethod.RVA, magic)); + } + + static byte[] decrypt(byte[] encrypted, uint key, Func decryptFunc) { + var reader = new BinaryReader(new MemoryStream(encrypted)); + var decrypted = new byte[reader.ReadInt32() ^ key]; + for (int i = 0; i < decrypted.Length; i++) { + uint magic = Utils.readEncodedUInt32(reader); + decrypted[i] = decryptFunc(magic); + } + + return decrypted; + } + public object decryptInt32(MethodDefinition caller, MethodDefinition decryptMethod, object[] args) { var info = methodToDecrypterInfo.find(decryptMethod); byte typeCode; diff --git a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterUtils.cs b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterUtils.cs index 77269fa9..225d7f0b 100644 --- a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterUtils.cs +++ b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterUtils.cs @@ -71,6 +71,14 @@ namespace de4dot.code.deobfuscators.Confuser { } public static FieldDefinition findStreamField(MethodDefinition method, TypeDefinition declaringType) { + return findStreamField(method, declaringType, "System.IO.Stream"); + } + + public static FieldDefinition findMemoryStreamField(MethodDefinition method, TypeDefinition declaringType) { + return findStreamField(method, declaringType, "System.IO.MemoryStream"); + } + + public static FieldDefinition findStreamField(MethodDefinition method, TypeDefinition declaringType, string fieldTypeName) { var instrs = method.Body.Instructions; for (int i = 0; i < instrs.Count - 1; i++) { var newobj = instrs[i]; @@ -86,7 +94,7 @@ namespace de4dot.code.deobfuscators.Confuser { var field = stsfld.Operand as FieldDefinition; if (field == null || field.DeclaringType != declaringType) continue; - if (field.FieldType.FullName != "System.IO.MemoryStream") + if (field.FieldType.FullName != fieldTypeName) continue; return field; diff --git a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV15.cs b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV15.cs index d13b4301..11ffbb9f 100644 --- a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV15.cs +++ b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV15.cs @@ -23,7 +23,6 @@ using System.IO; using Mono.Cecil; using Mono.Cecil.Cil; using de4dot.blocks; -using de4dot.PE; namespace de4dot.code.deobfuscators.Confuser { class ConstantsDecrypterV15 : ConstantsDecrypterBase { @@ -91,7 +90,7 @@ namespace de4dot.code.deobfuscators.Confuser { version = ConfuserVersion.v17_r73740_dynamic; } else if (DotNetUtils.callsMethod(method, "System.String System.Text.Encoding::GetString(System.Byte[],System.Int32,System.Int32)")) { - if ((info.nativeMethod = findNativeMethod(method)) == null) + if ((nativeMethod = findNativeMethod(method)) == null) version = ConfuserVersion.v17_r73764_dynamic; else version = ConfuserVersion.v17_r73764_native; @@ -104,7 +103,7 @@ namespace de4dot.code.deobfuscators.Confuser { DeobUtils.hasInteger(method, 0x10000) && DeobUtils.hasInteger(method, 0xFFFF)) version = ConfuserVersion.v17_r73822_normal; - else if ((info.nativeMethod = findNativeMethod(method)) == null) + else if ((nativeMethod = findNativeMethod(method)) == null) version = ConfuserVersion.v17_r73822_dynamic; else version = ConfuserVersion.v17_r73822_native; @@ -119,23 +118,6 @@ namespace de4dot.code.deobfuscators.Confuser { } } - static MethodDefinition findNativeMethod(MethodDefinition method) { - var instrs = method.Body.Instructions; - for (int i = 0; i < instrs.Count; i++) { - var call = instrs[i]; - 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; - } - public override void initialize() { if ((resource = findResource(theDecrypterInfo.decryptMethod)) == null) throw new ApplicationException("Could not find encrypted consts resource"); @@ -144,8 +126,7 @@ namespace de4dot.code.deobfuscators.Confuser { if (!initializeFields(theDecrypterInfo)) throw new ApplicationException("Could not find all fields"); - var constants = DeobUtils.inflate(resource.GetResourceData(), true); - reader = new BinaryReader(new MemoryStream(constants)); + setConstantsData(DeobUtils.inflate(resource.GetResourceData(), true)); } bool initializeFields(DecrypterInfo info) { @@ -155,7 +136,7 @@ namespace de4dot.code.deobfuscators.Confuser { case ConfuserVersion.v17_r73822_native: if (!add(ConstantsDecrypterUtils.findDictField(info.decryptMethod, info.decryptMethod.DeclaringType))) return false; - if (!add(ConstantsDecrypterUtils.findStreamField(info.decryptMethod, info.decryptMethod.DeclaringType))) + if (!add(ConstantsDecrypterUtils.findMemoryStreamField(info.decryptMethod, info.decryptMethod.DeclaringType))) return false; break; @@ -166,10 +147,6 @@ namespace de4dot.code.deobfuscators.Confuser { return true; } - EmbeddedResource findResource(MethodDefinition method) { - return DotNetUtils.getResource(module, DotNetUtils.getCodeStrings(method)) as EmbeddedResource; - } - protected override byte[] decryptData(DecrypterInfo info, MethodDefinition caller, object[] args, out byte typeCode) { uint offs = info.calcHash(caller.MetadataToken.ToUInt32()) ^ (uint)args[0]; reader.BaseStream.Position = offs; @@ -188,12 +165,12 @@ namespace de4dot.code.deobfuscators.Confuser { case ConfuserVersion.v15_r60785_normal: return decryptConstant_v15_r60785_normal(info, encrypted, offs); case ConfuserVersion.v15_r60785_dynamic: return decryptConstant_v15_r60785_dynamic(info, encrypted, offs); case ConfuserVersion.v17_r73404_normal: return decryptConstant_v17_r73404_normal(info, encrypted, offs); - case ConfuserVersion.v17_r73740_dynamic: return decryptConstant_v17_r73740_dynamic(info, encrypted, offs); - case ConfuserVersion.v17_r73764_dynamic: return decryptConstant_v17_r73740_dynamic(info, encrypted, offs); - case ConfuserVersion.v17_r73764_native: return decryptConstant_v17_r73764_native(info, encrypted, offs); + case ConfuserVersion.v17_r73740_dynamic: return decryptConstant_v17_r73740_dynamic(info, encrypted, offs, 0); + case ConfuserVersion.v17_r73764_dynamic: return decryptConstant_v17_r73740_dynamic(info, encrypted, offs, 0); + case ConfuserVersion.v17_r73764_native: return decryptConstant_v17_r73764_native(info, encrypted, offs, 0); case ConfuserVersion.v17_r73822_normal: return decryptConstant_v17_r73404_normal(info, encrypted, offs); - case ConfuserVersion.v17_r73822_dynamic: return decryptConstant_v17_r73740_dynamic(info, encrypted, offs); - case ConfuserVersion.v17_r73822_native: return decryptConstant_v17_r73764_native(info, encrypted, offs); + case ConfuserVersion.v17_r73822_dynamic: return decryptConstant_v17_r73740_dynamic(info, encrypted, offs, 0); + case ConfuserVersion.v17_r73822_native: return decryptConstant_v17_r73764_native(info, encrypted, offs, 0); default: throw new ApplicationException("Invalid version"); } } @@ -254,103 +231,5 @@ namespace de4dot.code.deobfuscators.Confuser { byte[] decryptConstant_v17_r73404_normal(DecrypterInfo info, byte[] encrypted, uint offs) { return ConfuserUtils.decrypt(info.key0 ^ offs, encrypted); } - - static VariableDefinition getDynamicLocal_v17_r73740(MethodDefinition method) { - var instrs = method.Body.Instructions; - for (int i = 0; i < instrs.Count; i++) { - i = ConfuserUtils.findCallMethod(instrs, i, Code.Callvirt, "System.Byte System.IO.BinaryReader::ReadByte()"); - if (i < 0 || i + 5 >= instrs.Count) - break; - if (!DotNetUtils.isStloc(instrs[i + 1])) - continue; - var ldloc = instrs[i + 2]; - if (!DotNetUtils.isLdloc(ldloc)) - continue; - if (!DotNetUtils.isLdloc(instrs[i + 3])) - continue; - var ldci4 = instrs[i + 4]; - if (!DotNetUtils.isLdcI4(ldci4) || DotNetUtils.getLdcI4Value(ldci4) != 0x7F) - continue; - if (instrs[i + 5].OpCode.Code != Code.And) - continue; - - return DotNetUtils.getLocalVar(method.Body.Variables, ldloc); - } - return null; - } - - static int getDynamicEndIndex_v17_r73740(MethodDefinition method, VariableDefinition local) { - var instrs = method.Body.Instructions; - for (int i = 0; i < instrs.Count - 5; i++) { - var stloc = instrs[i]; - if (!DotNetUtils.isStloc(stloc) || DotNetUtils.getLocalVar(method.Body.Variables, stloc) != local) - continue; - if (!DotNetUtils.isLdloc(instrs[i + 1])) - continue; - if (!DotNetUtils.isLdloc(instrs[i + 2])) - continue; - var ldloc = instrs[i + 3]; - if (!DotNetUtils.isLdloc(ldloc) || DotNetUtils.getLocalVar(method.Body.Variables, ldloc) != local) - continue; - if (instrs[i + 4].OpCode.Code != Code.Conv_U1) - continue; - if (instrs[i + 5].OpCode.Code != Code.Stelem_I1) - continue; - - return i; - } - return -1; - } - - static int getDynamicStartIndex_v17_r73740(MethodDefinition method, int endIndex) { - if (endIndex < 0) - return -1; - var instrs = method.Body.Instructions; - for (int i = endIndex; i >= 0; i--) { - if (i == 0) - return i == endIndex ? -1 : i + 1; - if (instrs[i].OpCode.FlowControl == FlowControl.Next) - continue; - - return i + 1; - } - return -1; - } - - byte[] decryptConstant_v17_r73740_dynamic(DecrypterInfo info, byte[] encrypted, uint offs) { - var local = getDynamicLocal_v17_r73740(info.decryptMethod); - if (local == null) - throw new ApplicationException("Could not find local"); - - int endIndex = getDynamicEndIndex_v17_r73740(info.decryptMethod, local); - int startIndex = getDynamicStartIndex_v17_r73740(info.decryptMethod, endIndex); - if (startIndex < 0) - throw new ApplicationException("Could not find start/end index"); - - var constReader = new ConstantsReader(info.decryptMethod); - return decrypt(encrypted, magic => { - constReader.setConstantInt32(local, magic); - int index = startIndex, result; - if (!constReader.getNextInt32(ref index, out result) || index != endIndex) - throw new ApplicationException("Could not decrypt integer"); - return (byte)result; - }); - } - - byte[] decryptConstant_v17_r73764_native(DecrypterInfo info, byte[] encrypted, uint offs) { - var x86Emu = new x86Emulator(new PeImage(fileData)); - return decrypt(encrypted, magic => (byte)x86Emu.emulate((uint)info.nativeMethod.RVA, magic)); - } - - byte[] decrypt(byte[] encrypted, Func decryptFunc) { - var reader = new BinaryReader(new MemoryStream(encrypted)); - var decrypted = new byte[reader.ReadInt32()]; - for (int i = 0; i < decrypted.Length; i++) { - uint magic = Utils.readEncodedUInt32(reader); - decrypted[i] = decryptFunc(magic); - } - - return decrypted; - } } } diff --git a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV17.cs b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV17.cs new file mode 100644 index 00000000..ee240bbd --- /dev/null +++ b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV17.cs @@ -0,0 +1,276 @@ +/* + 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 Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Confuser { + // Since v1.7 r74708 + class ConstantsDecrypterV17 : ConstantsDecrypterBase { + MethodDefinition initMethod; + ConfuserVersion version = ConfuserVersion.Unknown; + + enum ConfuserVersion { + Unknown, + v17_r74708_normal, + v17_r74708_dynamic, + v17_r74708_native, + } + + class DecrypterInfoV17 : DecrypterInfo { + public readonly ConfuserVersion version = ConfuserVersion.Unknown; + public uint key4; + + public DecrypterInfoV17(ConfuserVersion version, MethodDefinition decryptMethod) { + this.version = version; + this.decryptMethod = decryptMethod; + } + + protected override bool initializeKeys() { + if (!findKey0(decryptMethod, out key0)) + return false; + if (!findKey1_v17(decryptMethod, out key1)) + return false; + if (!findKey2Key3(decryptMethod, out key2, out key3)) + return false; + if (!findKey4(decryptMethod, out key4)) + return false; + + return true; + } + + static bool findKey1_v17(MethodDefinition method, out uint key) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 4; i++) { + var stloc = instrs[i]; + if (!DotNetUtils.isStloc(stloc)) + continue; + var ldci4 = instrs[i + 1]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + var ldcloc = instrs[i + 2]; + if (!DotNetUtils.isLdloc(ldcloc)) + continue; + if (DotNetUtils.getLocalVar(method.Body.Variables, stloc) != DotNetUtils.getLocalVar(method.Body.Variables, ldcloc)) + continue; + if (instrs[i + 3].OpCode.Code != Code.Xor) + continue; + if (!DotNetUtils.isStloc(instrs[i + 4])) + continue; + + key = (uint)DotNetUtils.getLdcI4Value(ldci4); + return true; + } + key = 0; + return false; + } + + bool findKey4(MethodDefinition method, out uint key) { + switch (version) { + case ConfuserVersion.v17_r74708_normal: return findKey4_normal(method, out key); + case ConfuserVersion.v17_r74708_dynamic: return findKey4_other(method, out key); + case ConfuserVersion.v17_r74708_native: return findKey4_other(method, out key); + default: + throw new ApplicationException("Invalid version"); + } + } + + static bool findKey4_normal(MethodDefinition method, out uint key) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 5; i++) { + if (!DotNetUtils.isLdloc(instrs[i])) + continue; + if (!DotNetUtils.isLdloc(instrs[i + 1])) + continue; + if (instrs[i + 2].OpCode.Code != Code.Add) + continue; + var ldci4 = instrs[i + 3]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + if (instrs[i + 4].OpCode.Code != Code.Mul) + continue; + if (!DotNetUtils.isStloc(instrs[i + 5])) + continue; + + key = (uint)DotNetUtils.getLdcI4Value(ldci4); + return true; + } + key = 0; + return false; + } + + static bool findKey4_other(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.Int32 System.IO.BinaryReader::ReadInt32()"); + if (index < 0) + break; + if (index + 1 >= instrs.Count) + break; + var ldci4 = instrs[index + 1]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + + key = (uint)DotNetUtils.getLdcI4Value(ldci4); + return true; + } + key = 0; + return false; + } + } + + public override bool Detected { + get { return initMethod != null; } + } + + public ConstantsDecrypterV17(ModuleDefinition module, byte[] fileData, ISimpleDeobfuscator simpleDeobfuscator) + : base(module, fileData, simpleDeobfuscator) { + } + + static readonly string[] requiredLocalsCctor = new string[] { + "System.Reflection.Assembly", + "System.IO.Compression.DeflateStream", + "System.Byte[]", + "System.Int32", + }; + public void find() { + var cctor = DotNetUtils.getModuleTypeCctor(module); + if (cctor == null) + return; + if (!new LocalTypes(cctor).all(requiredLocalsCctor)) + return; + + simpleDeobfuscator.deobfuscate(cctor, true); + if (!add(ConstantsDecrypterUtils.findDictField(cctor, cctor.DeclaringType))) + return; + if (!add(ConstantsDecrypterUtils.findStreamField(cctor, cctor.DeclaringType))) + return; + + var method = getDecryptMethod(); + if (method == null) + return; + if (DeobUtils.hasInteger(method, 0x100) && + DeobUtils.hasInteger(method, 0x10000) && + DeobUtils.hasInteger(method, 0xFFFF)) + version = ConfuserVersion.v17_r74708_normal; + else if ((nativeMethod = findNativeMethod(method)) == null) + version = ConfuserVersion.v17_r74708_dynamic; + else + version = ConfuserVersion.v17_r74708_native; + + initMethod = cctor; + } + + MethodDefinition getDecryptMethod() { + foreach (var type in module.Types) { + if (type.Attributes != (TypeAttributes.Abstract | TypeAttributes.Sealed)) + continue; + if (!checkMethods(type.Methods)) + continue; + foreach (var method in type.Methods) { + if (!DotNetUtils.isMethod(method, "System.Object", "(System.UInt32,System.UInt32)")) + continue; + + return method; + } + } + return null; + } + + protected override byte[] decryptData(DecrypterInfo info2, MethodDefinition caller, object[] args, out byte typeCode) { + var info = (DecrypterInfoV17)info2; + uint offs = info.calcHash(info2.decryptMethod.MetadataToken.ToUInt32() ^ (info2.decryptMethod.DeclaringType.MetadataToken.ToUInt32() * (uint)args[0])) ^ (uint)args[1]; + reader.BaseStream.Position = offs; + typeCode = reader.ReadByte(); + if (typeCode != info.int32Type && typeCode != info.int64Type && + typeCode != info.singleType && typeCode != info.doubleType && + typeCode != info.stringType) + throw new ApplicationException("Invalid type code"); + + var encrypted = reader.ReadBytes(reader.ReadInt32()); + return decryptConstant(info, encrypted, offs, typeCode); + } + + byte[] decryptConstant(DecrypterInfoV17 info, byte[] encrypted, uint offs, byte typeCode) { + switch (info.version) { + case ConfuserVersion.v17_r74708_normal: return decryptConstant_v17_r74708_normal(info, encrypted, offs, typeCode); + case ConfuserVersion.v17_r74708_dynamic: return decryptConstant_v17_r74708_dynamic(info, encrypted, offs, typeCode); + case ConfuserVersion.v17_r74708_native: return decryptConstant_v17_r74708_native(info, encrypted, offs, typeCode); + default: + throw new ApplicationException("Invalid version"); + } + } + + byte[] decryptConstant_v17_r74708_normal(DecrypterInfoV17 info, byte[] encrypted, uint offs, byte typeCode) { + return ConfuserUtils.decrypt(info.key4 * (offs + typeCode), encrypted); + } + + byte[] decryptConstant_v17_r74708_dynamic(DecrypterInfoV17 info, byte[] encrypted, uint offs, byte typeCode) { + return decryptConstant_v17_r73740_dynamic(info, encrypted, offs, info.key4); + } + + byte[] decryptConstant_v17_r74708_native(DecrypterInfoV17 info, byte[] encrypted, uint offs, byte typeCode) { + return decryptConstant_v17_r73764_native(info, encrypted, offs, info.key4); + } + + public override void initialize() { + if ((resource = findResource(initMethod)) == null) + throw new ApplicationException("Could not find encrypted consts resource"); + + findDecrypterInfos(); + initializeDecrypterInfos(); + + setConstantsData(DeobUtils.inflate(resource.GetResourceData(), true)); + } + + void findDecrypterInfos() { + foreach (var type in module.Types) { + if (type.Attributes != (TypeAttributes.Abstract | TypeAttributes.Sealed)) + continue; + if (!checkMethods(type.Methods)) + continue; + foreach (var method in type.Methods) { + if (!DotNetUtils.isMethod(method, "System.Object", "(System.UInt32,System.UInt32)")) + continue; + + var info = new DecrypterInfoV17(version, method); + add(info); + } + } + } + + static bool checkMethods(IEnumerable methods) { + int numMethods = 0; + foreach (var method in methods) { + if (method.Name == ".ctor" || method.Name == ".cctor") + return false; + if (method.Attributes != (MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.CompilerControlled)) + return false; + if (!DotNetUtils.isMethod(method, "System.Object", "(System.UInt32,System.UInt32)")) + return false; + + numMethods++; + } + return numMethods > 0; + } + } +} diff --git a/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs b/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs index 6ae257a3..ee60d834 100644 --- a/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs @@ -80,6 +80,7 @@ namespace de4dot.code.deobfuscators.Confuser { ResourceDecrypter resourceDecrypter; ConstantsDecrypter constantsDecrypter; ConstantsDecrypterV15 constantsDecrypterV15; + ConstantsDecrypterV17 constantsDecrypterV17; Int32ValueInliner int32ValueInliner; Int64ValueInliner int64ValueInliner; SingleValueInliner singleValueInliner; @@ -140,6 +141,7 @@ namespace de4dot.code.deobfuscators.Confuser { toInt32(resourceDecrypter != null ? resourceDecrypter.Detected : false) + toInt32(constantsDecrypter != null ? constantsDecrypter.Detected : false) + toInt32(constantsDecrypterV15 != null ? constantsDecrypterV15.Detected : false) + + toInt32(constantsDecrypterV17 != null ? constantsDecrypterV17.Detected : false) + toInt32(stringDecrypter != null ? stringDecrypter.Detected : false) + toInt32(unpacker != null ? unpacker.Detected : false); if (sum > 0) @@ -167,15 +169,28 @@ namespace de4dot.code.deobfuscators.Confuser { void initTheRest() { resourceDecrypter = new ResourceDecrypter(module, DeobfuscatedFile); resourceDecrypter.find(); + constantsDecrypter = new ConstantsDecrypter(module, getFileData(), DeobfuscatedFile); - constantsDecrypter.find(); + constantsDecrypterV17 = new ConstantsDecrypterV17(module, getFileData(), DeobfuscatedFile); constantsDecrypterV15 = new ConstantsDecrypterV15(module, getFileData(), DeobfuscatedFile); - if (!constantsDecrypter.Detected) + do { + constantsDecrypter.find(); + if (constantsDecrypter.Detected) { + initializeConstantsDecrypter(); + break; + } + constantsDecrypterV17.find(); + if (constantsDecrypterV17.Detected) { + initializeConstantsDecrypter17(); + break; + } constantsDecrypterV15.find(); - if (constantsDecrypter.Detected) - initializeConstantsDecrypter(); - else if (constantsDecrypterV15.Detected) - initializeConstantsDecrypter15(); + if (constantsDecrypterV15.Detected) { + initializeConstantsDecrypter15(); + break; + } + } while (false); + proxyCallFixer = new ProxyCallFixer(module, getFileData(), DeobfuscatedFile); proxyCallFixer.findDelegateCreator(); if (!proxyCallFixer.Detected) { @@ -431,6 +446,11 @@ namespace de4dot.code.deobfuscators.Confuser { initialize(constantsDecrypterV15, ref hasInitializedConstantsDecrypter15); } + bool hasInitializedConstantsDecrypter17 = false; + void initializeConstantsDecrypter17() { + initialize(constantsDecrypterV17, ref hasInitializedConstantsDecrypter17); + } + void initialize(ConstantsDecrypterBase constDecrypter, ref bool hasInitialized) { if (hasInitialized || (constDecrypter == null || !constDecrypter.Detected)) return; @@ -455,10 +475,14 @@ namespace de4dot.code.deobfuscators.Confuser { doubleValueInliner.RemoveUnbox = true; DeobfuscatedFile.stringDecryptersAdded(); addFieldsToBeRemoved(constDecrypter.Fields, "Constants decrypter field"); + var moduleType = DotNetUtils.getModuleType(module); foreach (var info in constDecrypter.DecrypterInfos) { - addMethodToBeRemoved(info.decryptMethod, "Constants decrypter method"); - addMethodToBeRemoved(info.nativeMethod, "Constants decrypter native method"); + if (info.decryptMethod.DeclaringType == moduleType) + addMethodToBeRemoved(info.decryptMethod, "Constants decrypter method"); + else + addTypeToBeRemoved(info.decryptMethod.DeclaringType, "Constants decrypter type"); } + addMethodToBeRemoved(constDecrypter.NativeMethod, "Constants decrypter native method"); addResourceToBeRemoved(constDecrypter.Resource, "Encrypted constants"); }