diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index ab199156..66a2374a 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -77,6 +77,8 @@ + + @@ -91,6 +93,9 @@ + + + @@ -119,6 +124,7 @@ + diff --git a/de4dot.code/deobfuscators/UnpackedFile.cs b/de4dot.code/deobfuscators/UnpackedFile.cs new file mode 100644 index 00000000..9e12edd8 --- /dev/null +++ b/de4dot.code/deobfuscators/UnpackedFile.cs @@ -0,0 +1,34 @@ +/* + 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 . +*/ + +namespace de4dot.code.deobfuscators { + class UnpackedFile { + public string filename; + public byte[] data; + + public UnpackedFile(string filename, byte[] data) { + this.filename = filename; + this.data = data; + } + + public override string ToString() { + return string.Format("{0:X8} - {1}", data.Length, filename); + } + } +} diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v3/ApplicationModeDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v3/ApplicationModeDecrypter.cs new file mode 100644 index 00000000..3cd7a242 --- /dev/null +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v3/ApplicationModeDecrypter.cs @@ -0,0 +1,74 @@ +/* + 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 Mono.Cecil; +using de4dot.blocks; +using de4dot.blocks.cflow; + +namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 { + class ApplicationModeDecrypter { + ModuleDefinition module; + AssemblyResolver assemblyResolver; + MemoryPatcher memoryPatcher; + + public byte[] AssemblyKey { + get { return assemblyResolver.Key; } + } + + public byte[] AssemblyIv { + get { return assemblyResolver.Iv; } + } + + public MemoryPatcher MemoryPatcher { + get { return memoryPatcher; } + } + + public bool Detected { + get { return assemblyResolver != null; } + } + + public ApplicationModeDecrypter(ModuleDefinition module) { + this.module = module; + find(); + } + + void find() { + var cflowDeobfuscator = new CflowDeobfuscator() { + InlineMethods = true, + InlineInstanceMethods = true, + }; + + foreach (var type in module.Types) { + if (DotNetUtils.getPInvokeMethod(type, "kernel32", "CloseHandle") == null) + continue; + + var resolver = new AssemblyResolver(type, cflowDeobfuscator); + if (!resolver.Detected) + continue; + var patcher = new MemoryPatcher(type, cflowDeobfuscator); + if (!patcher.Detected) + continue; + + assemblyResolver = resolver; + memoryPatcher = patcher; + return; + } + } + } +} diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v3/ApplicationModeUnpacker.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v3/ApplicationModeUnpacker.cs new file mode 100644 index 00000000..16cb7368 --- /dev/null +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v3/ApplicationModeUnpacker.cs @@ -0,0 +1,190 @@ +/* + 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 System.IO; +using System.Text; +using System.Text.RegularExpressions; +using Mono.Cecil; +using de4dot.code.PE; + +namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 { + class IniFile { + Dictionary nameToValue = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public string this[string name] { + get { + string value; + nameToValue.TryGetValue(name, out value); + return value; + } + } + + public IniFile(byte[] data) { + using (var reader = new StreamReader(new MemoryStream(data), Encoding.UTF8)) { + while (true) { + var line = reader.ReadLine(); + if (line == null) + break; + var match = Regex.Match(line, @"^([^=]+)=([^;]+);?\s*$"); + if (match.Groups.Count < 3) + continue; + var name = match.Groups[1].ToString().Trim(); + var value = match.Groups[2].ToString().Trim(); + nameToValue[name] = value; + } + } + } + } + + // Unpacks "application mode" files (DNR 3.x) + class ApplicationModeUnpacker { + static byte[] key1 = new byte[32] { + 0x6B, 0x6C, 0xA7, 0x24, 0x25, 0x37, 0x67, 0x68, + 0x4A, 0x2F, 0x28, 0x29, 0x33, 0x77, 0x34, 0x35, + 0x5A, 0x5A, 0x48, 0x57, 0x24, 0x35, 0x24, 0x25, + 0x26, 0x67, 0x77, 0x53, 0x41, 0x44, 0x46, 0x32, + }; + static byte[] iv1 = new byte[16] { + 0x73, 0x64, 0xA7, 0x35, 0x24, 0xA7, 0x26, 0x67, + 0x34, 0x35, 0x37, 0x21, 0x32, 0x33, 0x6E, 0x6D, + }; + static byte[] key2 = new byte[32] { + 0x28, 0x24, 0x29, 0x28, 0x2F, 0x29, 0x28, 0x29, + 0x3D, 0x66, 0x67, 0x35, 0x35, 0x6A, 0x6D, 0x2C, + 0xA7, 0x39, 0x38, 0x2A, 0x6A, 0x67, 0x74, 0x36, + 0x35, 0x3D, 0xA7, 0x43, 0x33, 0x33, 0x24, 0x74, + }; + static byte[] iv2 = new byte[16] { + 0x67, 0x26, 0x35, 0xA7, 0x24, 0xA7, 0x37, 0x21, + 0x73, 0x33, 0x6E, 0x6D, 0x34, 0x32, 0x64, 0x35, + }; + + PeImage peImage; + List satelliteAssemblies = new List(); + uint[] sizes; + string[] filenames; + + public IEnumerable EmbeddedAssemblies { + get { return satelliteAssemblies; } + } + + public ApplicationModeUnpacker(PeImage peImage) { + this.peImage = peImage; + } + + public byte[] unpack() { + try { + return unpack2(); + } + catch { + return null; + } + } + + byte[] unpack2() { + uint headerOffset = peImage.ImageLength - 12; + uint offsetEncryptedAssembly = checkOffset(peImage.offsetReadUInt32(headerOffset)); + uint ezencryptionLibLength = peImage.offsetReadUInt32(headerOffset + 4); + uint iniFileLength = peImage.offsetReadUInt32(headerOffset + 8); + + uint offsetClrVersionNumber = checked(offsetEncryptedAssembly - 12); + uint iniFileOffset = checked(headerOffset - iniFileLength); + uint ezencryptionLibOffset = checked(iniFileOffset - ezencryptionLibLength); + + uint clrVerMajor = peImage.offsetReadUInt32(offsetClrVersionNumber); + uint clrVerMinor = peImage.offsetReadUInt32(offsetClrVersionNumber + 4); + uint clrVerBuild = peImage.offsetReadUInt32(offsetClrVersionNumber + 8); + if (clrVerMajor <= 0 || clrVerMajor >= 20 || clrVerMinor >= 20 || clrVerBuild >= 1000000) + return null; + + var settings = new IniFile(decompress2(peImage.offsetReadBytes(iniFileOffset, (int)iniFileLength))); + sizes = getSizes(settings["General_App_Satellite_Assemblies_Sizes"]); + if (sizes == null || sizes.Length <= 1) + return null; + if (sizes[0] != offsetEncryptedAssembly) + return null; + filenames = settings["General_App_Satellite_Assemblies"].Split('|'); + if (sizes.Length - 1 != filenames.Length) + return null; + + byte[] ezencryptionLibData = decompress1(peImage.offsetReadBytes(ezencryptionLibOffset, (int)ezencryptionLibLength)); + var ezencryptionLibModule = ModuleDefinition.ReadModule(new MemoryStream(ezencryptionLibData)); + var decrypter = new ApplicationModeDecrypter(ezencryptionLibModule); + if (!decrypter.Detected) + return null; + + var mainAssembly = unpackEmbeddedFile(0, decrypter); + decrypter.MemoryPatcher.patch(mainAssembly.data); + for (int i = 1; i < filenames.Length; i++) + satelliteAssemblies.Add(unpackEmbeddedFile(i, decrypter)); + + return mainAssembly.data; + } + + UnpackedFile unpackEmbeddedFile(int index, ApplicationModeDecrypter decrypter) { + uint offset = 0; + for (int i = 0; i < index + 1; i++) + offset += sizes[i]; + string filename = Path.GetFileName(filenames[index]); + var data = peImage.offsetReadBytes(offset, (int)sizes[index + 1]); + data = DeobUtils.decrypt(data, decrypter.AssemblyKey, decrypter.AssemblyIv); + data = decompress(data); + return new UnpackedFile(filename, data); + } + + static uint[] getSizes(string sizes) { + if (sizes == null) + return null; + var list = new List(); + foreach (var num in sizes.Split('|')) + list.Add(uint.Parse(num)); + return list.ToArray(); + } + + uint checkOffset(uint offset) { + if (offset >= peImage.ImageLength) + throw new Exception(); + return offset; + } + + static byte[] decompress1(byte[] data) { + return decompress(decrypt1(data)); + } + + static byte[] decompress2(byte[] data) { + return decompress(decrypt2(data)); + } + + static byte[] decompress(byte[] data) { + if (!QuickLZ.isCompressed(data)) + return data; + return QuickLZ.decompress(data); + } + + static byte[] decrypt1(byte[] data) { + return DeobUtils.decrypt(data, key1, iv1); + } + + static byte[] decrypt2(byte[] data) { + return DeobUtils.decrypt(data, key2, iv2); + } + } +} diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v3/AssemblyResolver.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v3/AssemblyResolver.cs new file mode 100644 index 00000000..3501943c --- /dev/null +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v3/AssemblyResolver.cs @@ -0,0 +1,66 @@ +/* + 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.Collections.Generic; +using Mono.Cecil; +using de4dot.blocks; +using de4dot.blocks.cflow; + +namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 { + class AssemblyResolver { + DecryptMethod decryptMethod = new DecryptMethod(); + + public byte[] Key { + get { return decryptMethod.Key; } + } + + public byte[] Iv { + get { return decryptMethod.Iv; } + } + + public bool Detected { + get { return decryptMethod.Detected; } + } + + public AssemblyResolver(TypeDefinition type, ICflowDeobfuscator cflowDeobfuscator) { + find(type, cflowDeobfuscator); + } + + void find(TypeDefinition type, ICflowDeobfuscator cflowDeobfuscator) { + var additionalTypes = new List { + "System.IO.BinaryReader", + "System.IO.FileStream", + "System.Reflection.Assembly", + "System.Reflection.Assembly[]", + "System.String", + }; + foreach (var method in type.Methods) { + if (!DotNetUtils.isMethod(method, "System.Reflection.Assembly", "(System.Object,System.ResolveEventArgs)")) + continue; + if (!DecryptMethod.couldBeDecryptMethod(method, additionalTypes)) + continue; + cflowDeobfuscator.deobfuscateCflow(method); + if (!decryptMethod.getKey(method)) + continue; + + return; + } + } + } +} diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v3/DecryptMethod.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v3/DecryptMethod.cs new file mode 100644 index 00000000..f6c43b9b --- /dev/null +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v3/DecryptMethod.cs @@ -0,0 +1,77 @@ +/* + 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; + +namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 { + class DecryptMethod { + MethodDefinition decryptionMethod; + byte[] key; + byte[] iv; + + public byte[] Key { + get { return key; } + } + + public byte[] Iv { + get { return iv; } + } + + public bool Detected { + get { return decryptionMethod != null; } + } + + public static bool couldBeDecryptMethod(MethodDefinition method, IEnumerable additionalTypes) { + if (method.Body == null) + return false; + + var localTypes = new LocalTypes(method); + var requiredTypes = new List { + "System.Byte[]", + "System.IO.MemoryStream", + "System.Security.Cryptography.CryptoStream", + "System.Security.Cryptography.ICryptoTransform", + }; + requiredTypes.AddRange(additionalTypes); + if (!localTypes.all(requiredTypes)) + return false; + if (!localTypes.exists("System.Security.Cryptography.RijndaelManaged") && + !localTypes.exists("System.Security.Cryptography.AesManaged")) + return false; + + return true; + } + + public bool getKey(MethodDefinition method) { + var tmpKey = ArrayFinder.getInitializedByteArray(method, 32); + if (tmpKey == null) + return false; + var tmpIv = ArrayFinder.getInitializedByteArray(method, 16); + if (tmpIv == null) + return false; + + decryptionMethod = method; + key = tmpKey; + iv = tmpIv; + return true; + } + } +} diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v3/MemoryPatcher.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v3/MemoryPatcher.cs new file mode 100644 index 00000000..4a572990 --- /dev/null +++ b/de4dot.code/deobfuscators/dotNET_Reactor/v3/MemoryPatcher.cs @@ -0,0 +1,146 @@ +/* + 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 System.IO; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; +using de4dot.blocks.cflow; +using de4dot.code.PE; + +namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 { + class MemoryPatcher { + DecryptMethod decryptMethod = new DecryptMethod(); + List patchInfos = new List(); + + class PatchInfo { + public int[] offsets; + public int[] values; + public PatchInfo(int[] offsets, int[] values) { + this.offsets = offsets; + this.values = values; + } + } + + public bool Detected { + get { return decryptMethod.Detected; } + } + + public MemoryPatcher(TypeDefinition type, ICflowDeobfuscator cflowDeobfuscator) { + find(type, cflowDeobfuscator); + } + + void find(TypeDefinition type, ICflowDeobfuscator cflowDeobfuscator) { + var additionalTypes = new List { + "System.IO.BinaryWriter", + }; + foreach (var method in type.Methods) { + if (!DotNetUtils.isMethod(method, "System.Void", "(System.Int32[],System.UInt32[])")) + continue; + if (!DecryptMethod.couldBeDecryptMethod(method, additionalTypes)) + continue; + cflowDeobfuscator.deobfuscateCflow(method); + if (!decryptMethod.getKey(method)) + continue; + + findPatchData(type, cflowDeobfuscator); + return; + } + } + + void findPatchData(TypeDefinition type, ICflowDeobfuscator cflowDeobfuscator) { + var locals = new List { + "System.Int32[]", + "System.UInt32[]", + }; + foreach (var method in type.Methods) { + if (method.Attributes != MethodAttributes.Private) + continue; + if (!DotNetUtils.isMethod(method, "System.Void", "()")) + continue; + if (!new LocalTypes(method).exactly(locals)) + continue; + cflowDeobfuscator.deobfuscateCflow(method); + var patchInfo = getPatchInfo(method); + if (patchInfo == null) + continue; + + patchInfos.Add(patchInfo); + } + } + + PatchInfo getPatchInfo(MethodDefinition method) { + int index1 = 0, index2, index3, size1, size2, size3; + if (!ArrayFinder.findNewarr(method, ref index1, out size1)) + return null; + index2 = index1 + 1; + if (!ArrayFinder.findNewarr(method, ref index2, out size2)) + return null; + index3 = index2 + 1; + if (ArrayFinder.findNewarr(method, ref index3, out size3)) + return null; + + if (size1 <= 0 || size1 > 35) + return null; + + var ary1 = ArrayFinder.getInitializedInt32Array(size1, method, ref index1); + var ary2 = ArrayFinder.getInitializedInt32Array(size2, method, ref index2); + if (ary1 == null || ary2 == null) + return null; + ary2 = decrypt(ary2); + if (ary2 == null || ary2.Length != ary2.Length) + return null; + + for (int i = 0; i < ary1.Length; i++) + ary1[i] = -ary1[i]; + + return new PatchInfo(ary1, ary2); + } + + int[] decrypt(int[] data) { + var memStream = new MemoryStream(); + var writer = new BinaryWriter(memStream); + foreach (var value in data) + writer.Write(value); + byte[] decrypted; + try { + decrypted = DeobUtils.decrypt(memStream.ToArray(), decryptMethod.Key, decryptMethod.Iv); + } + catch { + return null; + } + if (decrypted.Length / 4 * 4 != decrypted.Length) + return null; + var newData = new int[decrypted.Length / 4]; + for (int i = 0; i < newData.Length; i++) + newData[i] = BitConverter.ToInt32(decrypted, i * 4); + return newData; + } + + public void patch(byte[] peImageData) { + var peImage = new PeImage(peImageData); + foreach (var info in patchInfos) { + for (int i = 0; i < info.offsets.Length; i++) + peImage.dotNetSafeWriteOffset((uint)info.offsets[i], BitConverter.GetBytes(info.values[i])); + } + } + } +}