From 6e262eb621c45ea12c76d9e00b4a555db3ae424d Mon Sep 17 00:00:00 2001 From: de4dot Date: Wed, 1 Aug 2012 22:38:54 +0200 Subject: [PATCH] Add support for Confuser 1.5 r60785 constants encrypter (normal mode) --- de4dot.code/de4dot.code.csproj | 1 + .../deobfuscators/Confuser/ConfuserUtils.cs | 10 + .../Confuser/ConstantsDecrypterV15.cs | 330 ++++++++++++++++++ .../deobfuscators/Confuser/Deobfuscator.cs | 56 ++- 4 files changed, 386 insertions(+), 11 deletions(-) create mode 100644 de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV15.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 55d54e57..4bd9ec32 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -71,6 +71,7 @@ + diff --git a/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs b/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs index 0a9f25fe..bed35103 100644 --- a/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs +++ b/de4dot.code/deobfuscators/Confuser/ConfuserUtils.cs @@ -34,6 +34,16 @@ namespace de4dot.code.deobfuscators.Confuser { return -1; } + public static int findCallMethod(IList instrs, int index, Code callCode, string methodFullName) { + for (int i = index; i < instrs.Count; i++) { + if (!isCallMethod(instrs[i].Instruction, callCode, methodFullName)) + continue; + + return i; + } + return -1; + } + public static bool isCallMethod(Instruction instr, Code callCode, string methodFullName) { if (instr.OpCode.Code != callCode) return false; diff --git a/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV15.cs b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV15.cs new file mode 100644 index 00000000..d8b82a51 --- /dev/null +++ b/de4dot.code/deobfuscators/Confuser/ConstantsDecrypterV15.cs @@ -0,0 +1,330 @@ +/* + 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 de4dot.blocks; + +namespace de4dot.code.deobfuscators.Confuser { + class ConstantsDecrypterV15 { + ModuleDefinition module; + ISimpleDeobfuscator simpleDeobfuscator; + MethodDefinition decryptMethod; + EmbeddedResource resource; + uint key0, key1, key2, key3; + byte doubleType, singleType, int32Type, int64Type, stringType; + BinaryReader reader; + + public MethodDefinition Method { + get { return decryptMethod; } + } + + public EmbeddedResource Resource { + get { return resource; } + } + + public bool Detected { + get { return decryptMethod != null; } + } + + public ConstantsDecrypterV15(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator) { + this.module = module; + this.simpleDeobfuscator = simpleDeobfuscator; + } + + static readonly string[] requiredLocals = new string[] { + "System.Byte[]", + "System.Collections.Generic.Dictionary`2", + "System.IO.BinaryReader", + "System.IO.Compression.DeflateStream", + "System.IO.MemoryStream", + "System.Random", + "System.Reflection.Assembly", + }; + public void find() { + var type = DotNetUtils.getModuleType(module); + if (type == null) + return; + foreach (var method in type.Methods) { + if (!method.IsStatic || method.Body == null) + continue; + if (!DotNetUtils.isMethod(method, "System.Object", "(System.UInt32)")) + continue; + var localTypes = new LocalTypes(method); + if (!localTypes.all(requiredLocals)) + continue; + + decryptMethod = method; + break; + } + } + + public void initialize() { + if ((resource = findResource(decryptMethod)) == null) + throw new ApplicationException("Could not find encrypted consts resource"); + + if (!initializeKeys()) + throw new ApplicationException("Could not find all keys"); + if (!initializeTypeCodes()) + throw new ApplicationException("Could not find all type codes"); + + var constants = DeobUtils.inflate(resource.GetResourceData(), true); + reader = new BinaryReader(new MemoryStream(constants)); + } + + bool initializeKeys() { + if (!findKey0(decryptMethod, out key0)) + return false; + if (!findKey1(decryptMethod, out key1)) + return false; + if (!findKey2Key3(decryptMethod, out key2, out key3)) + return false; + + return true; + } + + 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])) + continue; + if (instrs[i + 1].OpCode.Code != Code.Or) + continue; + var ldci4 = instrs[i + 2]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + if (instrs[i + 3].OpCode.Code != Code.Xor) + continue; + if (instrs[i + 4].OpCode.Code != Code.Add) + continue; + if (!DotNetUtils.isStloc(instrs[i + 5])) + continue; + + key = (uint)DotNetUtils.getLdcI4Value(ldci4); + return true; + } + key = 0; + return false; + } + + static bool findKey1(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.Reflection.MemberInfo::get_MetadataToken()"); + if (index < 0) + break; + if (index + 2 > instrs.Count) + break; + if (!DotNetUtils.isStloc(instrs[index + 1])) + continue; + var ldci4 = instrs[index + 2]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + + key = (uint)DotNetUtils.getLdcI4Value(ldci4); + return true; + } + key = 0; + return false; + } + + 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]; + if (!DotNetUtils.isLdcI4(ldci4_1)) + continue; + if (!DotNetUtils.isStloc(instrs[i + 1])) + continue; + var ldci4_2 = instrs[i + 2]; + if (!DotNetUtils.isLdcI4(ldci4_2)) + continue; + if (!DotNetUtils.isStloc(instrs[i + 3])) + continue; + + key2 = (uint)DotNetUtils.getLdcI4Value(ldci4_1); + key3 = (uint)DotNetUtils.getLdcI4Value(ldci4_2); + return true; + } + key2 = 0; + key3 = 0; + return false; + } + + bool initializeTypeCodes() { + var allBlocks = new Blocks(decryptMethod).MethodBlocks.getAllBlocks(); + if (!findTypeCode(allBlocks, out doubleType, Code.Call, "System.Double System.BitConverter::ToDouble(System.Byte[],System.Int32)")) + return false; + if (!findTypeCode(allBlocks, out singleType, Code.Call, "System.Single System.BitConverter::ToSingle(System.Byte[],System.Int32)")) + return false; + if (!findTypeCode(allBlocks, out int32Type, Code.Call, "System.Int32 System.BitConverter::ToInt32(System.Byte[],System.Int32)")) + return false; + if (!findTypeCode(allBlocks, out int64Type, Code.Call, "System.Int64 System.BitConverter::ToInt64(System.Byte[],System.Int32)")) + return false; + if (!findTypeCode(allBlocks, out stringType, Code.Callvirt, "System.String System.Text.Encoding::GetString(System.Byte[])")) + return false; + return true; + } + + static bool findTypeCode(IList allBlocks, out byte typeCode, Code callCode, string bitConverterMethod) { + foreach (var block in allBlocks) { + if (block.Sources.Count != 1) + continue; + int index = ConfuserUtils.findCallMethod(block.Instructions, 0, callCode, bitConverterMethod); + if (index < 0) + continue; + + if (!findTypeCode(block.Sources[0], out typeCode)) + continue; + + return true; + } + typeCode = 0; + return false; + } + + static bool findTypeCode(Block block, out byte typeCode) { + var instrs = block.Instructions; + int numCeq = 0; + for (int i = instrs.Count - 1; i >= 0; i--) { + var instr = instrs[i]; + if (instr.OpCode.Code == Code.Ceq) { + numCeq++; + continue; + } + if (!DotNetUtils.isLdcI4(instr.Instruction)) + continue; + if (numCeq != 0 && numCeq != 2) + continue; + + typeCode = (byte)DotNetUtils.getLdcI4Value(instr.Instruction); + return true; + } + typeCode = 0; + return false; + } + + EmbeddedResource findResource(MethodDefinition method) { + return DotNetUtils.getResource(module, DotNetUtils.getCodeStrings(method)) as EmbeddedResource; + } + + public object decryptInt32(MethodDefinition caller, uint magic) { + byte typeCode; + var data = decryptData(caller, magic, out typeCode); + if (typeCode != int32Type) + return null; + if (data.Length != 4) + throw new ApplicationException("Invalid data length"); + return BitConverter.ToInt32(data, 0); + } + + public object decryptInt64(MethodDefinition caller, uint magic) { + byte typeCode; + var data = decryptData(caller, magic, out typeCode); + if (typeCode != int64Type) + return null; + if (data.Length != 8) + throw new ApplicationException("Invalid data length"); + return BitConverter.ToInt64(data, 0); + } + + public object decryptSingle(MethodDefinition caller, uint magic) { + byte typeCode; + var data = decryptData(caller, magic, out typeCode); + if (typeCode != singleType) + return null; + if (data.Length != 4) + throw new ApplicationException("Invalid data length"); + return BitConverter.ToSingle(data, 0); + } + + public object decryptDouble(MethodDefinition caller, uint magic) { + byte typeCode; + var data = decryptData(caller, magic, out typeCode); + if (typeCode != doubleType) + return null; + if (data.Length != 8) + throw new ApplicationException("Invalid data length"); + return BitConverter.ToDouble(data, 0); + } + + public string decryptString(MethodDefinition caller, uint magic) { + byte typeCode; + var data = decryptData(caller, magic, out typeCode); + if (typeCode != stringType) + return null; + return Encoding.UTF8.GetString(data); + } + + byte[] decryptData(MethodDefinition caller, uint magic, out byte typeCode) { + uint offs = calcHash(caller.MetadataToken.ToUInt32()) ^ magic; + reader.BaseStream.Position = offs; + typeCode = reader.ReadByte(); + if (typeCode != int32Type && typeCode != int64Type && + typeCode != singleType && typeCode != doubleType && + typeCode != stringType) + throw new ApplicationException("Invalid type code"); + + var encrypted = reader.ReadBytes(reader.ReadInt32()); + var rand = new Random((int)(key0 ^ offs)); + var decrypted = new byte[encrypted.Length]; + rand.NextBytes(decrypted); + for (int i = 0; i < decrypted.Length; i++) + decrypted[i] ^= encrypted[i]; + + return decrypted; + + } + + uint calcHash(uint x) { + uint h0 = key1 ^ x; + uint h1 = key2; + uint h2 = key3; + for (uint i = 1; i <= 64; i++) { + h0 = (h0 << 8) | (h0 >> 24); + uint n = h0 & 0x3F; + if (n >= 0 && n < 16) { + h1 |= ((byte)(h0 >> 8) & (h0 >> 16)) ^ (byte)~h0; + h2 ^= (h0 * i + 1) & 0xF; + h0 += (h1 | h2) ^ key0; + } + else if (n >= 16 && n < 32) { + h1 ^= ((h0 & 0x00FF00FF) << 8) ^ (ushort)((h0 >> 8) | ~h0); + h2 += (h0 * i) & 0x1F; + h0 |= (h1 + ~h2) & key0; + } + else if (n >= 32 && n < 48) { + h1 += (byte)(h0 | (h0 >> 16)) + (~h0 & 0xFF); + h2 -= ~(h0 + n) % 48; + h0 ^= (h1 % h2) | key0; + } + else if (n >= 48 && n < 64) { + h1 ^= ((byte)(h0 >> 16) | ~(h0 & 0xFF)) * (~h0 & 0x00FF0000); + h2 += (h0 ^ (i - 1)) % n; + h0 -= ~(h1 ^ h2) + key0; + } + } + return h0; + } + } +} diff --git a/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs b/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs index 1176bdb6..7da67659 100644 --- a/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Confuser/Deobfuscator.cs @@ -79,6 +79,7 @@ namespace de4dot.code.deobfuscators.Confuser { AntiDumping antiDumping; ResourceDecrypter resourceDecrypter; ConstantsDecrypter constantsDecrypter; + ConstantsDecrypterV15 constantsDecrypterV15; Int32ValueInliner int32ValueInliner; Int64ValueInliner int64ValueInliner; SingleValueInliner singleValueInliner; @@ -137,6 +138,7 @@ namespace de4dot.code.deobfuscators.Confuser { toInt32(antiDumping != null ? antiDumping.Detected : false) + toInt32(resourceDecrypter != null ? resourceDecrypter.Detected : false) + toInt32(constantsDecrypter != null ? constantsDecrypter.Detected : false) + + toInt32(constantsDecrypterV15 != null ? constantsDecrypterV15.Detected : false) + toInt32(stringDecrypter != null ? stringDecrypter.Detected : false) + toInt32(unpacker != null ? unpacker.Detected : false); if (sum > 0) @@ -166,8 +168,13 @@ namespace de4dot.code.deobfuscators.Confuser { resourceDecrypter.find(); constantsDecrypter = new ConstantsDecrypter(module, getFileData(), DeobfuscatedFile); constantsDecrypter.find(); + constantsDecrypterV15 = new ConstantsDecrypterV15(module, DeobfuscatedFile); + if (!constantsDecrypter.Detected) + constantsDecrypterV15.find(); if (constantsDecrypter.Detected) initializeConstantsDecrypter(); + else if (constantsDecrypterV15.Detected) + initializeConstantsDecrypter15(); proxyCallFixer = new ProxyCallFixer(module, getFileData(), DeobfuscatedFile); proxyCallFixer.findDelegateCreator(); if (!proxyCallFixer.Detected) { @@ -279,6 +286,7 @@ namespace de4dot.code.deobfuscators.Confuser { removeObfuscatorAttribute(); initializeConstantsDecrypter(); + initializeConstantsDecrypter15(); initializeStringDecrypter(); if (jitMethodsDecrypter != null) { @@ -340,20 +348,18 @@ namespace de4dot.code.deobfuscators.Confuser { bool hasInitializedStringDecrypter = false; void initializeStringDecrypter() { - if (hasInitializedStringDecrypter) + if (hasInitializedStringDecrypter || (stringDecrypter== null || !stringDecrypter.Detected)) return; hasInitializedStringDecrypter = true; - if (stringDecrypter != null && stringDecrypter.Detected) { - decryptResources(); - stringDecrypter.initialize(); - staticStringInliner.add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.decrypt(staticStringInliner.Method, (int)args[0])); - } + decryptResources(); + stringDecrypter.initialize(); + staticStringInliner.add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.decrypt(staticStringInliner.Method, (int)args[0])); } bool hasInitializedConstantsDecrypter = false; void initializeConstantsDecrypter() { - if (hasInitializedConstantsDecrypter) + if (hasInitializedConstantsDecrypter || (constantsDecrypter == null || !constantsDecrypter.Detected)) return; hasInitializedConstantsDecrypter = true; @@ -377,6 +383,32 @@ namespace de4dot.code.deobfuscators.Confuser { addResourceToBeRemoved(constantsDecrypter.Resource, "Encrypted constants"); } + bool hasInitializedConstantsDecrypter15 = false; + void initializeConstantsDecrypter15() { + if (hasInitializedConstantsDecrypter15 || (constantsDecrypterV15 == null || !constantsDecrypterV15.Detected)) + return; + hasInitializedConstantsDecrypter15 = true; + + decryptResources(); + constantsDecrypterV15.initialize(); + int32ValueInliner = new Int32ValueInliner(); + int64ValueInliner = new Int64ValueInliner(); + singleValueInliner = new SingleValueInliner(); + doubleValueInliner = new DoubleValueInliner(); + staticStringInliner.add(constantsDecrypterV15.Method, (method, gim, args) => constantsDecrypterV15.decryptString(staticStringInliner.Method, (uint)args[0])); + int32ValueInliner.add(constantsDecrypterV15.Method, (method, gim, args) => constantsDecrypterV15.decryptInt32(int32ValueInliner.Method, (uint)args[0])); + int64ValueInliner.add(constantsDecrypterV15.Method, (method, gim, args) => constantsDecrypterV15.decryptInt64(int64ValueInliner.Method, (uint)args[0])); + singleValueInliner.add(constantsDecrypterV15.Method, (method, gim, args) => constantsDecrypterV15.decryptSingle(singleValueInliner.Method, (uint)args[0])); + doubleValueInliner.add(constantsDecrypterV15.Method, (method, gim, args) => constantsDecrypterV15.decryptDouble(doubleValueInliner.Method, (uint)args[0])); + int32ValueInliner.RemoveUnbox = true; + int64ValueInliner.RemoveUnbox = true; + singleValueInliner.RemoveUnbox = true; + doubleValueInliner.RemoveUnbox = true; + DeobfuscatedFile.stringDecryptersAdded(); + addMethodToBeRemoved(constantsDecrypterV15.Method, "Constants decrypter method"); + addResourceToBeRemoved(constantsDecrypterV15.Resource, "Encrypted constants"); + } + void decryptResources() { var rsrc = resourceDecrypter.mergeResources(); if (rsrc == null) @@ -400,10 +432,12 @@ namespace de4dot.code.deobfuscators.Confuser { proxyCallFixerV1.deobfuscate(blocks); resourceDecrypter.deobfuscate(blocks); unpacker.deobfuscate(blocks); - int32ValueInliner.decrypt(blocks); - int64ValueInliner.decrypt(blocks); - singleValueInliner.decrypt(blocks); - doubleValueInliner.decrypt(blocks); + if (int32ValueInliner != null) { + int32ValueInliner.decrypt(blocks); + int64ValueInliner.decrypt(blocks); + singleValueInliner.decrypt(blocks); + doubleValueInliner.decrypt(blocks); + } base.deobfuscateMethodEnd(blocks); }