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
///