diff --git a/BurnOutSharp.Wrappers/Enums.cs b/BurnOutSharp.Wrappers/Enums.cs new file mode 100644 index 00000000..c0a9b0f3 --- /dev/null +++ b/BurnOutSharp.Wrappers/Enums.cs @@ -0,0 +1,23 @@ +namespace BurnOutSharp.Wrappers +{ + /// + /// Location that the data originated from + /// + internal enum DataSource + { + /// + /// Unknown origin / testing + /// + UNKNOWN = 0, + + /// + /// Byte array with offset + /// + ByteArray = 1, + + /// + /// Stream + /// + Stream = 2, + } +} \ No newline at end of file diff --git a/BurnOutSharp.Wrappers/LinearExecutable.cs b/BurnOutSharp.Wrappers/LinearExecutable.cs index 540e8d30..f7321c30 100644 --- a/BurnOutSharp.Wrappers/LinearExecutable.cs +++ b/BurnOutSharp.Wrappers/LinearExecutable.cs @@ -1,5 +1,6 @@ using System.IO; +// TODO: Create base class for all wrappers namespace BurnOutSharp.Wrappers { public class LinearExecutable @@ -284,6 +285,29 @@ namespace BurnOutSharp.Wrappers /// private Models.LinearExecutable.Executable _executable; + /// + /// Source of the original data + /// + private DataSource _dataSource = DataSource.UNKNOWN; + + /// + /// Source byte array data + /// + /// This is only populated if is + private byte[] _byteArrayData = null; + + /// + /// Source byte array data offset + /// + /// This is only populated if is + private int _byteArrayOffset = -1; + + /// + /// Source Stream data + /// + /// This is only populated if is + private Stream _streamData = null; + #endregion /// @@ -303,7 +327,13 @@ namespace BurnOutSharp.Wrappers if (executable == null) return null; - var wrapper = new LinearExecutable { _executable = executable }; + var wrapper = new LinearExecutable + { + _executable = executable, + _dataSource = DataSource.ByteArray, + _byteArrayData = data, + _byteArrayOffset = offset, + }; return wrapper; } @@ -318,7 +348,12 @@ namespace BurnOutSharp.Wrappers if (executable == null) return null; - var wrapper = new LinearExecutable { _executable = executable }; + var wrapper = new LinearExecutable + { + _executable = executable, + _dataSource = DataSource.Stream, + _streamData = data, + }; return wrapper; } diff --git a/BurnOutSharp.Wrappers/MSDOS.cs b/BurnOutSharp.Wrappers/MSDOS.cs index 39d0f45a..11625069 100644 --- a/BurnOutSharp.Wrappers/MSDOS.cs +++ b/BurnOutSharp.Wrappers/MSDOS.cs @@ -1,6 +1,7 @@ using System; using System.IO; +// TODO: Create base class for all wrappers namespace BurnOutSharp.Wrappers { public class MSDOS @@ -88,6 +89,29 @@ namespace BurnOutSharp.Wrappers /// private Models.MSDOS.Executable _executable; + /// + /// Source of the original data + /// + private DataSource _dataSource = DataSource.UNKNOWN; + + /// + /// Source byte array data + /// + /// This is only populated if is + private byte[] _byteArrayData = null; + + /// + /// Source byte array data offset + /// + /// This is only populated if is + private int _byteArrayOffset = -1; + + /// + /// Source Stream data + /// + /// This is only populated if is + private Stream _streamData = null; + #endregion /// @@ -107,7 +131,13 @@ namespace BurnOutSharp.Wrappers if (executable == null) return null; - var wrapper = new MSDOS { _executable = executable }; + var wrapper = new MSDOS + { + _executable = executable, + _dataSource = DataSource.ByteArray, + _byteArrayData = data, + _byteArrayOffset = offset, + }; return wrapper; } @@ -122,7 +152,12 @@ namespace BurnOutSharp.Wrappers if (executable == null) return null; - var wrapper = new MSDOS { _executable = executable }; + var wrapper = new MSDOS + { + _executable = executable, + _dataSource = DataSource.Stream, + _streamData = data, + }; return wrapper; } diff --git a/BurnOutSharp.Wrappers/NewExecutable.cs b/BurnOutSharp.Wrappers/NewExecutable.cs index 896723b4..2b7805f0 100644 --- a/BurnOutSharp.Wrappers/NewExecutable.cs +++ b/BurnOutSharp.Wrappers/NewExecutable.cs @@ -4,6 +4,7 @@ using System.IO; using System.Text; using static BurnOutSharp.Builder.Extensions; +// TODO: Create base class for all wrappers namespace BurnOutSharp.Wrappers { public class NewExecutable @@ -216,6 +217,29 @@ namespace BurnOutSharp.Wrappers /// private Models.NewExecutable.Executable _executable; + /// + /// Source of the original data + /// + private DataSource _dataSource = DataSource.UNKNOWN; + + /// + /// Source byte array data + /// + /// This is only populated if is + private byte[] _byteArrayData = null; + + /// + /// Source byte array data offset + /// + /// This is only populated if is + private int _byteArrayOffset = -1; + + /// + /// Source Stream data + /// + /// This is only populated if is + private Stream _streamData = null; + #endregion /// @@ -235,7 +259,13 @@ namespace BurnOutSharp.Wrappers if (executable == null) return null; - var wrapper = new NewExecutable { _executable = executable }; + var wrapper = new NewExecutable + { + _executable = executable, + _dataSource = DataSource.ByteArray, + _byteArrayData = data, + _byteArrayOffset = offset, + }; return wrapper; } @@ -250,7 +280,12 @@ namespace BurnOutSharp.Wrappers if (executable == null) return null; - var wrapper = new NewExecutable { _executable = executable }; + var wrapper = new NewExecutable + { + _executable = executable, + _dataSource = DataSource.Stream, + _streamData = data, + }; return wrapper; } diff --git a/BurnOutSharp.Wrappers/PortableExecutable.cs b/BurnOutSharp.Wrappers/PortableExecutable.cs index e9b52822..299b2ee0 100644 --- a/BurnOutSharp.Wrappers/PortableExecutable.cs +++ b/BurnOutSharp.Wrappers/PortableExecutable.cs @@ -5,6 +5,7 @@ using System.Text; using System.Xml; using static BurnOutSharp.Builder.Extensions; +// TODO: Create base class for all wrappers namespace BurnOutSharp.Wrappers { public class PortableExecutable @@ -326,6 +327,44 @@ namespace BurnOutSharp.Wrappers /// private Models.PortableExecutable.Executable _executable; + /// + /// Source of the original data + /// + private DataSource _dataSource = DataSource.UNKNOWN; + + /// + /// Source byte array data + /// + /// This is only populated if is + private byte[] _byteArrayData = null; + + /// + /// Source byte array data offset + /// + /// This is only populated if is + private int _byteArrayOffset = -1; + + /// + /// Source Stream data + /// + /// This is only populated if is + private Stream _streamData = null; + + /// + /// Array of santiized section names + /// + private string[] _sectionNames = null; + + /// + /// Lock object for concurrent modifications on + /// + private readonly object _rawSectionsLock = new object(); + + /// + /// Cached raw section data + /// + private Dictionary _rawSectionData = null; + #endregion /// @@ -345,7 +384,13 @@ namespace BurnOutSharp.Wrappers if (executable == null) return null; - var wrapper = new PortableExecutable { _executable = executable }; + var wrapper = new PortableExecutable + { + _executable = executable, + _dataSource = DataSource.ByteArray, + _byteArrayData = data, + _byteArrayOffset = offset, + }; return wrapper; } @@ -360,10 +405,125 @@ namespace BurnOutSharp.Wrappers if (executable == null) return null; - var wrapper = new PortableExecutable { _executable = executable }; + var wrapper = new PortableExecutable + { + _executable = executable, + _dataSource = DataSource.Stream, + _streamData = data, + }; return wrapper; } + + /// + /// Get all section names from the executable + /// + /// A PE executable wrapper on success, null on failure + public string[] GetSectionNames() + { + // Use the cached data if possible + if (_sectionNames != null) + return _sectionNames; + + // Otherwise, build and return the cached array + _sectionNames = new string[_executable.SectionTable.Length]; + for (int i = 0; i < _sectionNames.Length; i++) + { + var section = _executable.SectionTable[i]; + + // TODO: Handle long section names with leading `/` + byte[] sectionNameBytes = section.Name; + string sectionNameString = Encoding.UTF8.GetString(sectionNameBytes).TrimEnd('\0'); + _sectionNames[i] = sectionNameString; + } + + return _sectionNames; + } + /// + /// Get raw section data from the source file + /// + /// Stream representing the executable + /// Byte array representing the data, null on error + public byte[] GetRawSection(string sectionName) + { + // If we have an invalid section name + if (string.IsNullOrEmpty(sectionName)) + return null; + + // Validate the data source + switch (_dataSource) + { + // If we don't have a valid data source, we can't read the section data + case DataSource.UNKNOWN: + return null; + + // Byte array data requires both a valid array and offset + case DataSource.ByteArray: + if (_byteArrayData == null || _byteArrayOffset < 0) + return null; + break; + + // Stream data requires both a valid stream + case DataSource.Stream: + if (_streamData == null) + return null; + break; + } + + // Make sure the section exists + string[] sectionNames = GetSectionNames(); + if (sectionNames == null) + return null; + + // Get the section index from the array + int sectionIndex = Array.IndexOf(sectionNames, sectionName); + if (sectionIndex < 0) + return null; + + // Get the section data from the table + var section = _executable.SectionTable[sectionIndex]; + uint sectionAddress = section.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable); + if (sectionAddress == 0) + return null; + uint sectionSize = section.SizeOfRawData; + + lock (_rawSectionsLock) + { + // If we already have cached data, just use that immediately + if (_rawSectionData != null && _rawSectionData.ContainsKey(sectionName)) + return _rawSectionData[sectionName]; + + // If the section dictionary doesn't exist, create it + if (_rawSectionData == null) + _rawSectionData = new Dictionary(); + + // Populate the raw section data based on the source + byte[] sectionData = null; + switch (_dataSource) + { + case DataSource.ByteArray: + if (_byteArrayOffset + sectionAddress + sectionSize >= _byteArrayData.Length) + return null; + sectionData = new byte[sectionSize]; + Array.Copy(_byteArrayData, _byteArrayOffset + sectionAddress, sectionData, 0, sectionSize); + break; + + case DataSource.Stream: + if (sectionAddress + sectionSize >= _streamData.Length) + return null; + long currentLocation = _streamData.Position; + _streamData.Seek(sectionAddress, SeekOrigin.Begin); + sectionData = _streamData.ReadBytes((int)sectionSize); + _streamData.Seek(currentLocation, SeekOrigin.Begin); + break; + } + + // Cache and return the section data, even if null + _rawSectionData[sectionName] = sectionData; + return sectionData; + } + } + /// /// Pretty print the New Executable information ///