From e94eb917395e50c74ea363a79df3514538989daa Mon Sep 17 00:00:00 2001
From: Siegfried Pammer <siegfriedpammer@gmail.com>
Date: Thu, 16 Jan 2025 22:13:21 +0100
Subject: [PATCH] Fix #3372: Fix loading a DLL that contains byte sequences
 matching ZIP central directory.

---
 .../FileLoaders/FileLoaderRegistry.cs         |  3 +-
 .../FileLoaders/PEFileLoader.cs               | 50 +++++++++++++++++++
 ICSharpCode.ILSpyX/LoadedAssembly.cs          |  7 +--
 3 files changed, 53 insertions(+), 7 deletions(-)
 create mode 100644 ICSharpCode.ILSpyX/FileLoaders/PEFileLoader.cs

diff --git a/ICSharpCode.ILSpyX/FileLoaders/FileLoaderRegistry.cs b/ICSharpCode.ILSpyX/FileLoaders/FileLoaderRegistry.cs
index 8117b4b94..a6a264372 100644
--- a/ICSharpCode.ILSpyX/FileLoaders/FileLoaderRegistry.cs
+++ b/ICSharpCode.ILSpyX/FileLoaders/FileLoaderRegistry.cs
@@ -42,7 +42,8 @@ namespace ICSharpCode.ILSpyX.FileLoaders
 			Register(new XamarinCompressedFileLoader());
 			Register(new WebCilFileLoader());
 			Register(new MetadataFileLoader());
-			Register(new BundleFileLoader());
+			Register(new BundleFileLoader()); // bundles are PE files with a special signature, prefer over normal PE files
+			Register(new PEFileLoader()); // prefer PE format over archives, because ZIP has no fixed header
 			Register(new ArchiveFileLoader());
 		}
 	}
diff --git a/ICSharpCode.ILSpyX/FileLoaders/PEFileLoader.cs b/ICSharpCode.ILSpyX/FileLoaders/PEFileLoader.cs
new file mode 100644
index 000000000..d9014baee
--- /dev/null
+++ b/ICSharpCode.ILSpyX/FileLoaders/PEFileLoader.cs
@@ -0,0 +1,50 @@
+// Copyright (c) 2024 Siegfried Pammer
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System.IO;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Threading.Tasks;
+
+using ICSharpCode.Decompiler.Metadata;
+
+namespace ICSharpCode.ILSpyX.FileLoaders
+{
+	public sealed class PEFileLoader : IFileLoader
+	{
+		public async Task<LoadResult?> Load(string fileName, Stream stream, FileLoadContext context)
+		{
+			if (stream.Length < 2 || stream.ReadByte() != 'M' || stream.ReadByte() != 'Z')
+			{
+				return null;
+			}
+
+			return await LoadPEFile(fileName, stream, context).ConfigureAwait(false);
+		}
+
+		public static Task<LoadResult> LoadPEFile(string fileName, Stream stream, FileLoadContext context)
+		{
+			MetadataReaderOptions options = context.ApplyWinRTProjections
+				? MetadataReaderOptions.ApplyWindowsRuntimeProjections
+				: MetadataReaderOptions.None;
+			stream.Position = 0;
+			PEFile module = new PEFile(fileName, stream, PEStreamOptions.PrefetchEntireImage | PEStreamOptions.LeaveOpen, metadataOptions: options);
+			return Task.FromResult(new LoadResult { MetadataFile = module });
+		}
+	}
+}
diff --git a/ICSharpCode.ILSpyX/LoadedAssembly.cs b/ICSharpCode.ILSpyX/LoadedAssembly.cs
index 26c063e5d..64fca7a55 100644
--- a/ICSharpCode.ILSpyX/LoadedAssembly.cs
+++ b/ICSharpCode.ILSpyX/LoadedAssembly.cs
@@ -358,12 +358,7 @@ namespace ICSharpCode.ILSpyX
 				stream.Position = 0;
 				try
 				{
-					MetadataReaderOptions options = applyWinRTProjections
-						? MetadataReaderOptions.ApplyWindowsRuntimeProjections
-						: MetadataReaderOptions.None;
-
-					PEFile module = new PEFile(fileName, stream, PEStreamOptions.PrefetchEntireImage, metadataOptions: options);
-					result = new LoadResult { MetadataFile = module };
+					result = await PEFileLoader.LoadPEFile(fileName, stream, settings).ConfigureAwait(false);
 				}
 				catch (Exception ex)
 				{