From 6bde8b8b2053fcba960035b5d6ab32b1861dfabe Mon Sep 17 00:00:00 2001 From: de4dot Date: Wed, 26 Oct 2011 14:40:55 +0200 Subject: [PATCH] Decrypt some DNR 4.0 non-native obfuscated assemblies --- de4dot.code/de4dot.code.csproj | 2 + .../dotNET_Reactor/Deobfuscator.cs | 11 + .../dotNET_Reactor/EncryptedResource.cs | 191 ++++++++++++++++++ .../dotNET_Reactor/LocalTypes.cs | 55 +++++ .../dotNET_Reactor/MethodsDecrypter.cs | 127 ++++++++---- 5 files changed, 348 insertions(+), 38 deletions(-) create mode 100644 de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs create mode 100644 de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 5fb54e29..0b2b3665 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -70,6 +70,8 @@ + + diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs index 7a0caa55..d2259585 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs @@ -86,6 +86,17 @@ namespace de4dot.deobfuscators.dotNET_Reactor { methodsDecrypter.find(); } + public override byte[] getDecryptedModule() { + return methodsDecrypter.decrypt(DeobfuscatedFile); + } + + public override IDeobfuscator moduleReloaded(ModuleDefinition module) { + var newOne = new Deobfuscator(options); + newOne.setModule(module); + newOne.methodsDecrypter = new MethodsDecrypter(module, methodsDecrypter); + return newOne; + } + public override void deobfuscateBegin() { base.deobfuscateBegin(); } diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs b/de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs new file mode 100644 index 00000000..d6e768b2 --- /dev/null +++ b/de4dot.code/deobfuscators/dotNET_Reactor/EncryptedResource.cs @@ -0,0 +1,191 @@ +/* + Copyright (C) 2011 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.IO; +using System.Collections.Generic; +using System.Security.Cryptography; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; +using de4dot.blocks.cflow; + +namespace de4dot.deobfuscators.dotNET_Reactor { + class EncryptedResource { + ModuleDefinition module; + MethodDefinition resourceDecrypterMethod; + EmbeddedResource encryptedDataResource; + byte[] key, iv; + + public MethodDefinition ResourceDecrypterMethod { + get { return resourceDecrypterMethod; } + set { resourceDecrypterMethod = value; } + } + + public EncryptedResource(ModuleDefinition module) { + this.module = module; + } + + public EncryptedResource(ModuleDefinition module, EncryptedResource oldOne) { + this.module = module; + resourceDecrypterMethod = module.LookupToken(oldOne.resourceDecrypterMethod.MetadataToken.ToInt32()) as MethodDefinition; + encryptedDataResource = DotNetUtils.getResource(module, oldOne.encryptedDataResource.Name) as EmbeddedResource; + key = oldOne.key; + iv = oldOne.iv; + + if (resourceDecrypterMethod == null || encryptedDataResource == null || key == null || iv == null) + throw new ApplicationException("Could not initialize EncryptedResource"); + } + + public bool couldBeResourceDecrypter(MethodDefinition method) { + if (!method.IsStatic) + return false; + if (method.Body == null) + return false; + if (method.Body.Instructions.Count < 1000) + return false; + + var localTypes = new LocalTypes(method.Body.Variables); + var requiredTypes = new string[] { + "System.Byte[]", + "System.Diagnostics.StackFrame", + "System.IntPtr", + "System.IO.BinaryReader", + "System.IO.MemoryStream", + "System.Reflection.Assembly", + "System.Security.Cryptography.CryptoStream", + "System.Security.Cryptography.ICryptoTransform", + "System.Security.Cryptography.RijndaelManaged", + }; + if (!localTypes.all(requiredTypes)) + return false; + + if (findMethodsDecrypterResource(method) == null) + return false; + + return true; + } + + public void init(ISimpleDeobfuscator simpleDeobfuscator) { + if (resourceDecrypterMethod == null) + return; + + simpleDeobfuscator.deobfuscate(resourceDecrypterMethod); + + encryptedDataResource = findMethodsDecrypterResource(resourceDecrypterMethod); + if (encryptedDataResource == null) + throw new ApplicationException("Could not find encrypted resource"); + + key = initArray(resourceDecrypterMethod, 32); + if (key == null) + throw new ApplicationException("Could not find resource decrypter key"); + iv = initArray(resourceDecrypterMethod, 16); + if (iv == null) + throw new ApplicationException("Could not find resource decrypter IV"); + var publicKeyToken = module.Assembly.Name.PublicKeyToken; + if (publicKeyToken != null) { + for (int i = 0; i < 8; i++) + iv[i * 2 + 1] = publicKeyToken[i]; + } + } + + EmbeddedResource findMethodsDecrypterResource(MethodDefinition method) { + foreach (var s in DotNetUtils.getCodeStrings(method)) { + var resource = DotNetUtils.getResource(module, s) as EmbeddedResource; + if (resource != null) + return resource; + } + return null; + } + + static byte[] initArray(MethodDefinition method, int arraySize) { + int newarrIndex = findNewarr(method, arraySize); + if (newarrIndex < 0) + return null; + + var resultValueArray = new Value[arraySize]; + + var emulator = new InstructionEmulator(method.HasThis, false, method.Parameters, method.Body.Variables); + var theArray = new UnknownValue(); + emulator.push(theArray); + + var instructions = method.Body.Instructions; + for (int i = newarrIndex + 1; i < instructions.Count; i++) { + var instr = instructions[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + break; + if (instr.OpCode.Code == Code.Newarr) + break; + + if (instr.OpCode.Code == Code.Stelem_I1) { + var value = emulator.pop(); + var index = emulator.pop() as Int32Value; + var array = emulator.pop(); + if (ReferenceEquals(array, theArray) && index != null && index.allBitsValid()) { + if (0 <= index.value && index.value < resultValueArray.Length) + resultValueArray[index.value] = value; + } + } + else + emulator.emulate(instr); + } + + byte[] resultArray = new byte[resultValueArray.Length]; + for (int i = 0; i < resultArray.Length; i++) { + var intValue = resultValueArray[i] as Int32Value; + if (intValue == null || !intValue.allBitsValid()) + return null; + resultArray[i] = (byte)intValue.value; + } + + return resultArray; + } + + static int findNewarr(MethodDefinition method, int arraySize) { + var instructions = method.Body.Instructions; + for (int i = 0; i < instructions.Count; i++) { + var instr = instructions[i]; + if (instr.OpCode.Code != Code.Newarr || i < 1) + continue; + var ldci4 = instructions[i - 1]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + if (DotNetUtils.getLdcI4Value(ldci4) != arraySize) + continue; + + return i; + } + + return -1; + } + + public byte[] decrypt() { + if (encryptedDataResource == null || key == null || iv == null) + throw new ApplicationException("Can't decrypt resource"); + + using (var aes = new RijndaelManaged()) { + aes.Mode = CipherMode.CBC; + using (var transform = aes.CreateDecryptor(key, iv)) { + var encryptedData = encryptedDataResource.GetResourceData(); + return transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length); + } + } + } + } +} diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs b/de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs new file mode 100644 index 00000000..baa672d9 --- /dev/null +++ b/de4dot.code/deobfuscators/dotNET_Reactor/LocalTypes.cs @@ -0,0 +1,55 @@ +/* + Copyright (C) 2011 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.Cil; + +namespace de4dot.deobfuscators.dotNET_Reactor { + class LocalTypes { + Dictionary localTypes = new Dictionary(StringComparer.Ordinal); + + public LocalTypes(IList locals) { + foreach (var local in locals) { + var key = local.VariableType.FullName; + int count; + localTypes.TryGetValue(key, out count); + localTypes[key] = count + 1; + } + } + + public bool exists(string typeFullName) { + return localTypes.ContainsKey(typeFullName); + } + + public bool all(string[] types) { + foreach (var typeName in types) { + if (!exists(typeName)) + return false; + } + return true; + } + + public int count(string typeFullName) { + int count; + localTypes.TryGetValue(typeFullName, out count); + return count; + } + } +} diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs index e9f9e2db..ddad3d4e 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs @@ -19,20 +19,30 @@ using System; using System.Collections.Generic; +using System.IO; using Mono.Cecil; +using Mono.Cecil.Cil; using de4dot.blocks; namespace de4dot.deobfuscators.dotNET_Reactor { class MethodsDecrypter { ModuleDefinition module; - MethodDefinition methodsDecrypterMethod; + EncryptedResource encryptedResource; + long xorKey; + bool useXorKey; public bool Detected { - get { return methodsDecrypterMethod != null; } + get { return encryptedResource.ResourceDecrypterMethod != null; } } public MethodsDecrypter(ModuleDefinition module) { this.module = module; + this.encryptedResource = new EncryptedResource(module); + } + + public MethodsDecrypter(ModuleDefinition module, MethodsDecrypter oldOne) { + this.module = module; + this.encryptedResource = new EncryptedResource(module, oldOne.encryptedResource); } public void find() { @@ -40,66 +50,107 @@ namespace de4dot.deobfuscators.dotNET_Reactor { var callCounter = new CallCounter(); int typesLeft = 30; foreach (var type in module.GetTypes()) { - if (typesLeft-- <= 0) - break; var cctor = DotNetUtils.getMethod(type, ".cctor"); if (cctor == null || cctor.Body == null) continue; + if (typesLeft-- <= 0) + break; foreach (var info in DotNetUtils.getCalledMethods(module, cctor)) { var method = info.Item2; var key = new MethodReferenceAndDeclaringTypeKey(method); if (!checkedMethods.ContainsKey(key)) { checkedMethods[key] = true; - if (!couldBeMethodsDecrypter(method)) + if (!DotNetUtils.isMethod(method, "System.Void", "()")) + continue; + if (!encryptedResource.couldBeResourceDecrypter(method)) continue; } callCounter.add(method); } } - methodsDecrypterMethod = (MethodDefinition)callCounter.most(); + encryptedResource.ResourceDecrypterMethod = (MethodDefinition)callCounter.most(); } - bool couldBeMethodsDecrypter(MethodDefinition method) { - if (!method.IsStatic) - return false; - if (method.Body == null) - return false; - if (method.Body.Instructions.Count < 2000) - return false; + public byte[] decrypt(ISimpleDeobfuscator simpleDeobfuscator) { + if (encryptedResource.ResourceDecrypterMethod == null) + return null; - var localTypes = new Dictionary(StringComparer.Ordinal); - foreach (var local in method.Body.Variables) - localTypes[local.VariableType.FullName] = true; - var requiredTypes = new string[] { - "System.Byte[]", - "System.Diagnostics.StackFrame", - "System.IntPtr", - "System.IO.BinaryReader", - "System.IO.MemoryStream", - "System.Reflection.Assembly", - "System.Security.Cryptography.CryptoStream", - "System.Security.Cryptography.ICryptoTransform", - "System.Security.Cryptography.RijndaelManaged", - }; - foreach (var typeName in requiredTypes) { - if (!localTypes.ContainsKey(typeName)) - return false; + encryptedResource.init(simpleDeobfuscator); + initXorKey(); + var methodsData = encryptedResource.decrypt(); + + if (useXorKey) { + var stream = new MemoryStream(methodsData); + var reader = new BinaryReader(stream); + var writer = new BinaryWriter(stream); + int count = methodsData.Length / 8; + for (int i = 0; i < count; i++) { + long val = reader.ReadInt64(); + val ^= xorKey; + stream.Position -= 8; + writer.Write(val); + } } - if (!isResourceString(DotNetUtils.getCodeStrings(method))) - return false; + using (var fileStream = new FileStream(module.FullyQualifiedName, FileMode.Open, FileAccess.Read, FileShare.Read)) { + byte[] fileData = new byte[(int)fileStream.Length]; + fileStream.Read(fileData, 0, fileData.Length); + var peImage = new PE.PeImage(fileData); - return true; + var methodsDataReader = new BinaryReader(new MemoryStream(methodsData)); + int patchCount = methodsDataReader.ReadInt32(); + int mode = methodsDataReader.ReadInt32(); + if (!useXorKey || mode == 1) { + for (int i = 0; i < patchCount; i++) { + uint rva = methodsDataReader.ReadUInt32(); + uint data = methodsDataReader.ReadUInt32(); + peImage.write(rva, BitConverter.GetBytes(data)); + } + while (methodsDataReader.BaseStream.Position < methodsData.Length - 1) { + uint rva = methodsDataReader.ReadUInt32(); + uint token = methodsDataReader.ReadUInt32(); + int size = methodsDataReader.ReadInt32(); + if (size > 0) + peImage.write(rva, methodsDataReader.ReadBytes(size)); + } + } + else { + for (int i = 0; i < patchCount; i++) { + uint rva = methodsDataReader.ReadUInt32(); + uint data = methodsDataReader.ReadUInt32(); + peImage.write(rva, BitConverter.GetBytes(data)); + } + int count = methodsDataReader.ReadInt32(); + while (methodsDataReader.BaseStream.Position < methodsData.Length - 1) { + uint rva = methodsDataReader.ReadUInt32(); + uint token = methodsDataReader.ReadUInt32(); + int size = methodsDataReader.ReadInt32(); + if (size > 0) + peImage.write(rva, methodsDataReader.ReadBytes(size)); + } + } + + return fileData; + } } - bool isResourceString(IList strings) { - foreach (var s in strings) { - if (DotNetUtils.getResource(module, s) != null) - return true; + void initXorKey() { + useXorKey = false; + + var instructions = encryptedResource.ResourceDecrypterMethod.Body.Instructions; + for (int i = 0; i < instructions.Count - 1; i++) { + if (instructions[i].OpCode.Code != Code.Ldind_I8) + continue; + var ldci4 = instructions[i + 1]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + + xorKey = DotNetUtils.getLdcI4Value(ldci4); + useXorKey = true; + return; } - return false; } } }