diff --git a/SabreTools.Serialization/Deserializers/PortableExecutable.cs b/SabreTools.Serialization/Deserializers/PortableExecutable.cs
index eeeb981a..817285dd 100644
--- a/SabreTools.Serialization/Deserializers/PortableExecutable.cs
+++ b/SabreTools.Serialization/Deserializers/PortableExecutable.cs
@@ -103,11 +103,6 @@ namespace SabreTools.Serialization.Deserializers
pex.SectionTable[i] = ParseSectionHeader(data);
}
- // TODO: Figure out a way to determine the end of the data here
- // to find hidden things between the end of the section table and
- // the beginning of the next data block. Some packers and protections
- // can hide things here, e.g. ActiveMark
-
#endregion
#region COFF Symbol Table and COFF String Table
diff --git a/SabreTools.Serialization/Wrappers/PortableExecutable.cs b/SabreTools.Serialization/Wrappers/PortableExecutable.cs
index 06dc0bf9..32ba1918 100644
--- a/SabreTools.Serialization/Wrappers/PortableExecutable.cs
+++ b/SabreTools.Serialization/Wrappers/PortableExecutable.cs
@@ -390,6 +390,45 @@ namespace SabreTools.Serialization.Wrappers
///
public Models.PortableExecutable.SectionHeader[]? SectionTable => Model.SectionTable;
+ ///
+ /// Data after the section table, if it exists
+ ///
+ public byte[] SectionTrailerData
+ {
+ get
+ {
+ lock (_sectionTrailerDataLock)
+ {
+ // If we already have cached data, just use that immediately
+ if (_sectionTrailerData != null)
+ return _sectionTrailerData;
+
+ if (Stub?.Header?.NewExeHeaderAddr == null)
+ {
+ _sectionTrailerData = [];
+ return _sectionTrailerData;
+ }
+ if (COFFFileHeader == null)
+ {
+ _sectionTrailerData = [];
+ return _sectionTrailerData;
+ }
+
+ // Get the offset from the end of the section table
+ long endOfSectionTable = Stub.Header.NewExeHeaderAddr
+ + 24 // Signature size + COFF file header size
+ + COFFFileHeader.SizeOfOptionalHeader
+ + (COFFFileHeader.NumberOfSections * 40); // Size of a section header
+
+ // TODO: Figure out how to determine the end of the extra data
+ _sectionTrailerData = [];
+
+ // Cache and return the stub executable data, even if null
+ return _sectionTrailerData;
+ }
+ }
+ }
+
///
public Models.MSDOS.Executable? Stub => Model.Stub;
@@ -745,6 +784,16 @@ namespace SabreTools.Serialization.Wrappers
///
private readonly object _sectionStringDataLock = new();
+ ///
+ /// Data after the section table, if it exists
+ ///
+ private byte[]? _sectionTrailerData = null;
+
+ ///
+ /// Lock object for
+ ///
+ private readonly object _sectionTrailerDataLock = new();
+
///
/// Stub executable data, if it exists
///