diff --git a/README.md b/README.md index 216be75..519bdc4 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Hex patterns, include patterns and magic files for the use with the ImHex Hex Ed | BMP | `image/bmp` | [`patterns/bmp.hexpat`](patterns/bmp.hexpat) | OS2/Windows Bitmap files | | ELF | `application/x-executable` | [`patterns/elf.hexpat`](patterns/elf.hexpat) | ELF header in elf binaries | | PE | `application/x-dosexec` | [`patterns/pe.hexpat`](patterns/pe.hexpat) | PE header, COFF header, Standard COFF fields and Windows Specific fields | +| NE | | [`patterns/ne.hexpat`](patterns/ne.hexpat) | NE header and Standard NE fields | | Intel HEX | | [`patterns/intel_hex.hexpat`](patterns/intel_hex.hexpat) | [Intel hexadecimal object file format definition]("https://en.wikipedia.org/wiki/Intel_HEX") | | MIDI | `audio/midi` | [`patterns/midi.hexpat`](patterns/midi.hexpat) | MIDI header, event fields provided | | WAV | `audio/wav` | [`patterns/wav.hexpat`](patterns/wav.hexpat) | RIFF header, WAVE header, PCM header | diff --git a/patterns/ne.hexpat b/patterns/ne.hexpat new file mode 100644 index 0000000..53ce444 --- /dev/null +++ b/patterns/ne.hexpat @@ -0,0 +1,264 @@ +#include + +#pragma bitfield_order right_to_left + +struct DOSHeader { + char signature[2]; + u16 lastPageSize; + u16 numberOfPages; + u16 relocations; + u16 headerSizeInParagraphs; + u16 minimumAllocatedParagraphs; + u16 maximumAllocatedParagraphs; + u16 initialSSValue; + u16 initialRelativeSPValue; + u16 checksum; + u16 initialRelativeIPValue; + u16 initialCSValue; + u16 relocationsTablePointer; + u16 overlayNumber; + u8 overlayInformation[0x20]; + u32 neHeaderPointer; +}; + +u16 dosMessageOffset; +u16 pointedCodeOffset; + +fn finddosmessage() { + for (u8 i = 0, $+i < std::mem::read_unsigned(0x3C, 4), i = i + 1) { + if (std::mem::read_unsigned($+i, 1) == 0xBA) { // MOV instruction + dosMessageOffset = std::mem::read_unsigned($+i+1, 2); + break; + } + } +}; + +fn findpointingcode() { + for (u8 i = 0, $+i < std::mem::read_unsigned(0x3C, 4), i = i + 1) { + if (std::mem::read_unsigned($+i, 1) == 0xE8) { // CALL instruction + pointedCodeOffset = std::mem::read_unsigned($+i+1, 2); + return true; + } + } + return false; +}; + +fn isdosdata(char c) { + return c == 0x0D || c == '$'; +}; + +struct DOSStub { + if (findpointingcode()) { + u8 pointingCode[3]; + u8 code[while(std::mem::read_unsigned($, 1) != 0x00)] @ addressof(this) + pointedCodeOffset + 3; + char message[while(!isdosdata(std::mem::read_unsigned($, 1)))]; + char data[while(std::mem::read_string($-1, 1) != "$")]; + } + else { + finddosmessage(); + if (dosMessageOffset > 0) { + u8 code[while($ != addressof(this) + dosMessageOffset)]; + char message[while(!isdosdata(std::mem::read_unsigned($, 1)))]; + char data[while(std::mem::read_string($-1, 1) != "$")]; + } + else { + char code[while(std::mem::read_unsigned($, 1) != 0x00)]; + } + } +}; + +struct FileHeader { + DOSHeader dosHeader; + DOSStub dosStub @ dosHeader.headerSizeInParagraphs * 16; +}; + +FileHeader fileHeader @ 0x00; + +enum DGroupType : u8 { + NoAutoData, + SingleData, + MultipleData, + Null +}; + +fn formatDGroupType(u8 value) { + DGroupType dgroup = value; + + return dgroup; +}; + +enum AppType : u8 { + None, + Fullscreen, + WinPMCompatible, + UsesWinPM +}; + +fn formatAppType(u8 value) { + AppType app = value; + + return app; +}; + +bitfield ProgramFlags { + dGroupType : 2 [[format("formatDGroupType")]]; + globalInitialization : 1; + protectedModeOnly : 1; + instructions86 : 1; + instructions286 : 1; + instructions386 : 1; + instructionsX87 : 1; +}; + +bitfield ApplicationFlags { + applicationType : 2 [[format("formatAppType")]]; + padding : 1; + os2Application : 1; + reserved : 1; + imageError : 1; + nonConforming : 1; + dll : 1; +}; + +enum OSType : u8 { + Unknown = 0x00, + OS2 = 0x01, + Win16 = 0x02, + DOS4 = 0x03, + Win32 = 0x04, + BorlandOSServices = 0x05, + PharlapDOSExtenderOS2 = 0x81, + PharlapDOSExtenderWindows = 0x82 +}; + +bitfield OS2EXEFlags { + longFilename : 1; + protectedMode : 1; + proportionalFonts : 1; + gangloadArea : 1; +}; + +struct NEHeader { + char signature[2]; + u8 majorLinkerVersion; + u8 minorLinkerVersion; + u16 entryTableOffset; + u16 entryTableLength; + u32 fileCRC; + ProgramFlags programFlags; + ApplicationFlags appFlags; + u16 autoDataSegmentIndex; + u16 initHeapSize; + u16 initStackSize; + u32 entryPoint; + u32 initialStackPointer; + u16 segmentCount; + u16 moduleReferenceCount; + u16 nonResidentNamesTableSize; + u16 segmentTableOffset; + u16 resourceTableOffset; + u16 residentNamesTableOffset; + u16 moduleReferenceTableOffset; + u16 importedNamesTableOffset; + u8 *nonResidentNamesTablePointer[nonResidentNamesTableSize] : u32; + u16 moveableEntryCount; + u16 fileAlignmentSizeShiftCount; + u16 resourceCount; + OSType targetOS; + OS2EXEFlags os2ExeFlags; + u16 thunksReturnOffset; + u16 segmentReferenceThunksOffset; + u16 minimumCodeSwapAreaSize; + u8 expectedMinorWindowsVersion; + u8 expectedMajorWindowsVersion; +}; + +NEHeader neHeader @ fileHeader.dosHeader.neHeaderPointer; + +bitfield SegmentTableFlags { + dataSegment : 1; + typeMask : 2; + padding : 1; + moveable : 1; + padding : 1; + preloaded : 1; + padding : 1; + containsRelocationInfo : 1; + padding : 1; + discardPriority : 4; +} [[right_to_left]]; + +struct SegmentTable { + u16 segmentDataPointer; + u16 segmentLength; + SegmentTableFlags segmentTableFlags; + u16 minimumAllocationSize; +}; + +SegmentTable segmentTable[neHeader.segmentCount] @ addressof(neHeader) + neHeader.segmentTableOffset; + +bitfield BlockFlags { + padding : 4; + moveable : 1; + shared : 1; + preload : 1; + padding : 9; +}; + +struct ResourceTypeInformationBlock { + u8 *resourcePointer : u16; + u16 resourceLength; + BlockFlags flags; + u16 resourceID; + u32 reserved; +}; + +struct ResourceRecord { + u16 typeID; + u16 numberOfResources; + u32 reserved; + ResourceTypeInformationBlock blocks[numberOfResources]; +}; + +struct ResourceTable { + u16 alignmentShiftCount; + ResourceRecord records[neHeader.resourceTableEntryCount]; + u8 stringLength; + char string[stringLength]; +}; + +ResourceTable resourceTable[neHeader.resourceCount] @ addressof(neHeader) + neHeader.resourceTableOffset; + +struct ResidentName { + u8 stringLength; + char string[stringLength]; + u16 ordinalNumber; +}; + +ResidentName residentNameTable[while($+1 < addressof(neHeader) + neHeader.moduleReferenceTableOffset)] @ addressof(neHeader) + neHeader.residentNamesTableOffset; + +struct ModuleReference { + u16 moduleNameOffset; +}; + +ModuleReference moduleReferenceTable[neHeader.moduleReferenceCount] @ addressof(neHeader) + neHeader.moduleReferenceTableOffset; + +struct ImportedNameTable { + u8 stringLength; + char string[stringLength]; +}; + +ImportedNameTable importedNameTable[while($ < addressof(neHeader) + neHeader.entryTableOffset)] @ addressof(neHeader) + neHeader.importedNamesTableOffset; + +enum EntryDataType : u8 { + Unused, + Fixed = 0x01 ... 0xFE, + Moveable +}; + +struct EntryTable { + u8 entryCount; + EntryDataType segmentIndicator; +}; + +EntryTable entryTable[neHeader.entryTableLength/2] @ addressof(neHeader) + neHeader.entryTableOffset; diff --git a/tests/patterns/test_data/ne.hexpat.exe b/tests/patterns/test_data/ne.hexpat.exe new file mode 100644 index 0000000..d3b7d34 Binary files /dev/null and b/tests/patterns/test_data/ne.hexpat.exe differ