using System.IO; using System.Text; namespace BinaryObjectScanner.Wrappers { public class MSDOS : WrapperBase { #region Descriptive Properties /// public override string Description => "MS-DOS Executable"; #endregion #region Pass-Through Properties #region Header /// public string Magic => _executable.Header.Magic; /// public ushort LastPageBytes => _executable.Header.LastPageBytes; /// public ushort Pages => _executable.Header.Pages; /// public ushort RelocationItems => _executable.Header.RelocationItems; /// public ushort HeaderParagraphSize => _executable.Header.HeaderParagraphSize; /// public ushort MinimumExtraParagraphs => _executable.Header.MinimumExtraParagraphs; /// public ushort MaximumExtraParagraphs => _executable.Header.MaximumExtraParagraphs; /// public ushort InitialSSValue => _executable.Header.InitialSSValue; /// public ushort InitialSPValue => _executable.Header.InitialSPValue; /// public ushort Checksum => _executable.Header.Checksum; /// public ushort InitialIPValue => _executable.Header.InitialIPValue; /// public ushort InitialCSValue => _executable.Header.InitialCSValue; /// public ushort RelocationTableAddr => _executable.Header.RelocationTableAddr; /// public ushort OverlayNumber => _executable.Header.OverlayNumber; #endregion #region PE Extensions /// public ushort[] Reserved1 => _executable.Header.Reserved1; /// public ushort OEMIdentifier => _executable.Header.OEMIdentifier; /// public ushort OEMInformation => _executable.Header.OEMInformation; /// public ushort[] Reserved2 => _executable.Header.Reserved2; /// public uint NewExeHeaderAddr => _executable.Header.NewExeHeaderAddr; #endregion #region Relocation Table /// public BinaryObjectScanner.Models.MSDOS.RelocationEntry[] RelocationTable => _executable.RelocationTable; #endregion #endregion #region Instance Variables /// /// Internal representation of the executable /// private BinaryObjectScanner.Models.MSDOS.Executable _executable; #endregion #region Constructors /// /// Private constructor /// private MSDOS() { } /// /// Create an MS-DOS executable from a byte array and offset /// /// Byte array representing the executable /// Offset within the array to parse /// An MS-DOS executable wrapper on success, null on failure public static MSDOS Create(byte[] data, int offset) { // If the data is invalid if (data == null) return null; // If the offset is out of bounds if (offset < 0 || offset >= data.Length) return null; // Create a memory stream and use that MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset); return Create(dataStream); } /// /// Create an MS-DOS executable from a Stream /// /// Stream representing the executable /// An MS-DOS executable wrapper on success, null on failure public static MSDOS Create(Stream data) { // If the data is invalid if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead) return null; var executable = Builders.MSDOS.ParseExecutable(data); if (executable == null) return null; var wrapper = new MSDOS { _executable = executable, _dataSource = DataSource.Stream, _streamData = data, }; return wrapper; } #endregion #region Printing /// public override StringBuilder PrettyPrint() { StringBuilder builder = new StringBuilder(); builder.AppendLine("MS-DOS Executable Information:"); builder.AppendLine("-------------------------"); builder.AppendLine(); PrintHeader(builder); PrintRelocationTable(builder); return builder; } /// /// Print header information /// /// StringBuilder to append information to private void PrintHeader(StringBuilder builder) { builder.AppendLine(" Header Information:"); builder.AppendLine(" -------------------------"); builder.AppendLine($" Magic number: {Magic}"); builder.AppendLine($" Last page bytes: {LastPageBytes} (0x{LastPageBytes:X})"); builder.AppendLine($" Pages: {Pages} (0x{Pages:X})"); builder.AppendLine($" Relocation items: {RelocationItems} (0x{RelocationItems:X})"); builder.AppendLine($" Header paragraph size: {HeaderParagraphSize} (0x{HeaderParagraphSize:X})"); builder.AppendLine($" Minimum extra paragraphs: {MinimumExtraParagraphs} (0x{MinimumExtraParagraphs:X})"); builder.AppendLine($" Maximum extra paragraphs: {MaximumExtraParagraphs} (0x{MaximumExtraParagraphs:X})"); builder.AppendLine($" Initial SS value: {InitialSSValue} (0x{InitialSSValue:X})"); builder.AppendLine($" Initial SP value: {InitialSPValue} (0x{InitialSPValue:X})"); builder.AppendLine($" Checksum: {Checksum} (0x{Checksum:X})"); builder.AppendLine($" Initial IP value: {InitialIPValue} (0x{InitialIPValue:X})"); builder.AppendLine($" Initial CS value: {InitialCSValue} (0x{InitialCSValue:X})"); builder.AppendLine($" Relocation table address: {RelocationTableAddr} (0x{RelocationTableAddr:X})"); builder.AppendLine($" Overlay number: {OverlayNumber} (0x{OverlayNumber:X})"); } /// /// Print relocation table information /// /// StringBuilder to append information to private void PrintRelocationTable(StringBuilder builder) { builder.AppendLine(" Relocation Table Information:"); builder.AppendLine(" -------------------------"); if (RelocationItems == 0 || RelocationTable.Length == 0) { builder.AppendLine(" No relocation table items"); } else { for (int i = 0; i < RelocationTable.Length; i++) { var entry = RelocationTable[i]; builder.AppendLine($" Relocation Table Entry {i}"); builder.AppendLine($" Offset: {entry.Offset} (0x{entry.Offset:X})"); builder.AppendLine($" Segment: {entry.Segment} (0x{entry.Segment:X})"); } } builder.AppendLine(); } #if NET6_0_OR_GREATER /// public override string ExportJSON() => System.Text.Json.JsonSerializer.Serialize(_executable, _jsonSerializerOptions); #endif #endregion } }