Beispiel #1
0
        /// <summary>
        ///     Identifies if the specified executable is a Microsoft New Executable
        /// </summary>
        /// <returns><c>true</c> if the specified executable is a Microsoft New Executable, <c>false</c> otherwise.</returns>
        /// <param name="stream">Stream containing the executable.</param>
        public static bool Identify(FileStream stream)
        {
            FileStream BaseStream     = stream;
            MZ         BaseExecutable = new MZ(BaseStream);

            if (!BaseExecutable.Recognized)
            {
                return(false);
            }

            if (BaseExecutable.Header.new_offset >= BaseStream.Length)
            {
                return(false);
            }

            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);
            NEHeader Header = (NEHeader)Marshal.PtrToStructure(hdrPtr, typeof(NEHeader));

            Marshal.FreeHGlobal(hdrPtr);
            return(Header.signature == SIGNATURE);
        }
Beispiel #2
0
        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
                });
            }
        }
Beispiel #3
0
        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();
        }