Segment[] LoadSegmentTable() { var objectTableEntries = new ObjectTableEntry[hdr.obj_no]; var objectPageTableEntries = new ObjectPageTableEntry[hdr.module_pages_no]; var rdr = new LeImageReader(RawImage, hdr.obj_table_off + lfaNew); var objTblEntryReader = new StructureReader <ObjectTableEntry>(rdr); for (int i = 0; i < hdr.obj_no; i++) { objectTableEntries[i] = objTblEntryReader.Read(); } rdr = new LeImageReader(RawImage, hdr.obj_page_table_off + lfaNew); if (hdr.signature == SIGNATURE16) { var objPageTblEntry16Reader = new StructureReader <ObjectPageTableEntry16>(rdr); for (int i = 0; i < hdr.module_pages_no; i++) { ObjectPageTableEntry16 page16 = objPageTblEntry16Reader.Read(); int pageNo = (page16.High << 8) + page16.Low; objectPageTableEntries[i] = new ObjectPageTableEntry { DataSize = (ushort)hdr.page_size, Flags = (PageTableAttributes)page16.Flags, PageDataOffset = (uint)((pageNo - 1) * hdr.page_size) }; } } else { var objPageTblEntryReader = new StructureReader <ObjectPageTableEntry>(rdr); for (int i = 0; i < hdr.module_pages_no; i++) { objectPageTableEntries[i] = objPageTblEntryReader.Read(); } } int debugSections = 0; int winrsrcSections = 0; if (hdr.debug_info_len > 0) { debugSections = 1; } if (hdr.win_res_len > 0) { winrsrcSections = 1; } Segment[] sections = new Segment[objectTableEntries.Length + debugSections + winrsrcSections]; for (int i = 0; i < objectTableEntries.Length; i++) { sections[i] = new Segment { Flags = objectTableEntries[i].ObjectFlags }; if (objectTableEntries[i].ObjectFlags.HasFlag(ObjectFlags.Resource)) { sections[i].Name = ".rsrc"; } else if (objectTableEntries[i].ObjectFlags.HasFlag(ObjectFlags.Executable)) { sections[i].Name = ".text"; } else if (!objectTableEntries[i].ObjectFlags.HasFlag(ObjectFlags.Writable)) { sections[i].Name = ".rodata"; } else if (new LeImageReader(objectTableEntries[i].Name).ReadCString(PrimitiveType.Char, Encoding.ASCII).ToString().ToLower() == "bss") { sections[i].Name = ".bss"; } else if (!string.IsNullOrWhiteSpace(new LeImageReader(objectTableEntries[i].Name).ReadCString(PrimitiveType.Char, Encoding.ASCII).ToString().Trim())) { sections[i].Name = new LeImageReader(objectTableEntries[i].Name).ReadCString(PrimitiveType.Char, Encoding.ASCII).ToString().Trim(); } else { sections[i].Name = ".data"; } if (objectTableEntries[i].PageTableEntries == 0 || objectTableEntries[i].PageTableIndex > objectPageTableEntries.Length) { sections[i].DataLength = objectTableEntries[i].VirtualSize; continue; } int shift = (int)(hdr.signature == SIGNATURE16 ? 0 : hdr.page_off_shift); if (objectPageTableEntries[objectTableEntries[i].PageTableIndex - 1] .Flags.HasFlag(PageTableAttributes.IteratedDataPage)) { sections[i].DataOffset = (objectPageTableEntries[objectTableEntries[i].PageTableIndex - 1].PageDataOffset << shift) + hdr.obj_iter_pages_off; } else if (objectPageTableEntries[objectTableEntries[i].PageTableIndex - 1] .Flags.HasFlag(PageTableAttributes.LegalPhysicalPage)) { sections[i].DataOffset = (objectPageTableEntries[objectTableEntries[i].PageTableIndex - 1].PageDataOffset << shift) + hdr.data_pages_off; } else { sections[i].DataOffset = 0; } sections[i].DataLength = 0; for (int j = 0; j < objectTableEntries[i].PageTableEntries; j++) { sections[i].DataLength += objectPageTableEntries[j + objectTableEntries[i].PageTableIndex - 1].DataSize; } if (sections[i].DataOffset + sections[i].DataLength > RawImage.Length) { sections[i].DataLength = (uint)RawImage.Length - sections[i].DataOffset; } sections[i].BaseAddress = objectTableEntries[i].RelocationBaseAddress; } if (winrsrcSections > 0) { sections[sections.Length - debugSections - winrsrcSections] = new Segment { Name = ".rsrc", DataLength = hdr.win_res_len, DataOffset = hdr.win_res_off } } ; if (debugSections > 0) { sections[sections.Length - debugSections] = new Segment { Name = ".debug", DataLength = hdr.debug_info_len, DataOffset = hdr.debug_info_off } } ; return(sections); }
// TODO: How to know VxD structure offset 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(LXHeader))]; BaseStream.Read(buffer, 0, buffer.Length); IntPtr hdrPtr = Marshal.AllocHGlobal(buffer.Length); Marshal.Copy(buffer, 0, hdrPtr, buffer.Length); Header = (LXHeader)Marshal.PtrToStructure(hdrPtr, typeof(LXHeader)); Marshal.FreeHGlobal(hdrPtr); Recognized = Header.signature == SIGNATURE || Header.signature == SIGNATURE16; if (!Recognized) { return; } Type = Header.signature == SIGNATURE16 ? "Linear Executable (LE)" : "Linear eXecutable (LX)"; IsBigEndian = Header.byte_order == 1 || Header.word_order == 1; List <string> strings = new List <string>(); OperatingSystem reqOs = new OperatingSystem(); switch (Header.os_type) { case TargetOS.OS2: reqOs.Name = "OS/2"; if (Header.module_flags.HasFlag(ModuleFlags.PMIncompatible) && !Header.module_flags.HasFlag(ModuleFlags.PMCompatible) || !Header.module_flags.HasFlag(ModuleFlags.PMIncompatible) && Header.module_flags.HasFlag(ModuleFlags.PMCompatible)) { reqOs.Subsystem = "Console"; } else if (Header.module_flags.HasFlag(ModuleFlags.PMIncompatible) && Header.module_flags.HasFlag(ModuleFlags.PMCompatible)) { reqOs.Subsystem = "Presentation Manager"; } break; case TargetOS.Windows: case TargetOS.Win32: case TargetOS.Unknown: reqOs.Name = "Windows"; break; case TargetOS.DOS: reqOs.Name = "Windows"; break; default: reqOs.Name = $"Unknown code {(ushort)Header.os_type}"; break; } RequiredOperatingSystem = reqOs; if (Header.resident_names_off != 0) { ResidentNames = GetResidentStrings(BaseStream, BaseExecutable.Header.new_offset, Header.resident_names_off); if (ResidentNames.Length >= 1) { ModuleName = ResidentNames[0].name; if (ResidentNames.Length > 1) { NE.ResidentName[] newResidentNames = new NE.ResidentName[ResidentNames.Length - 1]; Array.Copy(ResidentNames, 1, newResidentNames, 0, ResidentNames.Length - 1); ResidentNames = newResidentNames; } else { ResidentNames = null; } } } if (Header.nonresident_name_table_len > 0) { NonResidentNames = GetResidentStrings(BaseStream, 0, Header.nonresident_name_table_off); if (NonResidentNames.Length >= 1) { ModuleDescription = NonResidentNames[0].name; if (NonResidentNames.Length > 1) { NE.ResidentName[] newNonResidentNames = new NE.ResidentName[NonResidentNames.Length - 1]; Array.Copy(NonResidentNames, 1, newNonResidentNames, 0, NonResidentNames.Length - 1); NonResidentNames = newNonResidentNames; } else { NonResidentNames = null; } } } if (Header.import_module_table_off != 0 && Header.import_module_entries > 0) { BaseStream.Position = Header.import_module_table_off + BaseExecutable.Header.new_offset; ImportedNames = new string[Header.import_module_entries]; for (int i = 0; i < Header.import_module_entries; i++) { int len = BaseStream.ReadByte(); buffer = new byte[len]; BaseStream.Read(buffer, 0, len); ImportedNames[i] = Encoding.ASCII.GetString(buffer); } } if (!string.IsNullOrEmpty(ModuleName)) { strings.Add(ModuleName); } if (!string.IsNullOrEmpty(ModuleDescription)) { strings.Add(ModuleDescription); } objectTableEntries = new ObjectTableEntry[Header.obj_no]; objectPageTableEntries = new ObjectPageTableEntry[Header.module_pages_no]; BaseStream.Position = Header.obj_table_off + BaseExecutable.Header.new_offset; buffer = new byte[Marshal.SizeOf(typeof(ObjectTableEntry))]; for (int i = 0; i < Header.obj_no; i++) { BaseStream.Read(buffer, 0, buffer.Length); objectTableEntries[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian <ObjectTableEntry>(buffer); } BaseStream.Position = Header.obj_page_table_off + BaseExecutable.Header.new_offset; if (Header.signature == SIGNATURE16) { buffer = new byte[Marshal.SizeOf(typeof(ObjectPageTableEntry16))]; for (int i = 0; i < Header.module_pages_no; i++) { BaseStream.Read(buffer, 0, buffer.Length); ObjectPageTableEntry16 page16 = BigEndianMarshal.ByteArrayToStructureLittleEndian <ObjectPageTableEntry16>(buffer); int pageNo = (page16.High << 8) + page16.Low; objectPageTableEntries[i] = new ObjectPageTableEntry { DataSize = (ushort)Header.page_size, Flags = (PageTableAttributes)page16.Flags, PageDataOffset = (uint)((pageNo - 1) * Header.page_size) }; } } else { buffer = new byte[Marshal.SizeOf(typeof(ObjectPageTableEntry))]; for (int i = 0; i < Header.module_pages_no; i++) { BaseStream.Read(buffer, 0, buffer.Length); objectPageTableEntries[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian <ObjectPageTableEntry>(buffer); } } int debugSections = 0; int winrsrcSections = 0; if (Header.debug_info_len > 0) { debugSections = 1; } if (Header.win_res_len > 0) { winrsrcSections = 1; } Segment[] sections = new Segment[objectTableEntries.Length + debugSections + winrsrcSections]; for (int i = 0; i < objectTableEntries.Length; i++) { sections[i] = new Segment { Flags = $"{objectTableEntries[i].ObjectFlags}" }; if (objectTableEntries[i].ObjectFlags.HasFlag(ObjectFlags.Resource)) { sections[i].Name = ".rsrc"; } else if (objectTableEntries[i].ObjectFlags.HasFlag(ObjectFlags.Executable)) { sections[i].Name = ".text"; } else if (!objectTableEntries[i].ObjectFlags.HasFlag(ObjectFlags.Writable)) { sections[i].Name = ".rodata"; } else if (StringHandlers.CToString(objectTableEntries[i].Name).ToLower() == "bss") { sections[i].Name = ".bss"; } else if (!string.IsNullOrWhiteSpace(StringHandlers.CToString(objectTableEntries[i].Name).Trim())) { sections[i].Name = StringHandlers.CToString(objectTableEntries[i].Name).Trim(); } else { sections[i].Name = ".data"; } if (objectTableEntries[i].PageTableEntries == 0 || objectTableEntries[i].PageTableIndex > objectPageTableEntries.Length) { sections[i].Size = objectTableEntries[i].VirtualSize; continue; } int shift = (int)(Header.signature == SIGNATURE16 ? 0 : Header.page_off_shift); if (objectPageTableEntries[objectTableEntries[i].PageTableIndex - 1] .Flags.HasFlag(PageTableAttributes.IteratedDataPage)) { sections[i].Offset = (objectPageTableEntries[objectTableEntries[i].PageTableIndex - 1].PageDataOffset << shift) + Header.obj_iter_pages_off; } else if (objectPageTableEntries[objectTableEntries[i].PageTableIndex - 1] .Flags.HasFlag(PageTableAttributes.LegalPhysicalPage)) { sections[i].Offset = (objectPageTableEntries[objectTableEntries[i].PageTableIndex - 1].PageDataOffset << shift) + Header.data_pages_off; } else { sections[i].Offset = 0; } sections[i].Size = 0; for (int j = 0; j < objectTableEntries[i].PageTableEntries; j++) { sections[i].Size += objectPageTableEntries[j + objectTableEntries[i].PageTableIndex - 1].DataSize; } if (sections[i].Offset + sections[i].Size > BaseStream.Length) { sections[i].Size = BaseStream.Length - sections[i].Offset; } } if (winrsrcSections > 0) { sections[sections.Length - debugSections - winrsrcSections] = new Segment { Name = ".rsrc", Size = Header.win_res_len, Offset = Header.win_res_off } } ; if (debugSections > 0) { sections[sections.Length - debugSections] = new Segment { Name = ".debug", Size = Header.debug_info_len, Offset = Header.debug_info_off } } ; // It only contains a RT_VERSION resource prefixed by some 12-byte header I can't find information about, so let's just skip it. if (winrsrcSections > 0) { buffer = new byte[Header.win_res_len]; BaseStream.Position = Header.win_res_off + 12; BaseStream.Read(buffer, 0, buffer.Length); WinVersion = new NE.Version(buffer); strings.AddRange(from s in WinVersion.StringsByLanguage from k in s.Value select k.Value); } resources = new ResourceTableEntry[Header.resource_entries]; BaseStream.Position = Header.resource_table_off + BaseExecutable.Header.new_offset; buffer = new byte[Marshal.SizeOf(typeof(ResourceTableEntry))]; for (int i = 0; i < resources.Length; i++) { BaseStream.Read(buffer, 0, buffer.Length); resources[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian <ResourceTableEntry>(buffer); } SortedDictionary <ResourceTypes, List <NE.Resource> > os2Resources = new SortedDictionary <ResourceTypes, List <NE.Resource> >(); for (int i = 0; i < resources.Length; i++) { os2Resources.TryGetValue(resources[i].type, out List <NE.Resource> thisResourceType); if (thisResourceType == null) { thisResourceType = new List <NE.Resource>(); } NE.Resource thisResource = new NE.Resource { id = resources[i].id, name = $"{resources[i].id}", flags = 0, dataOffset = (uint)(sections[resources[i].obj_no - 1].Offset + resources[i].offset), length = resources[i].size }; thisResource.data = new byte[thisResource.length]; BaseStream.Position = thisResource.dataOffset; BaseStream.Read(thisResource.data, 0, thisResource.data.Length); thisResourceType.Add(thisResource); os2Resources.Remove(resources[i].type); os2Resources.Add(resources[i].type, thisResourceType); } if (os2Resources.Count > 0) { neFormatResourceTable = new NE.ResourceTable(); int counter = 0; neFormatResourceTable.types = new NE.ResourceType[os2Resources.Count]; foreach (KeyValuePair <ResourceTypes, List <NE.Resource> > kvp in os2Resources) { neFormatResourceTable.types[counter].count = (ushort)kvp.Value.Count; neFormatResourceTable.types[counter].id = (ushort)kvp.Key; neFormatResourceTable.types[counter].name = Resources.IdToName((ushort)kvp.Key); neFormatResourceTable.types[counter].resources = kvp.Value.OrderBy(r => r.id).ToArray(); counter++; } foreach (NE.ResourceType rtype in neFormatResourceTable.types) { if (rtype.name != "RT_STRING") { continue; } strings.AddRange(NE.GetOs2Strings(rtype)); } } Segments = sections; Strings = strings; }