private void Load() { var data = new Span <byte>(FileContent); DOSHeader = new MZHeader(FileContent); //Verify old DOS header is correct if (DOSHeader.Signature != 23117) { throw new Exception("Invalid Header"); } //Locate Windows Header ushort windowsHeaderOffset; if (data[0x18] >= 0x40) { windowsHeaderOffset = BitConverter.ToUInt16(FileContent, 0x3C); } else { throw new Exception("Unable to locate Windows Header location"); } //Load Windows Header WindowsHeader = new NEHeader(data.Slice(windowsHeaderOffset, 0x3F).ToArray()) { FileOffset = windowsHeaderOffset }; //Adjust Offsets According to Spec (Offset from beginning of Windows Header, not file) WindowsHeader.SegmentTableOffset += windowsHeaderOffset; WindowsHeader.ResourceTableOffset += windowsHeaderOffset; WindowsHeader.ResidentNameTableOffset += windowsHeaderOffset; WindowsHeader.ModleReferenceTableOffset += windowsHeaderOffset; WindowsHeader.ImportedNamesTableOffset += windowsHeaderOffset; WindowsHeader.EntryTableOffset += windowsHeaderOffset; //Load Segment Table SegmentTable = new List <Segment>(WindowsHeader.SegmentTableEntries); for (var i = 0; i < WindowsHeader.SegmentTableEntries; i++) { //Load Segment Header (8 bytes per record) var segment = new Segment(data.Slice(WindowsHeader.SegmentTableOffset + (i * 8), 8).ToArray()) { Ordinal = (ushort)(i + 1) }; segment.Offset <<= WindowsHeader.LogicalSectorAlignmentShift; //Attach Segment Data segment.Data = data.Slice((int)segment.Offset, segment.Length).ToArray(); //Attach Relocation Records if (segment.Flags.Contains(EnumSegmentFlags.HasRelocationInfo)) { var relocationInfoCursor = (int)segment.Offset + segment.Length; var relocationRecordEntries = BitConverter.ToUInt16(FileContent, relocationInfoCursor); relocationInfoCursor += 2; var records = new Dictionary <ushort, RelocationRecord>(); for (var j = 0; j < relocationRecordEntries; j++) { var relocationRecord = new RelocationRecord(_logger) { Data = data.Slice(relocationInfoCursor + j * 8, 8).ToArray() }; records.Add(relocationRecord.Offset, relocationRecord); } segment.RelocationRecords = records; } SegmentTable.Add(segment); } //Load Resource Table //ResourceTable = new List<ResourceRecord>(); //TODO -- Resource Table isn't used by MBBS modules so we'll skip loading this for now //TODO -- Implement this in a future version //Load Resident Name Table ResidentNameTable = new List <ResidentName>(); for (var i = 0; i < WindowsHeader.ModleReferenceTableOffset; i += 2) { var residentName = new ResidentName(); var residentNameLength = data[WindowsHeader.ResidentNameTableOffset + i]; //End of Names if (residentNameLength == 0) { break; } i++; residentName.Name = Encoding.Default.GetString(data.Slice(WindowsHeader.ResidentNameTableOffset + i, residentNameLength) .ToArray()); i += residentNameLength; residentName.IndexIntoEntryTable = BitConverter.ToUInt16(FileContent, WindowsHeader.ResidentNameTableOffset + i); ResidentNameTable.Add(residentName); } //Load Module & Imported Name Reference Tables ModuleReferenceTable = new List <ModuleReference>(WindowsHeader.ModuleReferenceTableEntries); ImportedNameTable = new Dictionary <ushort, ImportedName>(); for (var i = 0; i < WindowsHeader.ModuleReferenceTableEntries; i++) { var nameOffset = BitConverter.ToUInt16(FileContent, WindowsHeader.ModleReferenceTableOffset + i * 2); var fileOffset = (ushort)(nameOffset + WindowsHeader.ImportedNamesTableOffset); var module = new ModuleReference(); var importedName = new ImportedName() { Offset = nameOffset, FileOffset = fileOffset }; var name = Encoding.Default.GetString(data.Slice(fileOffset + 1, data[fileOffset]).ToArray()); module.Name = name; importedName.Name = name; importedName.Ordinal = (ushort)(i + 1); //Ordinal Index in Resource Tables start with 1 ModuleReferenceTable.Add(module); ImportedNameTable.Add(importedName.Ordinal, importedName); } //Load Entry Table EntryTable = new List <Entry>(data[WindowsHeader.EntryTableOffset]); //Value of 0 denotes no segment data if (data[WindowsHeader.EntryTableOffset] > 0) { var entryByteOffset = 0; ushort entryOrdinal = 1; while (WindowsHeader.EntryTableOffset + entryByteOffset < WindowsHeader.NonResidentNameTableOffset) { //0xFF is moveable (6 bytes), anything else is fixed as it becomes the segment number var entryCount = data[WindowsHeader.EntryTableOffset + entryByteOffset]; var entrySegment = data[WindowsHeader.EntryTableOffset + entryByteOffset + 1]; if (entryCount == 1 && entrySegment == 0) { entryByteOffset += 2; entryOrdinal += 1; continue; } var entrySize = entrySegment == 0xFF ? 6 : 3; for (var i = 0; i < entryCount; i++) { var entry = new Entry { SegmentNumber = entrySegment }; if (entrySize == 3) { entry.Flag = data[WindowsHeader.EntryTableOffset + entryByteOffset + 2 + entrySize * i]; entry.Offset = BitConverter.ToUInt16(FileContent, WindowsHeader.EntryTableOffset + entryByteOffset + 3 + entrySize * i); entry.SegmentNumber = entrySegment; entry.Ordinal = entryOrdinal; //First Entry is the Resident Name table is the module name, so we shift the ordinals by 1 to line up } else { entry.Flag = data[WindowsHeader.EntryTableOffset + entryByteOffset + 2 + entrySize * i]; entry.SegmentNumber = data[WindowsHeader.EntryTableOffset + entryByteOffset + 5 + (entrySize * i)]; entry.Offset = BitConverter.ToUInt16(FileContent, WindowsHeader.EntryTableOffset + entryByteOffset + 6 + entrySize * i); } entryOrdinal++; EntryTable.Add(entry); } entryByteOffset += (entryCount * entrySize) + 2; } } //Load Non-Resident Name Table NonResidentNameTable = new List <NonResidentName>(); for (var i = (int)WindowsHeader.NonResidentNameTableOffset; i < (WindowsHeader.NonResidentNameTableOffset + WindowsHeader.NonResidentNameTableLength); i += 2) { var nameLength = data[i]; i++; var name = Encoding.Default.GetString(data.Slice(i, nameLength).ToArray()); i += nameLength; var indexIntoEntryTable = BitConverter.ToUInt16(FileContent, i); NonResidentNameTable.Add(new NonResidentName() { Name = name, IndexIntoEntryTable = indexIntoEntryTable }); } }
void Initialize() { Recognized = false; if (BaseStream == null) { return; } BaseExecutable = new MZ(BaseStream); if (!BaseExecutable.Recognized) { return; } if (BaseExecutable.Header.new_offset >= BaseStream.Length) { return; } BaseStream.Seek(BaseExecutable.Header.new_offset, SeekOrigin.Begin); byte[] buffer = new byte[Marshal.SizeOf(typeof(NEHeader))]; BaseStream.Read(buffer, 0, buffer.Length); IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length); Marshal.Copy(buffer, 0, hdrPtr, buffer.Length); Header = (NEHeader)Marshal.PtrToStructure(hdrPtr, typeof(NEHeader)); Marshal.FreeHGlobal(hdrPtr); if (Header.signature != SIGNATURE) { return; } Recognized = true; Type = "New Executable (NE)"; List <string> strings = new List <string>(); OperatingSystem reqOs = new OperatingSystem(); switch (Header.target_os) { case TargetOS.OS2: reqOs.Name = "OS/2"; if (Header.os_major > 0) { reqOs.MajorVersion = Header.os_major; reqOs.MinorVersion = Header.os_minor; } else { reqOs.MajorVersion = 1; reqOs.MinorVersion = 0; } if (Header.application_flags.HasFlag(ApplicationFlags.FullScreen) && !Header.application_flags.HasFlag(ApplicationFlags.GUICompatible) || !Header.application_flags.HasFlag(ApplicationFlags.FullScreen) && Header.application_flags.HasFlag(ApplicationFlags.GUICompatible)) { reqOs.Subsystem = "Console"; } else if (Header.application_flags.HasFlag(ApplicationFlags.FullScreen) && Header.application_flags.HasFlag(ApplicationFlags.GUICompatible)) { reqOs.Subsystem = "Presentation Manager"; } break; case TargetOS.Windows: case TargetOS.Win32: case TargetOS.Unknown: reqOs.Name = "Windows"; if (Header.os_major > 0) { reqOs.MajorVersion = Header.os_major; reqOs.MinorVersion = Header.os_minor; } else { switch (Header.target_os) { case TargetOS.Windows: reqOs.MajorVersion = 2; reqOs.MinorVersion = 0; break; case TargetOS.Unknown: reqOs.MajorVersion = 1; reqOs.MinorVersion = 0; break; } } break; case TargetOS.DOS: case TargetOS.Borland: reqOs.Name = "DOS"; reqOs.MajorVersion = Header.os_major; reqOs.MinorVersion = Header.os_minor; if (Header.target_os == TargetOS.Borland) { reqOs.Subsystem = "Borland Operating System Services"; } break; default: reqOs.Name = $"Unknown code {(byte)Header.target_os}"; reqOs.MajorVersion = Header.os_major; reqOs.MinorVersion = Header.os_minor; break; } RequiredOperatingSystem = reqOs; if (Header.segment_count > 0 && Header.segment_table_offset > 0 && Header.segment_table_offset + BaseExecutable.Header.new_offset < BaseStream.Length) { BaseStream.Position = Header.segment_table_offset + BaseExecutable.Header.new_offset; segments = new SegmentEntry[Header.segment_count]; for (int i = 0; i < segments.Length; i++) { buffer = new byte[Marshal.SizeOf(typeof(SegmentEntry))]; BaseStream.Read(buffer, 0, buffer.Length); segments[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian <SegmentEntry>(buffer); } } // Some executable indicates 0 entries, some indicate a table start and no limit, will need to explore till next item ushort resourceUpperLimit = ushort.MaxValue; if (Header.entry_table_offset >= Header.resource_table_offset && Header.entry_table_offset <= resourceUpperLimit) { resourceUpperLimit = Header.entry_table_offset; } if (Header.segment_table_offset >= Header.resource_table_offset && Header.segment_table_offset <= resourceUpperLimit) { resourceUpperLimit = Header.segment_table_offset; } if (Header.module_reference_offset >= Header.resource_table_offset && Header.module_reference_offset <= resourceUpperLimit) { resourceUpperLimit = Header.module_reference_offset; } if (Header.nonresident_names_offset >= Header.resource_table_offset && Header.nonresident_names_offset <= resourceUpperLimit) { resourceUpperLimit = (ushort)Header.nonresident_names_offset; } if (Header.resident_names_offset >= Header.resource_table_offset && Header.resident_names_offset <= resourceUpperLimit) { resourceUpperLimit = Header.resident_names_offset; } if (Header.imported_names_offset >= Header.resource_table_offset && Header.imported_names_offset <= resourceUpperLimit) { resourceUpperLimit = Header.imported_names_offset; } if (Header.resource_table_offset < resourceUpperLimit && Header.resource_table_offset != 0) { if (Header.target_os == TargetOS.Windows || Header.target_os == TargetOS.Win32 || Header.target_os == TargetOS.Unknown) { Resources = GetResources(BaseStream, BaseExecutable.Header.new_offset, Header.resource_table_offset, resourceUpperLimit); for (int t = 0; t < Resources.types.Length; t++) { Resources.types[t].resources = Resources.types[t].resources.OrderBy(r => r.name).ToArray(); } Resources.types = Resources.types.OrderBy(t => t.name).ToArray(); Versions = GetVersions().ToArray(); strings.AddRange(from v in Versions from s in v.StringsByLanguage from k in s.Value select k.Value); foreach (ResourceType rtype in Resources.types) { if (rtype.name != "RT_STRING") { continue; } strings.AddRange(GetWindowsStrings(rtype)); } } else if (Header.target_os == TargetOS.OS2 && segments != null && Header.resource_entries > 0) { BaseStream.Position = BaseExecutable.Header.new_offset + Header.resource_table_offset; buffer = new byte[Header.resource_entries * 4]; BaseStream.Read(buffer, 0, buffer.Length); ResourceTableEntry[] entries = new ResourceTableEntry[Header.resource_entries]; for (int i = 0; i < entries.Length; i++) { entries[i].etype = BitConverter.ToUInt16(buffer, i * 4 + 0); entries[i].ename = BitConverter.ToUInt16(buffer, i * 4 + 2); } SegmentEntry[] resourceSegments = new SegmentEntry[Header.resource_entries]; Array.Copy(segments, Header.segment_count - Header.resource_entries, resourceSegments, 0, Header.resource_entries); SegmentEntry[] realSegments = new SegmentEntry[Header.segment_count - Header.resource_entries]; Array.Copy(segments, 0, realSegments, 0, realSegments.Length); segments = realSegments; SortedDictionary <ushort, List <Resource> > os2resources = new SortedDictionary <ushort, List <Resource> >(); for (int i = 0; i < entries.Length; i++) { os2resources.TryGetValue(entries[i].etype, out List <Resource> thisResourceType); if (thisResourceType == null) { thisResourceType = new List <Resource>(); } Resource thisResource = new Resource { id = entries[i].ename, name = $"{entries[i].ename}", flags = (ResourceFlags)resourceSegments[i].dwFlags, dataOffset = (uint)(resourceSegments[i].dwLogicalSectorOffset << Header.alignment_shift), length = resourceSegments[i].dwSegmentLength }; if (thisResource.length == 0) { thisResource.length = 65536; } if (thisResource.dataOffset == 0) { thisResource.dataOffset = 65536; } if ((resourceSegments[i].dwFlags & (ushort)SegmentFlags.Huge) == (ushort)SegmentFlags.Huge) { thisResource.length <<= Header.alignment_shift; } thisResource.data = new byte[thisResource.length]; BaseStream.Position = thisResource.dataOffset; BaseStream.Read(thisResource.data, 0, thisResource.data.Length); thisResourceType.Add(thisResource); os2resources.Remove(entries[i].etype); os2resources.Add(entries[i].etype, thisResourceType); } if (os2resources.Count > 0) { Resources = new ResourceTable(); int counter = 0; Resources.types = new ResourceType[os2resources.Count]; foreach (KeyValuePair <ushort, List <Resource> > kvp in os2resources) { Resources.types[counter].count = (ushort)kvp.Value.Count; Resources.types[counter].id = kvp.Key; Resources.types[counter].name = Os2.Resources.IdToName(kvp.Key); Resources.types[counter].resources = kvp.Value.OrderBy(r => r.id).ToArray(); counter++; } foreach (ResourceType rtype in Resources.types) { if (rtype.name != "RT_STRING") { continue; } strings.AddRange(GetOs2Strings(rtype)); } } } } resourceUpperLimit = ushort.MaxValue; if (Header.entry_table_offset >= Header.module_reference_offset && Header.entry_table_offset <= resourceUpperLimit) { resourceUpperLimit = Header.entry_table_offset; } if (Header.segment_table_offset >= Header.module_reference_offset && Header.segment_table_offset <= resourceUpperLimit) { resourceUpperLimit = Header.segment_table_offset; } if (Header.resource_table_offset >= Header.module_reference_offset && Header.resource_table_offset <= resourceUpperLimit) { resourceUpperLimit = Header.resource_table_offset; } if (Header.nonresident_names_offset >= Header.module_reference_offset && Header.nonresident_names_offset <= resourceUpperLimit) { resourceUpperLimit = (ushort)Header.nonresident_names_offset; } if (Header.imported_names_offset >= Header.module_reference_offset && Header.imported_names_offset <= resourceUpperLimit) { resourceUpperLimit = Header.imported_names_offset; } if (Header.module_reference_offset < resourceUpperLimit && Header.module_reference_offset != 0 && Header.reference_count > 0) { short[] referenceOffsets = new short[Header.reference_count]; buffer = new byte[2]; BaseStream.Position = Header.module_reference_offset + BaseExecutable.Header.new_offset; for (int i = 0; i < Header.reference_count; i++) { BaseStream.Read(buffer, 0, 2); referenceOffsets[i] = BitConverter.ToInt16(buffer, 0); } ImportedNames = new string[Header.reference_count]; for (int i = 0; i < Header.reference_count; i++) { BaseStream.Position = Header.imported_names_offset + BaseExecutable.Header.new_offset + referenceOffsets[i]; int len = BaseStream.ReadByte(); buffer = new byte[len]; BaseStream.Read(buffer, 0, len); ImportedNames[i] = Encoding.ASCII.GetString(buffer); } } resourceUpperLimit = ushort.MaxValue; if (Header.entry_table_offset >= Header.resident_names_offset && Header.entry_table_offset <= resourceUpperLimit) { resourceUpperLimit = Header.entry_table_offset; } if (Header.segment_table_offset >= Header.resident_names_offset && Header.segment_table_offset <= resourceUpperLimit) { resourceUpperLimit = Header.segment_table_offset; } if (Header.module_reference_offset >= Header.resident_names_offset && Header.module_reference_offset <= resourceUpperLimit) { resourceUpperLimit = Header.module_reference_offset; } if (Header.nonresident_names_offset >= Header.resident_names_offset && Header.nonresident_names_offset <= resourceUpperLimit) { resourceUpperLimit = (ushort)Header.nonresident_names_offset; } if (Header.imported_names_offset >= Header.resident_names_offset && Header.imported_names_offset <= resourceUpperLimit) { resourceUpperLimit = Header.imported_names_offset; } if (Header.resident_names_offset < resourceUpperLimit && Header.resident_names_offset != 0) { ResidentNames = GetResidentStrings(BaseStream, BaseExecutable.Header.new_offset, Header.resident_names_offset, resourceUpperLimit); if (ResidentNames.Length >= 1) { ModuleName = ResidentNames[0].name; if (ResidentNames.Length > 1) { ResidentName[] newResidentNames = new ResidentName[ResidentNames.Length - 1]; Array.Copy(ResidentNames, 1, newResidentNames, 0, ResidentNames.Length - 1); ResidentNames = newResidentNames; } else { ResidentNames = null; } } } if (Header.nonresident_table_size > 0) { NonResidentNames = GetResidentStrings(BaseStream, Header.nonresident_names_offset, 0, (ushort)(Header.nonresident_names_offset + Header.nonresident_table_size)); if (NonResidentNames.Length >= 1) { ModuleDescription = NonResidentNames[0].name; if (NonResidentNames.Length > 1) { ResidentName[] newNonResidentNames = new ResidentName[NonResidentNames.Length - 1]; Array.Copy(NonResidentNames, 1, newNonResidentNames, 0, NonResidentNames.Length - 1); NonResidentNames = newNonResidentNames; } else { NonResidentNames = null; } } } if (!string.IsNullOrEmpty(ModuleName)) { strings.Add(ModuleName); } if (!string.IsNullOrEmpty(ModuleDescription)) { strings.Add(ModuleDescription); } if (strings.Count > 0) { Strings = strings.Distinct().OrderBy(s => s); } if (segments == null) { return; } List <Segment> libsegs = new List <Segment>(); foreach (SegmentEntry seg in segments) { Segment libseg = new Segment { Flags = $"{(SegmentFlags)(seg.dwFlags & SEGMENT_FLAGS_MASK)}", Name = (SegmentType)(seg.dwFlags & SEGMENT_TYPE_MASK) == SegmentType.Code ? ".text" : ".data", Offset = seg.dwLogicalSectorOffset << Header.alignment_shift, Size = seg.dwSegmentLength }; if (Header.target_os == TargetOS.OS2 && (seg.dwFlags & (int)SegmentFlags.Huge) == (int)SegmentFlags.Huge) { libseg.Size <<= Header.alignment_shift; } libsegs.Add(libseg); } Segments = libsegs.OrderBy(s => s.Offset).ToArray(); }