diff --git a/patterns/pe.hexpat b/patterns/pe.hexpat index 47647ad..2c03872 100644 --- a/patterns/pe.hexpat +++ b/patterns/pe.hexpat @@ -1,13 +1,64 @@ -#pragma MIME application/x-msdownload #pragma MIME application/x-dosexec +#pragma MIME application/x-msdownload +#pragma bitfield_order right_to_left +#pragma pattern_limit 0x400000 -#pragma pattern_limit 100000 -#pragma array_limit 10000 - -#include #include -#include -#include +#include + +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; + u16 reservedWords[4]; + u16 oemIdentifier; + u16 oemInformation; + u16 otherReservedWords[10]; + u32 coffHeaderPointer; +}; + +u16 dosMessageOffset; +fn isntdosmessage(char c) { + return c == 0x0D || c == '$'; +}; + +fn finddosmessage() { + for (u8 i = 0, $+i < std::mem::read_unsigned(0x3C, 4), i = i + 1) { + if (std::mem::read_unsigned($+i, 1) == 0xBA) { + dosMessageOffset = std::mem::read_unsigned($+i+1, 2); + break; + } + } +}; + +struct DOSStub { + finddosmessage(); + if (dosMessageOffset > 0) { + u8 code[while($ != addressof(this) + dosMessageOffset)]; + char message[while(!isntdosmessage(std::mem::read_unsigned($, 1)))]; + char data[while(std::mem::read_string($-1, 1) != "$")]; + } else { + u8 code[while(std::mem::read_unsigned($, 6) != 0x00)] @ dosHeader.headerSizeInParagraphs * 16; + } +}; + +struct PEHeader { + DOSHeader dosHeader; + DOSStub dosStub @ dosHeader.headerSizeInParagraphs * 16; +}; + +PEHeader peHeader @ 0x00; enum MachineType : u16 { Unknown = 0x00, @@ -42,6 +93,7 @@ enum MachineType : u16 { }; enum PEFormat : u16 { + ROM = 0x107, PE32 = 0x10B, PE32Plus = 0x20B }; @@ -68,7 +120,7 @@ bitfield Characteristics { executableImage : 1; lineNumbersStripped : 1; localSymbolsStripped : 1; - aggressiveWsTrim : 1; + aggressivelyTrimWorkingSet : 1; largeAddressAware : 1; padding : 1; bytesReversedLo : 1; @@ -80,11 +132,10 @@ bitfield Characteristics { dll : 1; uniprocessorMachineOnly : 1; bytesReversedHi : 1; -} [[right_to_left]]; +}; bitfield DLLCharacteristics { - padding : 4; - padding : 1; + padding : 5; highEntropyVA : 1; dynamicBase : 1; forceIntegrity : 1; @@ -96,36 +147,7 @@ bitfield DLLCharacteristics { wdmDriver : 1; cfGuard : 1; terminalServerAware : 1; -} [[right_to_left]]; - -bitfield SectionFlags { - padding : 3; - doNotPad : 1; - padding : 1; - containsCode : 1; - containsInitializedData : 1; - containsUninitializedData : 1; - linkOther : 1; - comments : 1; - padding : 1; - remove : 1; - comdat : 1; - padding : 2; - globalPointerRelocation : 1; - purgeable : 1; - is16Bit : 1; - locked : 1; - preloaded : 1; - dataAlignment : 4; - extendedRelocations : 1; - discardable : 1; - notCacheable : 1; - notPageable : 1; - shared : 1; - executed : 1; - read : 1; - writtenTo : 1; -} [[right_to_left]]; +}; struct DataDirectory { u32 virtualAddress; @@ -194,49 +216,61 @@ struct COFFHeader { } }; -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; - padding[4 * 2]; - u16 oemIdentifier; - u16 oemInformation; - padding[10 * 2]; - u32 coffHeaderPointer; -}; - -fn isdosmessage(char c) { - return std::ctype::isalnum(c) || c == '.' || c == ' '; -}; - -struct DOSStub { - u8 code[while(std::mem::read_string($, 4) != "This")]; - char message[while(isdosmessage(std::mem::read_unsigned($, 1)))]; - char data[while(std::mem::read_unsigned($, 1) != 0x00)]; -}; - -struct PEHeader { - DOSHeader dosHeader; - if (dosHeader.headerSizeInParagraphs * 16 < std::mem::size()) { - DOSStub dosStub @ dosHeader.headerSizeInParagraphs * 16; - } -}; - -PEHeader peHeader @ 0x00; - COFFHeader coffHeader @ peHeader.dosHeader.coffHeaderPointer; +enum AlignmentType : u8 { + None, + BoundaryOf1Byte, + BoundaryOf2Bytes, + BoundaryOf4Bytes, + BoundaryOf8Bytes, + BoundaryOf16Bytes, + BoundaryOf32Bytes, + BoundaryOf64Bytes, + BoundaryOf128Bytes, + BoundaryOf256Bytes, + BoundaryOf512Bytes, + BoundaryOf1024Bytes, + BoundaryOf2048Bytes, + BoundaryOf4096Bytes, + BoundaryOf8192Bytes, +}; + +fn formatAlignmentBits(u8 value) { + AlignmentType enumValue = value; + + return enumValue; +}; + +bitfield SectionFlags { + padding : 3; + doNotPad : 1; + padding : 1; + containsCode : 1; + containsInitializedData : 1; + containsUninitializedData : 1; + linkOther : 1; + containsComments : 1; + padding : 1; + remove : 1; + containsCOMDATData : 1; + padding : 2; + globalPointerRelocation : 1; + purgeable : 1; + is16Bit : 1; + locked : 1; + preloaded : 1; + dataAlignment : 4 [[format("formatAlignmentBits")]]; + extendedRelocations : 1; + discardable : 1; + notCacheable : 1; + notPageable : 1; + shared : 1; + executed : 1; + read : 1; + writtenTo : 1; +}; + struct SectionHeader { char name[8]; if (coffHeader.characteristics.executableImage) { @@ -254,72 +288,45 @@ struct SectionHeader { SectionFlags characteristics; }; -SectionHeader sectionTable[coffHeader.numberOfSections] @ (addressof(coffHeader.characteristics) + 2) + coffHeader.sizeOfOptionalHeader; +SectionHeader sectionsTable[coffHeader.numberOfSections] @ addressof(coffHeader.characteristics)+2 + coffHeader.sizeOfOptionalHeader; +// Declarations of sections and units used by them u16 currentSectionIndex; fn updateSectionIndex() { currentSectionIndex = currentSectionIndex + 1; }; -struct NullTerminatedString { - char string[while(std::mem::read_unsigned($, 1) != 0x00)]; - padding[1]; -} [[inline]]; - -// Declarations of sections and variables used by it -// Imports + Readonly Data Section -struct DirectoryTable { - u32 lookupTableRVA; - u32 timeDateStamp; - u32 forwarderChain; - u32 dllNameRVA; - u32 addressTableRVA; +fn virtualDataDifference() { + return sectionsTable[currentSectionIndex].virtualAddress - sectionsTable[currentSectionIndex].ptrRawData; }; -struct ImportsSection { - DirectoryTable directoryTables[while(std::mem::read_unsigned($, 16) != 0x00)]; - DirectoryTable nullDirectoryTable; +fn formatNullTerminatedString(str string) { + return "\"" + std::string::substr(string, 0, std::string::length(string)-4) + "\""; }; -struct ReadonlyDataSection { - u8 readonlyDataStart[while($ < coffHeader.optionalHeader.directories[1].virtualAddress - (sectionTable[currentSectionIndex].virtualAddress - sectionTable[currentSectionIndex].ptrRawData))]; - ImportsSection importsSection; - u8 readonlyDataEnd[while($ < sectionTable[currentSectionIndex+1].ptrRawData)]; +// Exception Section +bitfield ExceptionSectionBits { + functionLength : 22; + instructions32Bit : 1; + exceptionHandler : 1; }; -// Resource Section -struct ResourceDirectoryTable { - u32 characteristics; - u32 timeDateStamp; - u16 majorVersion; - u16 minorVersion; - u16 nameEntriesAmount; - u16 idEntriesAmount; -}; - -enum ResourceID : u32 { - Bitmap = 0x02, - Icon = 0x03, - Menu = 0x04, - Dialog = 0x05, - String = 0x06, - GroupIcon = 0x0D, - Version = 0x10, - Manifest = 0x18 -}; - -struct IdDirectoryEntry { - ResourceID id; - u32 relativeOffsetToData; -}; - -struct ResourceDirectory { - ResourceDirectoryTable resourceDirectoryTable; - IdDirectoryEntry idEntries[resourceDirectoryTable.idEntriesAmount]; -}; - -struct ResourceSection { - ResourceDirectory rootDirectory; +struct FunctionTableEntry { + if (coffHeader.machine == MachineType::MIPSFPU) { + u32 beginVA; + u32 endVA; + u32 exceptionHandlerPointer; + u32 handlerDataPointer; + u32 prologEndVA; + } else if (coffHeader.machine == MachineType::ARM || coffHeader.machine == MachineType::ARM64 || coffHeader.machine == MachineType::ARMNT || coffHeader.machine == MachineType::POWERPC || coffHeader.machine == MachineType::POWERPCFP || coffHeader.machine == MachineType::SH3 || coffHeader.machine == MachineType::SH4) { + u32 beginVA; + u8 prologLength; + ExceptionSectionBits miscellaneousBits; + } else if (coffHeader.machine == MachineType::AMD64 || coffHeader.machine == MachineType::IA64) { + u32 beginRVA; + u32 endRVA; + u32 unwindInformationRVA; + } }; // Exports Section @@ -337,8 +344,12 @@ struct ExportDirectoryTable { u32 ordinalTableRVA; }; +struct ExportName { + char name[] [[format("formatNullTerminatedString")]]; +}; + struct ExportAddress { - if (std::mem::read_unsigned($, 4) > sectionTable[currentSectionIndex].sizeOfRawData && std::mem::read_unsigned($, 4) < sectionTable[currentSectionIndex].ptrRawData) { + if (std::mem::read_unsigned($, 4) > sectionsTable[currentSectionIndex].sizeOfRawData && std::mem::read_unsigned($, 4) < sectionsTable[currentSectionIndex].ptrRawData) { u32 exportRVA; } else { @@ -346,39 +357,599 @@ struct ExportAddress { } }; -struct ExportsSection { - ExportDirectoryTable exportDirectoryTable; - ExportAddress exportAddressTable[exportDirectoryTable.addressTableEntries]; - u32 exportNamePointerTable[exportDirectoryTable.numberOfNamePointers]; +struct ExportNamePointer { + u32 exportNameRVA; + ExportName exportName @ exportNameRVA - virtualDataDifference(); }; -// Exception Section -bitfield ExceptionSectionBits { - functionLength : 22; - instructions32Bit : 1; - exceptionHandler : 1; -} [[right_to_left]]; +struct ExportsSection { + ExportDirectoryTable exportDirectoryTable; + ExportAddress exportAddressTable[exportDirectoryTable.addressTableEntries] @ exportDirectoryTable.exportAddressTableRVA - virtualDataDifference(); + ExportNamePointer exportNamePointerTable[exportDirectoryTable.numberOfNamePointers] @ exportDirectoryTable.namePointerRVA - virtualDataDifference(); + u16 exportOrdinalTable[exportDirectoryTable.numberOfNamePointers] @ exportDirectoryTable.ordinalTableRVA - virtualDataDifference(); + char dllName[] @ exportDirectoryTable.nameRVA - virtualDataDifference() [[format("formatNullTerminatedString")]]; +}; -struct FunctionTableEntry { - if (coffHeader.machine == MachineType::MIPSFPU) { - u32 beginVA; - u32 endVA; - u32 exceptionHandlerPointer; - u32 handlerDataPointer; - u32 prologEndVA; - } else if (coffHeader.machine == MachineType::ARM || MachineType::ARM64 || MachineType::ARMNT || MachineType::POWERPC || MachineType::POWERPCFP || MachineType::SH3 || MachineType::SH4) { - u32 beginVA; - u8 prologLength; - ExceptionSectionBits miscellaneousBits; - } else if (coffHeader.machine == MachineType::AMD64 || MachineType::IA64) { - u32 beginRVA; - u32 endRVA; - u32 unwindInformationRVA; +// Imports + Readonly Data Section +fn architecture() { + return std::mem::read_unsigned(addressof(coffHeader.optionalHeader.magic), 2) / 0x41; +}; + +bitfield OrdinalFlagByte { + flag : 1; + padding : 7; +} [[left_to_right]]; + +struct NameTableEntry { + u16 hint; + char name[] [[format("formatNullTerminatedString")]]; + if ($ % 2 == 1) { u8 pad; } +}; + +struct AddressTableEntry { + OrdinalFlagByte ordinalFlagByte @ $+(architecture()-1); + if (ordinalFlagByte.flag) { + u16 ordinalNumber; + u8 nullPadding[architecture()-2]; + } else { + u32 nameTableRVA; + if (coffHeader.optionalHeader.magic == PEFormat::PE32Plus) { + u8 nullPadding[4]; + } } }; -struct ExceptionSection { - FunctionTableEntry functionTableEntries[while($ < sectionTable[currentSectionIndex].ptrRawData + sectionTable[currentSectionIndex].sizeOfRawData)]; +struct LookupTableEntry : AddressTableEntry { + if (!ordinalFlagByte.flag && std::mem::read_unsigned($, architecture()) > 0) { + NameTableEntry name @ nameTableRVA - virtualDataDifference(); + } +}; + +struct DirectoryTable { + u32 lookupTableRVA; + u32 timeDateStamp; + u32 forwarderChain; + u32 dllNameRVA; + u32 addressTableRVA; +} [[static]]; + +u16 importsIndex; + +struct ImportsStructure { + LookupTableEntry lookupTable[while(std::mem::read_unsigned($, architecture()) != 0)] @ parent.directoryTables[importsIndex].lookupTableRVA - virtualDataDifference(); + AddressTableEntry addressTable[while(std::mem::read_unsigned($, architecture()) != 0)] @ parent.directoryTables[importsIndex].addressTableRVA - virtualDataDifference(); + char dllName[] @ parent.directoryTables[importsIndex].dllNameRVA - virtualDataDifference() [[format("formatNullTerminatedString")]]; + importsIndex = importsIndex + 1; +} [[inline]]; + +struct ImportsSection { + DirectoryTable directoryTables[while(std::mem::read_unsigned($, 16) != 0x00)]; + DirectoryTable nullDirectoryTable; + ImportsStructure importsStructures[sizeof(directoryTables) / sizeof(directoryTables[0])]; +}; + +struct ReadonlyDataSection { + if (coffHeader.optionalHeader.numberOfRVAsAndSizes >= 0xC) { + $ += coffHeader.optionalHeader.directories[0xC].size; + } + u8 readonlyDataStart[while($ < coffHeader.optionalHeader.directories[1].virtualAddress - virtualDataDifference())]; + ImportsSection importsSection; +}; + +// Resource Section +using ResourceDirectory; + +struct ResourceString { + u16 length; + char16 string[length]; +}; + +struct NameDirectoryEntry { + if (std::mem::read_unsigned($+3, 1) == 0x80) { + u32 relativeOffsetToName; + ResourceString name @ sectionsTable[currentSectionIndex].ptrRawData + (relativeOffsetToName - 0x80000000); + } + else { + char name[4]; + } + u32 relativeOffsetToData; + ResourceDirectory directory @ sectionsTable[currentSectionIndex].ptrRawData + (relativeOffsetToData - 0x80000000); +}; + +// * Bitmap Resource +struct BitmapHeader { + u32 bitmapSize; + u32 bitmapWidth; + u32 bitmapHeight; + u16 bitmapPlanes; + u16 bitmapBitCount; + u32 bitmapCompression; + u32 bitmapImageSize; + u32 bitmapXPelsPerMeter; + u32 bitmapYPelsPerMeter; + u32 bitmapClrUsed; + u32 bitmapClrImportant; +}; + +struct Colors { + u8 blue [[color("1F77B4")]]; + u8 green [[color("2CA02C")]]; + u8 red [[color("D62728")]]; + u8 reserved [[color("828282")]]; +}; + +struct Bitmap { + BitmapHeader bmh; + + if ((bmh.bitmapBitCount != 24) && (bmh.bitmapBitCount != 32)) { + if (bmh.bitmapClrUsed > 0) { + Colors rgbq[bmh.bitmapClrUsed]; + } + else { + Colors rgbq[1 << bmh.bitmapBitCount]; + } + } +}; + +// * Dialog Resource +struct DialogTemplate { + u32 style; + u32 extendedStyles; + u16 numberOfItems; + u16 x; + u16 y; + u16 width; + u16 height; +}; + +struct DialogItemTemplate { + u32 style; + u32 extendedStyles; + u16 x; + u16 y; + u16 width; + u16 height; + u16 id; +}; + +struct NullTerminatedString16 { + char16 string[while(std::mem::read_unsigned($, 2) != 0)]; + char16 terminatingNull; +} [[inline]]; + +enum AtomType : u16 { + Button = 0x80, + Edit, + Static, + ListBox, + ScrollBar, + ComboBox +}; + +struct VLSElement { + if (std::mem::read_unsigned($, 2) == 0xFFFF) { + u16 atomDefiner; + } + else { + AtomType atom; + } +}; + +struct VariableLengthStructure { + if (std::mem::read_unsigned($, 2) == 0xFFFF) { + VLSElement vlsElement[2]; + } + else { + NullTerminatedString16 string; + } +}; + +struct DialogItem { + u16 alignment; + DialogItemTemplate template; + VariableLengthStructure itemClass; + VariableLengthStructure itemTitle; + u16 itemCreationData; + if (itemCreationData > 0) { + u8 itemData[itemCreationData]; + } +}; + +struct Dialog { + DialogTemplate dialogTemplate; + u16 dialogMenu; + VariableLengthStructure dialogClass; + VariableLengthStructure dialogTitle; + u16 dialogFontSize; + char16 dialogFontTypeface[while(std::mem::read_unsigned($, 2) != 0)]; + u16 terminatingNull; + DialogItem items[while($ < (parent.dataRVA - virtualDataDifference()) + parent.size)]; +}; + +// * String Resource +struct StringTableResource { + ResourceString strings[while(std::mem::read_unsigned($, 2) != 0x00)]; + u16 nullPadding[while($ < (parent.dataRVA - virtualDataDifference()) + parent.size)]; +}; + +// * GroupCursor Resource +struct GroupCursor { + u16 assorteddata; + u16 colors; + u16 otherassorteddata; + u16 pixels; + u16 assorteddataarray[5]; + u16 ordinalName; +}; + +// * GroupIcon Resource +struct GroupIconHeader { + u16 reserved; + u16 type; + u16 count; +}; + +struct GroupIconEntry { + u8 width; + u8 height; + u8 colorCount; + u8 reserved; + u16 planes; + u16 bitCount; + u32 bytesInResource; + u16 id; +}; + +struct GroupIcon { + GroupIconHeader header; + GroupIconEntry entries[header.count]; +}; + +// * Version Resource +struct VersionEntryHeader { + u16 length; + u16 valueLength; + u16 type; + char16 key[while(std::mem::read_unsigned($, 2) != 0)]; + u16 terminatingNull; + u8 nullPadding[while(std::mem::read_unsigned($, 1) == 0 && $ < addressof(terminatingNull)+5)]; +} [[inline]]; + +struct StringInfo { + VersionEntryHeader stringInfoHeader; + if (stringInfoHeader.valueLength > 0) { + NullTerminatedString16 string; + u8 nullPadding[while(std::mem::read_unsigned($, 1) == 0)]; + } +}; + +struct VersionEntry { + VersionEntryHeader header; + if (std::string::starts_with(std::string::to_string(header.key), "VS_VERSION_INFO")) { + u32 signature; + u16 structVersion[2]; + u16 fileVersion[4]; + u16 productVersion[4]; + u32 fileFlagsMask[2]; + u32 fileFlags; + u32 fileOS; + u32 fileType; + u32 fileSubType; + u32 fileTimestamp; + } + else if (std::string::starts_with(std::string::to_string(header.key), "StringFileInfo")) { + VersionEntryHeader stringTable; + StringInfo strings[while($ < addressof(stringTable) + stringTable.length)]; + } + else if (std::string::starts_with(std::string::to_string(header.key), "VarFileInfo")) { + VersionEntryHeader var; + u16 translation[var.valueLength / 2]; + } + else { + u8 value[header.valueLength*2]; + u8 nullPadding[while(std::mem::read_unsigned($, 1) == 0)]; + } +}; + +// * Resources Using TrueChar +fn displayTrueChar(u8 value) { + if (value < 0x10) { + if (value == 0x00) { + return "'␀' (" + std::format("0x0{:X}", value) + ")"; + } + else { + return "'" + char(value) + "' (" + std::format("0x0{:X}", value) + ")"; + } + } + else { + return "'" + char(value) + "' (" + std::format("0x{:X}", value) + ")"; + } +}; + +using TrueChar = u8 [[format("displayTrueChar")]]; + +struct XML { + u8 bom[while(std::mem::read_unsigned($, 1) != '<')]; + TrueChar contents[parent.size - sizeof(bom)] [[inline]]; +}; + +// * The rest of the section +enum ResourceID : u32 { + Cursor = 0x01, + Bitmap, + Icon, + Menu, + Dialog, + String, + StringData = 0x0A, + GroupCursor = 0x0C, + GroupIcon = 0x0E, + Version = 0x10, + Manifest = 0x18 +}; + +ResourceID resourceIDType; + +struct DataEntry { + u32 dataRVA; + u32 size; + u32 codepage; + u32 reserved; + + if (resourceIDType == ResourceID::Bitmap || (resourceIDType == ResourceID::Icon && std::mem::read_string((dataRVA-virtualDataDifference())+1, 3) != "PNG")) { + Bitmap bitmap @ dataRVA - virtualDataDifference(); + } + else if (resourceIDType == ResourceID::Dialog) { + Dialog dialog @ dataRVA - virtualDataDifference(); + } + else if (resourceIDType == ResourceID::String) { + StringTableResource stringTableResource @ dataRVA - virtualDataDifference(); + } + else if (resourceIDType == ResourceID::StringData) { + TrueChar stringData[size] @ dataRVA - virtualDataDifference(); + } + else if (resourceIDType == ResourceID::GroupCursor) { + GroupCursor groupCursor @ dataRVA - virtualDataDifference(); + } + else if (resourceIDType == ResourceID::GroupIcon) { + GroupIcon groupIcon @ dataRVA - virtualDataDifference(); + } + else if (resourceIDType == ResourceID::Version) { + VersionEntry version[while($ < (dataRVA - virtualDataDifference()) + size)] @ dataRVA - virtualDataDifference(); + } + else if (resourceIDType == ResourceID::Manifest) { + XML manifest @ dataRVA - virtualDataDifference(); + } + else { + u8 resource[size] @ dataRVA - virtualDataDifference(); + } +}; + +struct IdDirectoryEntry { + if ($ > (coffHeader.optionalHeader.directories[2].virtualAddress - virtualDataDifference()) + 0x10 + 8*(std::mem::read_unsigned(coffHeader.optionalHeader.directories[2].virtualAddress - virtualDataDifference() + 12, 2) + std::mem::read_unsigned(coffHeader.optionalHeader.directories[2].virtualAddress - virtualDataDifference() + 14, 2))) { + u32 id; + } + else { + ResourceID id; + resourceIDType = id; + } + u32 offsetToData; + + if (offsetToData >= 0x80000000) { + ResourceDirectory directory @ (coffHeader.optionalHeader.directories[2].virtualAddress - virtualDataDifference()) + (offsetToData - 0x80000000); + } + else { + DataEntry dataEntry @ (coffHeader.optionalHeader.directories[2].virtualAddress - virtualDataDifference()) + offsetToData; + } +}; + +struct ResourceDirectoryTable { + u32 characteristics; + u32 timeDateStamp; + u16 majorVersion; + u16 minorVersion; + u16 nameEntriesAmount; + u16 idEntriesAmount; +}; + +struct ResourceDirectory { + ResourceDirectoryTable resourceDirectoryTable; + NameDirectoryEntry nameEntries[resourceDirectoryTable.nameEntriesAmount]; + IdDirectoryEntry idEntries[resourceDirectoryTable.idEntriesAmount]; +}; + +struct ResourceSection { + ResourceDirectory rootDirectory; +}; + +// Relocations Section +enum BaseRelocationType : u8 { + Absolute, + High, + Low, + HighLow, + HighAdjacent, + Reserved = 6, + DIR64 = 10 +}; + +enum MIPSBaseRelocationType : u8 { + Absolute, + High, + Low, + HighLow, + HighAdjacent, + MIPSJMPAddress, + Reserved, + MIPSJMPAddress16 = 9, + DIR64 +}; + +enum ARMBaseRelocationType : u8 { + Absolute, + High, + Low, + HighLow, + HighAdjacent, + ARMMOV32, + Reserved, + DIR64 = 10 +}; + +enum RISCVBaseRelocationType : u8 { + Absolute, + High, + Low, + HighLow, + HighAdjacent, + RISCVHigh20, + Reserved, + RISCVLow12I, + RISCVLow12S, + DIR64 = 10 +}; + +enum THUMBBaseRelocationType : u8 { + Absolute, + High, + Low, + HighLow, + HighAdjacent, + ARMMOV32, + Reserved, + ThumbMOV32, + DIR64 = 10 +}; + +enum LoongarchBaseRelocationType : u8 { + Absolute, + High, + Low, + HighLow, + HighAdjacent, + Reserved = 6, + MarkLA = 8, + DIR64 = 10 +}; + +fn formatMIPSType(u8 value) { + MIPSBaseRelocationType mipsTypeBits = value; + return mipsTypeBits; +}; + +fn formatARMType(u8 value) { + ARMBaseRelocationType armTypeBits = value; + return armTypeBits; +}; + +fn formatRISCVType(u8 value) { + RISCVBaseRelocationType riscvTypeBits = value; + return riscvTypeBits; +}; + +fn formatTHUMBType(u8 value) { + THUMBBaseRelocationType thumbTypeBits = value; + return thumbTypeBits; +}; + +fn formatLoongarchType(u8 value) { + LoongarchBaseRelocationType loongarchTypeBits = value; + return loongarchTypeBits; +}; + +fn formatGenericType(u8 value) { + BaseRelocationType genericTypeBits = value; + return genericTypeBits; +}; + +bitfield MIPSBaseRelocationWord { + type : 4 [[format("formatMIPSType")]]; + offset : 12; +}; + +bitfield ARMBaseRelocationWord { + type : 4 [[format("formatARMType")]]; + offset : 12; +}; + +bitfield RISCVBaseRelocationWord { + type : 4 [[format("formatRISCVType")]]; + offset : 12; +}; + +bitfield THUMBBaseRelocationWord { + type : 4 [[format("formatTHUMBType")]]; + offset : 12; +}; + +bitfield LoongarchBaseRelocationWord { + type : 4 [[format("formatLoongarchType")]]; + offset : 12; +}; + +bitfield GenericBaseRelocationWord { + type : 4 [[format("formatGenericType")]]; + offset : 12; +}; + +struct BaseRelocationBlock { + u32 pageRVA; + u32 blockSize; + if (coffHeader.machine == MachineType::MIPS16 || coffHeader.machine == MachineType::MIPSFPU || coffHeader.machine == MachineType::MIPSFPU16) { + MIPSBaseRelocationWord word; + } + else if (coffHeader.machine == MachineType::ARM || coffHeader.machine == MachineType::ARM64 || coffHeader.machine == MachineType::ARMNT) { + ARMBaseRelocationWord word; + } + else if (coffHeader.machine == MachineType::RISCV32 || coffHeader.machine == MachineType::RISCV64 || coffHeader.machine == MachineType::RISCV128) { + RISCVBaseRelocationWord word; + } + else if (coffHeader.machine == MachineType::THUMB) { + THUMBBaseRelocationWord word; + } + else if (coffHeader.machine == MachineType::LOONGARCH32 || coffHeader.machine == MachineType::LOONGARCH64) { + LoongarchBaseRelocationWord word; + } + else { + GenericBaseRelocationWord word; + } +}; + +struct BaseRelocationTable { + BaseRelocationBlock baseRelocationBlocks[while($ < (coffHeader.optionalHeader.directories[5].virtualAddress - virtualDataDifference()) + coffHeader.optionalHeader.directories[5].size)]; +}; + +// Debug Section +enum DebugType : u32 { + Unknown, + COFF, + Codeview, + FPO, + Misc, + Exception, + Fixup, + OmapToSRC, + OmapFromSRC, + Borland, + Reserved10, + CLSID, + REPRO = 16, + ExtendedDLLCharacteristics = 20 +}; + +struct DebugDirectory { + u32 characteristics; + u32 timeDateStamp; + u32 majorVersion; + u32 minorVersion; + DebugType type; + u32 sizeOfData; + u32 virtualAddressOfRawData; + u32 pointerOfRawData; +}; + +struct DebugSection { + DebugDirectory directory; }; // TLS Section @@ -399,113 +970,213 @@ struct TLSSection { u32 characteristics; }; -// Relocations Section -bitfield RelocationWord { - type : 4; - offset : 12; -}; - -struct BaseRelocationBlock { - u32 pageRVA; - u32 blockSize; - RelocationWord word; -}; - -struct BaseRelocationTable { - BaseRelocationBlock baseRelocationBlocks[while(std::mem::read_unsigned($, 8) != 0x00)]; +// CRT Section +struct CRTSection { + u64 addresses[while($ < sectionsTable[currentSectionIndex].ptrRawData + sectionsTable[currentSectionIndex].sizeOfRawData)]; }; // General Section things -enum I386Relocations : u16 { - Absolute = 0x00, - Dir16 = 0x01, - Rel16 = 0x02, - Dir32 = 0x06 -}; - -struct Relocation { - u32 virtualAddress; - u32 symbolTableIndex; - if (coffHeader.machine == MachineType::I386) { - I386Relocations type; - } - else { - u16 type; - } -}; - struct LineNumber { u32 lineNumber @ $ + 4; if (lineNumber > 0x00) { - u32 virtualAddress @ $ - 8; + u32 virtualAddress; } else { - u32 symbolTableIndex @ $ - 8; + u32 symbolTableIndex; } - $ += 8; + $ += 4; }; -fn importsSectionExists() { - bool returnedValue = false; - std::print("Checking which section is .idata for read-only data section"); - for (u16 i = 0, i < coffHeader.numberOfSections, i = i + 1) { - std::print("Checking section " + std::string::to_string(i)); - if (sectionTable[i].name == ".idata") { - std::print("Check successful. Section " + std::string::to_string(i) + " is .idata, so the read-only data section won't have an imports section in it"); - returnedValue = true; - break; +fn importsPrint(str value) { + std::print("Check for .idata section " + value); +}; + +fn separateImportsSection() { + bool returnedValue; + if (coffHeader.optionalHeader.directories[1].size > 0) { + for (u16 i = 0, i < coffHeader.numberOfSections, i = i + 1) { + if (std::string::starts_with(sectionsTable[i].name, ".idata")) { + importsPrint("successful. It is section " + std::string::to_string(i) + ", so the read-only data section won't have an imports section in it"); + returnedValue = true; + break; + } } } if (!returnedValue) { - std::print("Check failed! This means there is no separate imports section"); + importsPrint("failed! This means there is no separate imports section"); } return returnedValue; }; struct Section { - if (std::string::starts_with(sectionTable[currentSectionIndex].name, ".pdata")) { // Exception section - ExceptionSection exceptionSection; + if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".pdata")) { // Exception section + FunctionTableEntry functionTableEntries[while($ < (coffHeader.optionalHeader.directories[3].virtualAddress - virtualDataDifference()) + coffHeader.optionalHeader.directories[3].size)] [[name("exceptionSection")]]; } - else if (std::string::starts_with(sectionTable[currentSectionIndex].name, ".rdata")) {// Read-only data section - if (importsSectionExists() || coffHeader.optionalHeader.directories[1].size == 0) { - u8 readonlyDataSection[sectionTable[currentSectionIndex].sizeOfRawData]; - } - else { - ReadonlyDataSection readonlyDataSection; - } - } - else if (std::string::starts_with(sectionTable[currentSectionIndex].name, ".idata")) {// Imports section - ImportsSection importsSection; - } - else if (std::string::starts_with(sectionTable[currentSectionIndex].name, ".edata")) {// Exports section + else if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".edata")) { // Exports section ExportsSection exportsSection; } - else if (std::string::starts_with(sectionTable[currentSectionIndex].name, ".rsrc")) { // Resource section + else if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".idata")) { // Imports section + ImportsSection importsSection; + } + else if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".rdata")) { // Read-only data section + if (separateImportsSection()) { + u8 readonlyDataSection[sectionsTable[currentSectionIndex].sizeOfRawData]; + } + else { + ReadonlyDataSection readOnlyDataSection; + } + } + else if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".rsrc")) { // Resource section ResourceSection resourceSection; } - else if (std::string::starts_with(sectionTable[currentSectionIndex].name, ".tls")) { // Thread-local storage section - TLSSection tlsSection; - } - else if (std::string::starts_with(sectionTable[currentSectionIndex].name, ".reloc")) {// Thread-local storage section + else if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".reloc")) { // Base relocations section BaseRelocationTable relocationsSection; } - else { - u8 freeformSection[sectionTable[currentSectionIndex].sizeOfRawData]; // Freeform data section + else if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".debug") || + std::string::starts_with(sectionsTable[currentSectionIndex].name, ".build")) { // Debug section + DebugSection debugSection; } - - // Additional things - //Relocation relocations[sectionTable[currentSectionIndex].numberOfRelocations] @ sectionTable[currentSectionIndex].ptrRelocations; - LineNumber lineNumbers[sectionTable[currentSectionIndex].numberOfLineNumbers] @ sectionTable[currentSectionIndex].ptrLineNumbers; - + else if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".tls")) { // Thread-local storage section + TLSSection tlsSection; + } + else if (std::string::starts_with(sectionsTable[currentSectionIndex].name, ".CRT")) { // CRT section + CRTSection crtSection; + } + else { + u8 freeformSection[sectionsTable[currentSectionIndex].sizeOfRawData]; // Freeform data section + } + + // Other things + LineNumber lineNumbers[sectionsTable[currentSectionIndex].numberOfLineNumbers] @ sectionsTable[currentSectionIndex].ptrLineNumbers; + // Next section - if (currentSectionIndex < coffHeader.numberOfSections-1) { // If it's not the last section (to avoid problems this code would have with it) - updateSectionIndex(); // Make the current section index the next section's index - if (sectionTable[currentSectionIndex].sizeOfRawData > 0) { // If the size of this section is bigger than 0 - $ = sectionTable[currentSectionIndex].ptrRawData; // Put the current offset at the start of the next section + if (currentSectionIndex < coffHeader.numberOfSections-1) { // If it's not the last section (to avoid problems this code would have with it) + updateSectionIndex(); // Make the current section index the next section's index + if (sectionsTable[currentSectionIndex].sizeOfRawData > 0) { // If the next section isn't empty + $ = sectionsTable[currentSectionIndex].ptrRawData; // Put the current offset at the start of it to account for any bytes left } } } [[inline]]; -Section sections[coffHeader.numberOfSections] @ sectionTable[0].ptrRawData; +fn btoi(bool value) { + if (value) { + return 1; + } + else { + return 0; + } +}; + +bool nonemptySections; +u16 firstNonemptySection; +fn findFirstNonemptySection() { + for (u16 i = 0, i < coffHeader.numberOfSections, i = i + 1) { + if (sectionsTable[i].sizeOfRawData > 0) { + nonemptySections = true; + firstNonemptySection = i; + break; + } + } +}; +findFirstNonemptySection(); +Section sections[btoi(nonemptySections) * coffHeader.numberOfSections] @ sectionsTable[firstNonemptySection].ptrRawData; + +// Rich Header +enum ProductType : u16 { + Unmarked = 0x00 ^ richHeaderEnd[0].mask.maskArray[1], + Imports = 0x01 ^ richHeaderEnd[0].mask.maskArray[1], + STDLIBDLL = 0x04 ^ richHeaderEnd[0].mask.maskArray[1], + VC6CVTRes = 0x06 ^ richHeaderEnd[0].mask.maskArray[1], + VC6CCompiler = 0x0A ^ richHeaderEnd[0].mask.maskArray[1], + VC6CPPCompiler = 0x0B ^ richHeaderEnd[0].mask.maskArray[1], + OldNames = 0x0C ^ richHeaderEnd[0].mask.maskArray[1], + MASM613 = 0x0E ^ richHeaderEnd[0].mask.maskArray[1], + VC2003Assembler = 0x0F ^ richHeaderEnd[0].mask.maskArray[1], + VC6SP5 = 0x16 ^ richHeaderEnd[0].mask.maskArray[1], + VC2002Linker = 0x19 ^ richHeaderEnd[0].mask.maskArray[1], + VC2002CCompiler = 0x1C ^ richHeaderEnd[0].mask.maskArray[1], + VC2002CPPCompiler = 0x1D ^ richHeaderEnd[0].mask.maskArray[1], + VC2003SDKIMP = 0x5D ^ richHeaderEnd[0].mask.maskArray[1], + VC2003CPPCompiler = 0x60 ^ richHeaderEnd[0].mask.maskArray[1], + VC2008SDKIMP = 0x93 ^ richHeaderEnd[0].mask.maskArray[1], + Linker12 = 0x9D ^ richHeaderEnd[0].mask.maskArray[1], + MASM10 = 0x9E ^ richHeaderEnd[0].mask.maskArray[1], + VC2010CCompiler = 0xAA ^ richHeaderEnd[0].mask.maskArray[1], + VC2010CPPCompiler = 0xAB ^ richHeaderEnd[0].mask.maskArray[1], + VC2015CVTRES = 0xFF ^ richHeaderEnd[0].mask.maskArray[1], + VC2015Linker = 0x101 ... 0x102 ^ richHeaderEnd[0].mask.maskArray[1], + VC2015Assembler = 0x103 ^ richHeaderEnd[0].mask.maskArray[1], + VC2015CCompiler = 0x104 ^ richHeaderEnd[0].mask.maskArray[1], + VC2015CPPCompiler = 0x105 ^ richHeaderEnd[0].mask.maskArray[1] +}; + +union RichHeaderMask { + u32 maskVariable; + u16 maskArray[2]; +}; + +struct RichHeaderEnd { + char signature[4]; + RichHeaderMask mask; +} [[inline]]; + +bool checkForRichHeader in; +u16 richHeaderEndPosition; +fn initializeRichHeader() { + if (checkForRichHeader) { + for (u16 richEndCursor = sizeof(peHeader), richEndCursor < addressof(coffHeader), richEndCursor = richEndCursor + 1) { + if (std::mem::read_string(richEndCursor, 4) == "Rich") { + richHeaderEndPosition = richEndCursor; + break; + } + } + } +}; +initializeRichHeader(); + +RichHeaderEnd richHeaderEnd[checkForRichHeader] @ richHeaderEndPosition; + +fn formatSignature(auto value) { + return "\"DanS\" "; +}; +fn formatHexadecimally(auto value) { + return std::string::to_string(value) + " (" + std::format("0x{:X}", value) + ")"; +}; +fn unmaskBuild(u16 value) { + return formatHexadecimally(value ^ richHeaderEnd[0].mask.maskArray[0]); +}; +fn unmask(u32 value) { + return formatHexadecimally(value ^ richHeaderEnd[0].mask.maskVariable); +}; + +using NullPadding = u32 [[format("unmask")]]; + +struct Product { + u16 buildNumber [[format("unmaskBuild")]]; + ProductType productID; + u32 objectCount [[format("unmask")]]; +}; + +struct RichHeaderCorpus { + char maskedSignature[4] [[format("formatSignature")]]; + NullPadding nullPadding[3]; + Product products[while($ < richHeaderEndPosition)]; +} [[inline]]; + +u8 richHeaderCorpusPosition; +fn setupRichHeader() { + if (checkForRichHeader) { + //0x20 is the size of a Rich Header with one product + for (u16 richCorpusCursor = richHeaderEndPosition - 0x20, richCorpusCursor > sizeof(peHeader), richCorpusCursor = richCorpusCursor - 0x01) { + if (str(std::mem::read_unsigned(richCorpusCursor, 4) ^ richHeaderEnd[0].mask.maskVariable) == "DanS") { + richHeaderCorpusPosition = richCorpusCursor; + break; + } + } + } +}; +setupRichHeader(); + +RichHeaderCorpus richHeaderCorpus[checkForRichHeader] @ richHeaderCorpusPosition; // Symbol & String Tables enum SectionNumberType : s16 { @@ -575,22 +1246,18 @@ struct SymbolNameAddress { u32 offset; }; -struct SymbolName { - if (std::mem::read_unsigned($, 4) == 0) { - SymbolNameAddress nameAddress; - } - else { - char shortName[8]; - } -}; - struct SymbolType { SymbolTypeMSB msb; SymbolTypeLSB lsb; }; struct Symbol { - SymbolName name; + if (std::mem::read_unsigned($, 4) == 0) { + SymbolNameAddress nameAddress; + } + else { + char shortName[8]; + } u32 value; SectionNumberType sectionNumber; SymbolType type; @@ -598,89 +1265,17 @@ struct Symbol { u8 numberOfAuxSymbols; }; -Symbol symbolTable[coffHeader.numberOfSymbols] @ coffHeader.pointerToSymbolTable; +bool checkForDebugInformation in; + +Symbol symbolTable[btoi(checkForDebugInformation) * coffHeader.numberOfSymbols] @ coffHeader.pointerToSymbolTable; + +struct StringTableString { + char string[] [[format("formatNullTerminatedString")]]; +} [[inline]]; struct StringTable { u32 size; - NullTerminatedString strings[while($ < addressof(this) + size)]; + StringTableString strings[while($ < addressof(this) + size)]; } [[inline]]; -StringTable stringTable[coffHeader.numberOfSymbols > 0] @ addressof(symbolTable) + sizeof(symbolTable); - -// Rich Header -enum ProductType : u16 { - Unmarked = 0x00 ^ richHeaderEnd[0].mask.maskArray[1], - Imports = 0x01 ^ richHeaderEnd[0].mask.maskArray[1], - STDLIBDLL = 0x04 ^ richHeaderEnd[0].mask.maskArray[1], - VC6CVTRes = 0x06 ^ richHeaderEnd[0].mask.maskArray[1], - VC6CCompiler = 0x0A ^ richHeaderEnd[0].mask.maskArray[1], - VC6CPPCompiler = 0x0B ^ richHeaderEnd[0].mask.maskArray[1], - OldNames = 0x0C ^ richHeaderEnd[0].mask.maskArray[1], - MASM613 = 0x0E ^ richHeaderEnd[0].mask.maskArray[1], - VC2003Assembler = 0x0F ^ richHeaderEnd[0].mask.maskArray[1], - VC2002Linker = 0x19 ^ richHeaderEnd[0].mask.maskArray[1], - VC2002CCompiler = 0x1C ^ richHeaderEnd[0].mask.maskArray[1], - VC2002CPPCompiler = 0x1D ^ richHeaderEnd[0].mask.maskArray[1], - VC2003SDKIMP = 0x5D ^ richHeaderEnd[0].mask.maskArray[1], - VC2003CPPCompiler = 0x60 ^ richHeaderEnd[0].mask.maskArray[1], - VC2008SDKIMP = 0x93 ^ richHeaderEnd[0].mask.maskArray[1], - Linker12 = 0x9D ^ richHeaderEnd[0].mask.maskArray[1], - MASM10 = 0x9E ^ richHeaderEnd[0].mask.maskArray[1], - VC2010CCompiler = 0xAA ^ richHeaderEnd[0].mask.maskArray[1], - VC2010CPPCompiler = 0xAB ^ richHeaderEnd[0].mask.maskArray[1] -}; - -union RichHeaderMask { - u32 maskVariable; - u16 maskArray[2]; -}; - -struct RichHeaderEnd { - char signature[4]; - RichHeaderMask mask; -} [[inline]]; - -u8 richHeaderAmount; -u8 richHeaderEndPosition; -u8 richHeaderCorpusPosition; -fn initializeRichHeader() { - $ = sizeof(peHeader); - while ($ < peHeader.dosHeader.coffHeaderPointer) { - if (std::mem::read_string($, 4) == "Rich") { - richHeaderAmount = 1; - richHeaderEndPosition = $; - break; - } - $ += 1; - } -}; -initializeRichHeader(); - -RichHeaderEnd richHeaderEnd[richHeaderAmount] @ richHeaderEndPosition; - -struct Product { - u16 buildNumber; - ProductType productID; - u32 objectCount; -}; - -struct RichHeaderCorpus { - char maskedSignature[4]; - u32 nullPadding[3]; - Product products[while($ != richHeaderEndPosition)]; -} [[inline]]; - -fn setupRichHeader() { - if (richHeaderAmount > 0) { - //0x20 is the size of a Rich Header with one product - for (u8 richCursor = $ - 0x20, richCursor > sizeof(peHeader), richCursor = richCursor - 0x01) { - if (str(std::mem::read_unsigned(richCursor, 4) ^ richHeaderEnd[0].mask.maskVariable) == "DanS") { - richHeaderCorpusPosition = richCursor; - break; - } - } - } -}; -setupRichHeader(); - -RichHeaderCorpus richHeaderCorpus[richHeaderAmount] @ richHeaderCorpusPosition; +StringTable stringTable[checkForDebugInformation] @ addressof(symbolTable) + sizeof(symbolTable);