diff --git a/BurnOutSharp.Wrappers/Enums.cs b/BurnOutSharp.Wrappers/Enums.cs index c0a9b0f3..b74c374f 100644 --- a/BurnOutSharp.Wrappers/Enums.cs +++ b/BurnOutSharp.Wrappers/Enums.cs @@ -3,7 +3,7 @@ namespace BurnOutSharp.Wrappers /// /// Location that the data originated from /// - internal enum DataSource + public enum DataSource { /// /// Unknown origin / testing diff --git a/BurnOutSharp.Wrappers/LinearExecutable.cs b/BurnOutSharp.Wrappers/LinearExecutable.cs index f7321c30..a65865b6 100644 --- a/BurnOutSharp.Wrappers/LinearExecutable.cs +++ b/BurnOutSharp.Wrappers/LinearExecutable.cs @@ -1,9 +1,8 @@ using System.IO; -// TODO: Create base class for all wrappers namespace BurnOutSharp.Wrappers { - public class LinearExecutable + public class LinearExecutable : WrapperBase { #region Pass-Through Properties @@ -285,29 +284,6 @@ 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 /// @@ -360,7 +336,7 @@ namespace BurnOutSharp.Wrappers /// /// Pretty print the Linear Executable information /// - public void Print() + public override void Print() { // TODO: Implement printing } diff --git a/BurnOutSharp.Wrappers/MSDOS.cs b/BurnOutSharp.Wrappers/MSDOS.cs index 11625069..d9e95fa9 100644 --- a/BurnOutSharp.Wrappers/MSDOS.cs +++ b/BurnOutSharp.Wrappers/MSDOS.cs @@ -1,10 +1,9 @@ using System; using System.IO; -// TODO: Create base class for all wrappers namespace BurnOutSharp.Wrappers { - public class MSDOS + public class MSDOS : WrapperBase { #region Pass-Through Properties @@ -89,29 +88,6 @@ 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 /// @@ -164,7 +140,7 @@ namespace BurnOutSharp.Wrappers /// /// Pretty print the MS-DOS executable information /// - public void Print() + public override void Print() { Console.WriteLine("MS-DOS Executable Information:"); Console.WriteLine("-------------------------"); diff --git a/BurnOutSharp.Wrappers/NewExecutable.cs b/BurnOutSharp.Wrappers/NewExecutable.cs index 2b7805f0..cb2d426c 100644 --- a/BurnOutSharp.Wrappers/NewExecutable.cs +++ b/BurnOutSharp.Wrappers/NewExecutable.cs @@ -4,10 +4,9 @@ using System.IO; using System.Text; using static BurnOutSharp.Builder.Extensions; -// TODO: Create base class for all wrappers namespace BurnOutSharp.Wrappers { - public class NewExecutable + public class NewExecutable : WrapperBase { #region Pass-Through Properties @@ -217,29 +216,6 @@ 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 /// @@ -292,7 +268,7 @@ namespace BurnOutSharp.Wrappers /// /// Pretty print the New Executable information /// - public void Print() + public override void Print() { Console.WriteLine("New Executable Information:"); Console.WriteLine("-------------------------"); diff --git a/BurnOutSharp.Wrappers/PortableExecutable.cs b/BurnOutSharp.Wrappers/PortableExecutable.cs index 299b2ee0..0e2c8b91 100644 --- a/BurnOutSharp.Wrappers/PortableExecutable.cs +++ b/BurnOutSharp.Wrappers/PortableExecutable.cs @@ -5,10 +5,9 @@ using System.Text; using System.Xml; using static BurnOutSharp.Builder.Extensions; -// TODO: Create base class for all wrappers namespace BurnOutSharp.Wrappers { - public class PortableExecutable + public class PortableExecutable : WrapperBase { #region Pass-Through Properties @@ -316,6 +315,33 @@ namespace BurnOutSharp.Wrappers #region Extension Properties + /// + /// Array of sanitized section names + /// + public string[] SectionNames + { + get + { + // 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; + } + } + // TODO: Determine what extension properties are needed #endregion @@ -328,30 +354,7 @@ 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 + /// Array of sanitized section names /// private string[] _sectionNames = null; @@ -363,7 +366,7 @@ namespace BurnOutSharp.Wrappers /// /// Cached raw section data /// - private Dictionary _rawSectionData = null; + private readonly Dictionary _rawSectionData = new Dictionary(); #endregion @@ -414,31 +417,6 @@ namespace BurnOutSharp.Wrappers 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 /// @@ -451,32 +429,15 @@ namespace BurnOutSharp.Wrappers 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; - } + if (!DataSourceIsValid()) + return null; // Make sure the section exists - string[] sectionNames = GetSectionNames(); - if (sectionNames == null) + if (SectionNames == null) return null; // Get the section index from the array - int sectionIndex = Array.IndexOf(sectionNames, sectionName); + int sectionIndex = Array.IndexOf(SectionNames, sectionName); if (sectionIndex < 0) return null; @@ -490,33 +451,11 @@ namespace BurnOutSharp.Wrappers lock (_rawSectionsLock) { // If we already have cached data, just use that immediately - if (_rawSectionData != null && _rawSectionData.ContainsKey(sectionName)) + if (_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; - } + byte[] sectionData = ReadFromDataSource((int)sectionAddress, (int)sectionSize); // Cache and return the section data, even if null _rawSectionData[sectionName] = sectionData; @@ -527,7 +466,7 @@ namespace BurnOutSharp.Wrappers /// /// Pretty print the New Executable information /// - public void Print() + public override void Print() { Console.WriteLine("Portable Executable Information:"); Console.WriteLine("-------------------------"); diff --git a/BurnOutSharp.Wrappers/WrapperBase.cs b/BurnOutSharp.Wrappers/WrapperBase.cs new file mode 100644 index 00000000..49ecdc7c --- /dev/null +++ b/BurnOutSharp.Wrappers/WrapperBase.cs @@ -0,0 +1,135 @@ +using System; +using System.IO; +using static BurnOutSharp.Builder.Extensions; + +namespace BurnOutSharp.Wrappers +{ + public abstract class WrapperBase + { + #region Instance Variabled + + /// + /// Source of the original data + /// + protected DataSource _dataSource = DataSource.UNKNOWN; + + /// + /// Source byte array data + /// + /// This is only populated if is + protected byte[] _byteArrayData = null; + + /// + /// Source byte array data offset + /// + /// This is only populated if is + protected int _byteArrayOffset = -1; + + /// + /// Source Stream data + /// + /// This is only populated if is + protected Stream _streamData = null; + + #endregion + + #region Data + + /// + /// Validate the backing data source + /// + /// True if the data source is valid, false otherwise + protected bool DataSourceIsValid() + { + switch (_dataSource) + { + // Byte array data requires both a valid array and offset + case DataSource.ByteArray: + return _byteArrayData != null && _byteArrayOffset >= 0; + + // Stream data requires both a valid stream + case DataSource.Stream: + return _streamData != null && _streamData.CanRead && _streamData.CanSeek; + + // Everything else is invalid + case DataSource.UNKNOWN: + default: + return false; + } + } + + /// + /// Check if a data segment is valid in the data source + /// + /// Position in the source + /// Length of the data to check + /// True if the positional data is valid, false otherwise + protected bool SegmentValid(int position, int length) + { + // Validate the data souece + if (!DataSourceIsValid()) + return false; + + switch (_dataSource) + { + case DataSource.ByteArray: + return _byteArrayOffset + position + length < _byteArrayData.Length; + + case DataSource.Stream: + return position + length < _streamData.Length; + + // Everything else is invalid + case DataSource.UNKNOWN: + default: + return false; + } + } + + /// + /// Read data from the source + /// + /// Position in the source to read from + /// Length of the requested data + /// Byte array containing the requested data, null on error + protected byte[] ReadFromDataSource(int position, int length) + { + // Validate the data souece + if (!DataSourceIsValid()) + return null; + + // Validate the requested segment + if (!SegmentValid(position, length)) + return null; + + // Read and retuen the data + byte[] sectionData = null; + switch (_dataSource) + { + case DataSource.ByteArray: + sectionData = new byte[length]; + Array.Copy(_byteArrayData, _byteArrayOffset + position, sectionData, 0, length); + break; + + case DataSource.Stream: + long currentLocation = _streamData.Position; + _streamData.Seek(position, SeekOrigin.Begin); + sectionData = _streamData.ReadBytes(length); + _streamData.Seek(currentLocation, SeekOrigin.Begin); + break; + } + + return sectionData; + } + + #endregion + + #region Printing + + /// + /// Pretty print the Executable information + /// + public abstract void Print(); + + #endregion + } +} \ No newline at end of file