diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index a9063c1b..cdf69bb0 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -27,6 +27,7 @@
+
diff --git a/de4dot.code/deobfuscators/DeobUtils.cs b/de4dot.code/deobfuscators/DeobUtils.cs
index 69ae031c..e756aa63 100644
--- a/de4dot.code/deobfuscators/DeobUtils.cs
+++ b/de4dot.code/deobfuscators/DeobUtils.cs
@@ -25,13 +25,36 @@ You should have received a copy of the GNU General Public License
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using ICSharpCode.SharpZipLib.Zip.Compression;
+#if NETFRAMEWORK
+using BrotliSharpLib;
+#endif
using de4dot.blocks;
namespace de4dot.code.deobfuscators {
public static class DeobUtils {
public static void DecryptAndAddResources(ModuleDef module, string encryptedName, Func decryptResource) {
Logger.v("Decrypting resources, name: {0}", Utils.ToCsharpString(encryptedName));
- var decryptedResourceData = decryptResource();
+ AddDecryptedResources(module, decryptResource());
+ }
+
+ public static void DecryptAndAddResources(ModuleDef module, string encryptedName, Func> decryptResources) {
+ Logger.v("Decrypting resources, name: {0}", Utils.ToCsharpString(encryptedName));
+ BadImageFormatException lastException = null;
+ foreach (var decryptedResourceData in decryptResources()) {
+ try {
+ AddDecryptedResources(module, decryptedResourceData);
+ return;
+ }
+ catch (BadImageFormatException ex) {
+ lastException = ex;
+ }
+ }
+ if (lastException != null)
+ throw lastException;
+ throw new BadImageFormatException();
+ }
+
+ static void AddDecryptedResources(ModuleDef module, byte[] decryptedResourceData) {
if (decryptedResourceData == null)
throw new ApplicationException("decryptedResourceData is null");
var resourceModule = ModuleDefMD.Load(decryptedResourceData);
@@ -163,6 +186,21 @@ public static byte[] Inflate(byte[] data, int start, int len, Inflater inflater)
return memStream.ToArray();
}
+ public static byte[] BrotliDecompress(byte[] data) {
+#if NETFRAMEWORK
+ return Brotli.DecompressBuffer(data, 0, data.Length);
+#else
+ using (var input = new MemoryStream(data, writable: false)) {
+ using (var brotli = new BrotliStream(input, CompressionMode.Decompress)) {
+ using (var output = new MemoryStream()) {
+ brotli.CopyTo(output);
+ return output.ToArray();
+ }
+ }
+ }
+#endif
+ }
+
public static byte[] Gunzip(Stream input, int decompressedSize) {
using (var gzip = new GZipStream(input, CompressionMode.Decompress)) {
var decompressed = new byte[decompressedSize];
diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs b/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs
index 9b06bbb2..c82ad177 100644
--- a/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs
+++ b/de4dot.code/deobfuscators/dotNET_Reactor/v4/ResourceResolver.cs
@@ -151,34 +151,63 @@ MethodDef FindInitMethod(ISimpleDeobfuscator simpleDeobfuscator) {
public EmbeddedResource MergeResources() {
if (encryptedResource.Resource == null)
return null;
- DeobUtils.DecryptAndAddResources(module, encryptedResource.Resource.Name.String, () => {
- byte[] decrypted;
- try {
- decrypted = encryptedResource.Decrypt();
- }
- catch (Exception ex) {
- Logger.e("Resource decryption failed:\n{0}", ex);
- return null;
- }
+ byte[] decrypted;
+ try {
+ decrypted = encryptedResource.Decrypt();
+ }
+ catch (Exception ex) {
+ Logger.e("Resource decryption failed:\n{0}", ex);
+ return null;
+ }
+ DeobUtils.DecryptAndAddResources(module, encryptedResource.Resource.Name.String,
+ () => GetDecompressedResources(decrypted));
+ return encryptedResource.Resource;
+ }
- if (decrypted.Length < 64)
- throw new Exception("Decrypted resource data has length " + decrypted.Length);
- if (decrypted[0] == 0x4D && decrypted[1] == 0x5A && decrypted[62] == 0 && decrypted[63] == 0)
- return decrypted;
+ static IEnumerable GetDecompressedResources(byte[] decrypted) {
+ if (decrypted.Length >= 2 && decrypted[0] == 0x4D && decrypted[1] == 0x5A)
+ yield return decrypted;
- try {
- return QuickLZ.Decompress(decrypted);
- }
- catch {
- try {
- return DeobUtils.Inflate(decrypted, true);
- }
- catch {
- return null;
- }
- }
- });
- return encryptedResource.Resource;
+ if (TryQuickLZDecompress(decrypted, out var decompressed))
+ yield return decompressed;
+ if (TryInflate(decrypted, out var inflated))
+ yield return inflated;
+
+ if (TryBrotliDecompress(decrypted, out var brotli))
+ yield return brotli;
+ }
+
+ static bool TryQuickLZDecompress(byte[] data, out byte[] decompressed) {
+ try {
+ decompressed = QuickLZ.Decompress(data);
+ return true;
+ }
+ catch {
+ decompressed = null;
+ return false;
+ }
+ }
+
+ static bool TryInflate(byte[] data, out byte[] inflated) {
+ try {
+ inflated = DeobUtils.Inflate(data, true);
+ return true;
+ }
+ catch {
+ inflated = null;
+ return false;
+ }
+ }
+
+ static bool TryBrotliDecompress(byte[] data, out byte[] brotli) {
+ try {
+ brotli = DeobUtils.BrotliDecompress(data);
+ return true;
+ }
+ catch {
+ brotli = null;
+ return false;
+ }
}
}
}