Add data source to all wrappers, add note

This commit is contained in:
Matt Nadareski
2022-12-02 17:19:59 -08:00
parent 3c5d670924
commit a59bedec5d
5 changed files with 296 additions and 8 deletions

View File

@@ -0,0 +1,23 @@
namespace BurnOutSharp.Wrappers
{
/// <summary>
/// Location that the data originated from
/// </summary>
internal enum DataSource
{
/// <summary>
/// Unknown origin / testing
/// </summary>
UNKNOWN = 0,
/// <summary>
/// Byte array with offset
/// </summary>
ByteArray = 1,
/// <summary>
/// Stream
/// </summary>
Stream = 2,
}
}

View File

@@ -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
/// </summary>
private Models.LinearExecutable.Executable _executable;
/// <summary>
/// Source of the original data
/// </summary>
private DataSource _dataSource = DataSource.UNKNOWN;
/// <summary>
/// Source byte array data
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.ByteArray"/></remarks>
private byte[] _byteArrayData = null;
/// <summary>
/// Source byte array data offset
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.ByteArray"/></remarks>
private int _byteArrayOffset = -1;
/// <summary>
/// Source Stream data
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.Stream"/></remarks>
private Stream _streamData = null;
#endregion
/// <summary>
@@ -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;
}

View File

@@ -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
/// </summary>
private Models.MSDOS.Executable _executable;
/// <summary>
/// Source of the original data
/// </summary>
private DataSource _dataSource = DataSource.UNKNOWN;
/// <summary>
/// Source byte array data
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.ByteArray"/></remarks>
private byte[] _byteArrayData = null;
/// <summary>
/// Source byte array data offset
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.ByteArray"/></remarks>
private int _byteArrayOffset = -1;
/// <summary>
/// Source Stream data
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.Stream"/></remarks>
private Stream _streamData = null;
#endregion
/// <summary>
@@ -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;
}

View File

@@ -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
/// </summary>
private Models.NewExecutable.Executable _executable;
/// <summary>
/// Source of the original data
/// </summary>
private DataSource _dataSource = DataSource.UNKNOWN;
/// <summary>
/// Source byte array data
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.ByteArray"/></remarks>
private byte[] _byteArrayData = null;
/// <summary>
/// Source byte array data offset
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.ByteArray"/></remarks>
private int _byteArrayOffset = -1;
/// <summary>
/// Source Stream data
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.Stream"/></remarks>
private Stream _streamData = null;
#endregion
/// <summary>
@@ -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;
}

View File

@@ -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
/// </summary>
private Models.PortableExecutable.Executable _executable;
/// <summary>
/// Source of the original data
/// </summary>
private DataSource _dataSource = DataSource.UNKNOWN;
/// <summary>
/// Source byte array data
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.ByteArray"/></remarks>
private byte[] _byteArrayData = null;
/// <summary>
/// Source byte array data offset
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.ByteArray"/></remarks>
private int _byteArrayOffset = -1;
/// <summary>
/// Source Stream data
/// </summary>
/// <remarks>This is only populated if <see cref="_dataSource"/> is <see cref="DataSource.Stream"/></remarks>
private Stream _streamData = null;
/// <summary>
/// Array of santiized section names
/// </summary>
private string[] _sectionNames = null;
/// <summary>
/// Lock object for concurrent modifications on <see cref="_rawSectionData"/>
/// </summary>
private readonly object _rawSectionsLock = new object();
/// <summary>
/// Cached raw section data
/// </summary>
private Dictionary<string, byte[]> _rawSectionData = null;
#endregion
/// <summary>
@@ -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;
}
/// <summary>
/// Get all section names from the executable
/// </summary>
/// <returns>A PE executable wrapper on success, null on failure</returns>
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;
}
/// <summary>
/// Get raw section data from the source file
/// </summary>
/// <param name="sectionName">Stream representing the executable</param>
/// <returns>Byte array representing the data, null on error</returns>
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<string, byte[]>();
// 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;
}
}
/// <summary>
/// Pretty print the New Executable information
/// </summary>