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]));
+ }
+ }
+ }
+}