diff --git a/README.md b/README.md index b52e591..c7d4c8d 100644 --- a/README.md +++ b/README.md @@ -67,13 +67,14 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | DTED | | [`patterns/dted.hexpat`](patterns/dted.hexpat) | Digital Terrain Elevation Data (DTED) | | ELF | `application/x-executable` | [`patterns/elf.hexpat`](patterns/elf.hexpat) | ELF header in elf binaries | | EVTX | `application/x-ms-evtx` | [`patterns/evtx.hexpat`](patterns/evtx.hexpat) | MS Windows Vista Event Log | -| EXFAT | | [`patterns/exfat.hexpat`](patterns/exfat.hexpat) | Extensible File Allocation Table (exFAT) | -| EXT4 | | [`patterns/ext4.hexpat`](patterns/ext4.hexpat) | Ext4 filesystem | +| EXFAT | | [`patterns/fs/exfat.hexpat`](patterns/fs/exfat.hexpat) | Extensible File Allocation Table (exFAT) | +| EXT4 | | [`patterns/fs/ext4.hexpat`](patterns/fs/ext4.hexpat) | Ext4 File System | | FAS | | [`patterns/fas_oskasoftware.hexpat`](patterns/fas_oskasoftware.hexpat) [`patterns/fas_oskasoftware_old.hexpat`](patterns/fas_oskasoftware_old.hexpat) (Old versions of Oska DeskMate) | Oska Software DeskMates FAS (Frames and Sequences) file | +| FAT32 | | [`patterns/fs/fat32.hexpat`](patterns/fs/fat32.hexpat) | FAT32 File System | | FBX | | [`patterns/fbx.hexpat`](patterns/fbx.hexpat) | Kaydara FBX Binary | | FDT | | [`patterns/fdt.hexpat`](patterns/fdt.hexpat) | Flat Linux Device Tree blob | | FFX | | [`patterns/ffx/*`](https://gitlab.com/EvelynTSMG/imhex-ffx-pats) | Various Final Fantasy X files | -| File System | `application/x-ima` | [`patterns/fs.hexpat`](patterns/fs.hexpat) | Drive File System | +| File System | `application/x-ima` | [`patterns/fs/pattern.hexpat`](patterns/fs/pattern.hexpat) | Drive File System | | FLAC | `audio/flac` | [`patterns/flac.hexpat`](patterns/flac.hexpat) | Free Lossless Audio Codec, FLAC Audio Format | | FLC/FLIC | | [`patterns/flc.hexpat`](patterns/flc.hexpat) | FLC/FLIC animation file | | Flipper Zero Settings | | [`patterns/flipper_settings.hexpat`](patterns/flipper_settings.hexpat) | Flipper Zero Settings Files | @@ -105,7 +106,6 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | LCE Savefile | | [`patterns/lcesave.hexpat`](patterns/lcesave.hexpat) | Minecraft Legacy Console Edition save file | | LZNT1 | | [`patterns/lznt1.hexpat`](patterns/lznt1.hexpat) | LZNT1 compressed data format | | Mach-O | `application/x-mach-binary` | [`patterns/macho.hexpat`](patterns/macho.hexpat) | Mach-O executable | -| MBR & GPT | | [`patterns/partition_table.hexpat`](patterns/partition_table.hexpat) | Partition tables with primary focus on GPT | | MIDI | `audio/midi` | [`patterns/midi.hexpat`](patterns/midi.hexpat) | MIDI header, event fields provided | | MiniDump | `application/x-dmp` | [`patterns/minidump.hexpat`](patterns/minidump.hexpat) | Windows MiniDump files | | MO | | [`patterns/mo.hexpat`](patterns/mo.hexpat) | GNU Machine Object (MO) files containing translations for gettext | @@ -121,7 +121,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | NotepadWindowState | | [`patterns/notepadwindowstate.hexpat`](patterns/notepadwindowstate.hexpat) | Windows 11 Notepad - Window State .bin file | | NRO | | [`patterns/nro.hexpat`](patterns/nro.hexpat) | Nintendo Switch NRO files | | NTAG | | [`patterns/ntag.hexpat`](patterns/ntag.hexpat) | NTAG213/NTAG215/NTAG216, NFC Forum Type 2 Tag compliant IC | -| NTFS | | [`patterns/ntfs.hexpat`](patterns/ntfs.hexpat) | NTFS (NT File System) | +| NTFS | | [`patterns/fs/ntfs.hexpat`](patterns/fs/ntfs.hexpat) | NTFS (NT File System) | | OGG | `audio/ogg` | [`patterns/ogg.hexpat`](patterns/ogg.hexpat) | OGG Audio format | | ORP / ORS | | [`patterns/orp.hexpat`](patterns/orp.hexpat) | OpenRGB profile format | | PAK | | [`patterns/xgspak.hexpat`](patterns/xgspak.hexpat) | Exient XGS Engine Pak files | @@ -148,7 +148,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | RAR | `application/x-rar` | [`patterns/rar.hexpat`](patterns/rar.hexpat) | RAR archive file format | | RAS | `image/x-sun-raster` | [`patterns/ras.hexpat`](patterns/ras.hexpat) | RAS image files | | RCF 1.2 | | [`patterns/rcf_v1_2.hexpat`](patterns/rcf_v1_2.hexpat) | Radcore Cement Library 1.2 file header | -| ReFS | | [`patterns/refs.hexpat`](patterns/refs.hexpat) | Microsoft Resilient File System | +| ReFS | | [`patterns/refs.hexpat`](patterns/fs/refs.hexpat) | Microsoft Resilient File System | | RGBDS | | [`patterns/rgbds.hexpat`](patterns/rgbds.hexpat) | [RGBDS](https://rgbds.gbdev.io) object file format | | RPM | | [`patterns/rpm.hexpat`](patterns/rpm.hexpat) | [RPM](http://ftp.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html) package file format | | Shell Link | `application/x-ms-shortcut` | [`patterns/lnk.hexpat`](patterns/lnk.hexpat) | Windows Shell Link file format | diff --git a/patterns/exfat.hexpat b/patterns/fs/exfat.hexpat similarity index 100% rename from patterns/exfat.hexpat rename to patterns/fs/exfat.hexpat diff --git a/patterns/ext4.hexpat b/patterns/fs/ext4.hexpat similarity index 100% rename from patterns/ext4.hexpat rename to patterns/fs/ext4.hexpat diff --git a/patterns/fs/fat32.hexpat b/patterns/fs/fat32.hexpat new file mode 100644 index 0000000..f39e0cd --- /dev/null +++ b/patterns/fs/fat32.hexpat @@ -0,0 +1,108 @@ +import std.core; + +u64 bytesPerCluster; + +struct FSInfo { + u32 leadSignature; + padding[480]; + u32 structSignature; + u32 freeClusterCount; + u32 nextFreeCluster; + padding[12]; + u32 trailSignature; +}; + +bitfield SequenceNumber { + padding : 1; + lastLogical : 1; + padding : 1; + number : 5; +} [[bitfield_order(std::core::BitfieldOrder::MostToLeastSignificant, 8)]]; + +enum EntryStatus : u8 { + Regular = 0x00, + DotEntry = 0x2E, + DeletedEntry = 0xE5 +}; + +union EntryStatusOrSequenceNumber { + EntryStatus entryStatus; + SequenceNumber sequenceNumber; +}; + +bitfield Attributes { + readOnly : 1; + hidden : 1; + systemFile : 1; + volumeLabel : 1; + subdirectory : 1; + archive : 1; + padding : 2; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]]; + +struct DirEntry { + char fileName[8]; + char extension[3]; + Attributes attributes; + u8 reserved[10]; + u16 time, date; + u16 startingCluster; + u32 fileSize; + + u8 data[fileSize] @ startingCluster * bytesPerCluster; +}; + +struct VFATDirEntry { + EntryStatusOrSequenceNumber entryStatusOrSequenceNumber; + char16 name1[5]; + Attributes attributes; + u8 type; + u8 nameChecksum; + char16 name2[6]; + u16 startingCluster; + char16 name3[2]; + + if (entryStatusOrSequenceNumber.sequenceNumber.number > 1) + VFATDirEntry nextLogicalEntry; + else + DirEntry physicalEntry; +}; + +struct FAT32 { + u8 jmpCode[3]; + char oemName[8]; + u16 bytesPerSector; + u8 sectorsPerCluster; + u16 reservedAreaSize; + u8 numFats; + u16 rootEntryCount; + u16 numSectors; + u8 mediaType; + u16 fatSize; + u16 sectorsPerTrack; + u16 numHeads; + u32 numHiddenSectors; + u32 numFsSectors; + u32 numFatSectors; + u16 extFlags; + u16 fsVersion; + u32 rootCluster; + u16 fsInfoSector; + u16 backupBootSector; + padding[12]; + u8 driveNumber; + padding[1]; + u8 bootSignature; + u32 volumeID; + char volumeLabel[11]; + char fsType[8]; + u8 bootstrapCode[420]; + u16 signature; + + bytesPerCluster = (sectorsPerCluster * 1024) * bytesPerSector; + + FSInfo fsInfo @ addressof(this) + fsInfoSector * bytesPerSector; + VFATDirEntry rootDirEntry @ addressof(this) + rootCluster * bytesPerCluster; +}; + +FAT32 fat32 @ 0x00; \ No newline at end of file diff --git a/patterns/ntfs.hexpat b/patterns/fs/ntfs.hexpat similarity index 97% rename from patterns/ntfs.hexpat rename to patterns/fs/ntfs.hexpat index cabb56f..4ce56df 100644 --- a/patterns/ntfs.hexpat +++ b/patterns/fs/ntfs.hexpat @@ -25,7 +25,7 @@ struct GUID { u16 Data2; u16 Data3; u8 Data4[8]; -}; +} [[static]]; bitfield RUNLIST_HEADER { unsigned offset : 4; @@ -36,6 +36,9 @@ struct RUNLIST { RUNLIST_HEADER header; u8 count[header.length]; u8 lcn[header.offset]; + + if (header.length == 0) + break; }; struct BIOS_PARAMETER_BLOCK { @@ -101,13 +104,13 @@ enum ATTR_TYPES : u32 { }; enum ATTR_DEF_FLAGS : u32 { - ATTR_DEF_INDEXABLE = 0x02, - ATTR_DEF_MULTIPLE = 0x04, - ATTR_DEF_NOT_ZERO = 0x08, - ATTR_DEF_INDEXED_UNIQUE = 0x10, - ATTR_DEF_NAMED_UNIQUE = 0x20, - ATTR_DEF_RESIDENT = 0x40, - ATTR_DEF_ALWAYS_LOG = 0x80, + ATTR_DEF_INDEXABLE = 0x02, + ATTR_DEF_MULTIPLE = 0x04, + ATTR_DEF_NOT_ZERO = 0x08, + ATTR_DEF_INDEXED_UNIQUE = 0x10, + ATTR_DEF_NAMED_UNIQUE = 0x20, + ATTR_DEF_RESIDENT = 0x40, + ATTR_DEF_ALWAYS_LOG = 0x80, }; enum COLLATION_RULES : u32 { @@ -176,12 +179,12 @@ struct STANDARD_INFORMATION_HEADER { u64 last_mft_change_time; u64 last_access_time; FILE_ATTR_FLAGS file_attributes; -}; +} [[static]]; struct STANDARD_INFORMATION_OLD { STANDARD_INFORMATION_HEADER header [[inline]]; u8 reserved12[12]; -}; +} [[static]]; struct STANDARD_INFORMATION { STANDARD_INFORMATION_HEADER header [[inline]]; @@ -192,17 +195,17 @@ struct STANDARD_INFORMATION { u32 security_id; u64 quota_charged; u64 usn; -}; +} [[static]]; struct FILE_NAME_ATTR_PACKED { u16 packed_ea_size; u16 reserved; -}; +} [[static]]; union FILE_NAME_ATTR_FORM { FILE_NAME_ATTR_PACKED packed [[inline]]; u32 reparse_point_tag; -}; +} [[static]]; struct FILE_NAME_ATTR { leMFT_REF parent_directory; @@ -252,7 +255,7 @@ struct VOLUME_INFORMATION { u8 major_ver; u8 minor_ver; VOLUME_FLAGS flags; -}; +} [[static]]; enum SECURITY_DESCRIPTOR_CONTROL : u16 { SE_OWNER_DEFAULTED = 0x0001, @@ -671,8 +674,8 @@ struct RESTART_PAGE_HEADER { }; enum RESTART_AREA_FLAGS : u16 { - RESTART_VOLUME_IS_CLEAN = 0x0002, - RESTART_SPACE_FILLER = 0xffff, + RESTART_VOLUME_IS_CLEAN = 0x0002, + RESTART_SPACE_FILLER = 0xffff, }; struct RESTART_AREA { @@ -768,7 +771,7 @@ struct ATTR_RECORD { } } - RUNLIST runlist[]; + RUNLIST runlist[while(true)]; } else { u32 value_offset_delta = 0; if (value_offset > (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes)) { @@ -988,7 +991,7 @@ MFT_RECORD mft_sec @ nbs.mft_lcn * cluster_size + 9 * mft_rec_size [[name("$Secu // ============= $UpCase ============= MFT_RECORD mft_uc @ nbs.mft_lcn * cluster_size + 10 * mft_rec_size [[name("$UpCase")]]; -u8 uc_tbl[1] @ mft_get_dat_attr_lcn(mft_uc) * cluster_size [[name("$UpCase, tabl")]]; +u8 uc_tbl[1] @ mft_get_dat_attr_lcn(mft_uc) * cluster_size [[name("$UpCase, tabl")]]; // ============= $Extend ============= MFT_RECORD mft_ext @ nbs.mft_lcn * cluster_size + 11 * mft_rec_size [[name("$Extend")]]; \ No newline at end of file diff --git a/patterns/fs/pattern.hexpat b/patterns/fs/pattern.hexpat new file mode 100644 index 0000000..cdeedbb --- /dev/null +++ b/patterns/fs/pattern.hexpat @@ -0,0 +1,231 @@ +#pragma author WerWolv +#pragma description Drive File System +#pragma MIME application/x-ima + +import std.io; +import std.core; +import type.magic; +import type.guid; +import type.base; + +import * from fs.fat32 as FAT32Partition; +import * from fs.exfat as ExFATPartition; +import * from fs.ntfs as NTFSPartition; + +const u32 SectorSize = 512; + +struct DiskTimeStamp { + u8 seconds, minutes, hours; +}; + +enum DiskProtection : u16 { + None = 0x0000, + CopyProtected = 0x5A5A +}; + +bitfield CHS { + h : 8; + s : 6; + c : 10; +} [[format("chs_formatter")]]; + +fn chs_formatter(CHS chs) { + return std::format("({:X}, {:X}, {:X}) | 0x{:X}", chs.c, chs.h, chs.s, (chs.c * 16 + chs.h) * 63 + (chs.s - 1)); +}; + +enum PartitionStatus : u8 { + None = 0x00, + Active = 0x80 +}; + +enum MBRPartitionType : u8 { + Empty = 0x00, + FAT12 = 0x01, + XENIXRoot = 0x02, + XENIXUsr = 0x03, + FAT16_16_32MB = 0x04, + ExtendedCHS = 0x05, + FAT16_32MBPlus = 0x06, + NTFS = 0x07, + AIX = 0x08, + AIXBootable = 0x09, + OS2BootManager = 0x0A, + FAT32_CHS = 0x0B, + FAT32_LBA = 0x0C, + FAT16_LBA = 0x0E, + ExtendedLBA = 0x0F, + OPUS = 0x10, + HiddenFAT12 = 0x11, + CompaqDiagnostics = 0x12, + HiddenFAT16_16_32MB = 0x14, + HiddenFAT16_32MBPlus = 0x16, + HiddenNTFS = 0x17, + ASTSmartSleep = 0x18, + HiddenFAT32_CHS = 0x1B, + HiddenFAT32_LBA = 0x1C, + HiddenFAT16_LBA = 0x1E, + NEC_DOS = 0x24, + WindowsRecovery = 0x27, + Plan9 = 0x39, + PowerQuest = 0x3C, + Venix286 = 0x40, + PPC_PReP_Boot = 0x41, + SFS = 0x42, + QNX4_x = 0x4D, + QNX4_x_2ndPart = 0x4E, + QNX4_x_3rdPart = 0x4F, + OnTrackDM = 0x50, + OnTrackDM6Aux1 = 0x51, + CP_M = 0x52, + OnTrackDM6Aux3 = 0x53, + OnTrackDM6 = 0x54, + EZDrive = 0x55, + GoldenBow = 0x56, + PriamEDisk = 0x5C, + SpeedStor = 0x61, + GNU_HURD = 0x63, + NovellNetware286 = 0x64, + NovellNetware386 = 0x65, + DiskSecureMultiBoot = 0x70, + PC_IX = 0x75, + XOSL = 0x78, + OldMinix = 0x80, + LinuxMinix = 0x81, + LinuxSwap = 0x82, + Linux = 0x83, + OS2HiddenCDrive = 0x84, + LinuxExtended = 0x85, + NTFSVolumeSet = 0x86, + NTFSVolumeSet2 = 0x87, + LinuxLVM = 0x8E, + Amoeba = 0x93, + AmoebaBBT = 0x94, + BSD_OS = 0x9F, + IBMThinkpadHibernation = 0xA0, + FreeBSD = 0xA5, + OpenBSD = 0xA6, + NeXTSTEP = 0xA7, + MacOSX = 0xA8, + NetBSD = 0xA9, + BSDIFS = 0xB7, + BSDISwap = 0xB8, + BootWizardHidden = 0xBB, + DRDOSFAT12 = 0xC1, + DRDOSFAT16 = 0xC4, + DRDOSFAT16B = 0xC6, + Syrinx = 0xC7, + NonFSData = 0xDA, + CP_M_CTOS = 0xDB, + DellUtility = 0xDE, + BootIt = 0xDF, + DOSAccess = 0xE1, + DOSRO = 0xE3, + SpeedStor2 = 0xE4, + BeOS = 0xEB, + GPTProtective = 0xEE, + EFI_System = 0xEF, + LinuxPA_RISC = 0xF0, + SpeedStor3 = 0xF1, + DOSSecondary = 0xF2, + LinuxRAID = 0xFD, + LANstep = 0xFE, + Unknown = 0xFF +}; + +enum GPTPartitionType : u128 { + UnusedEntry = u128("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), + MicrosoftReservedPartition = u128("\x16\xE3\xC9\xE3\x5C\x0B\xB8\x4D\x81\x7D\xF9\x2D\xF0\x02\x15\xAE"), + PartitionBasicData = u128("\xA2\xA0\xD0\xEB\xE5\xB9\x33\x44\x87\xC0\x68\xB6\xB7\x26\x99\xC7") +}; + +bitfield GPTAttributes { + bool platform_required : 1; + padding : 59; + bool read_only : 1; + bool shadow_copy : 1; + bool hidden : 1; + bool no_drive_letter : 1; +}; + +struct GPTEntry { + GPTPartitionType partitionType [[no_unique_address]]; + type::GUID partitionTypeGuid; + type::GUID partitionGuid; + u64 firstLba; + u64 lastLba; + GPTAttributes attribute; + char16 partitionName[36]; + padding[parent.entrySize - 128]; + + u64 partitionOffset = firstLba * SectorSize [[export]]; + match (partitionType) { + (GPTPartitionType::UnusedEntry): {} + (GPTPartitionType::MicrosoftReservedPartition): + std::mem::Bytes<(lastLba - firstLba + 1) * SectorSize> microsoftReservedPartition @ partitionOffset; + (GPTPartitionType::PartitionBasicData): {} + (_): std::error(std::format("Unknown GPT Partition Type {}", partitionType)); + } +}; + +struct GUIDPartitionTable { + type::Magic<"EFI PART"> signature; + u16 versionMinor; + u16 versionMajor; + u32 headerSize; + type::Hex headerCrc32; + u32 reserved; + u64 currentLba; + u64 backupLba; + u64 firstUsableLba; + u64 lastUsableLba; + type::GUID diskGuid; + u64 entryLba; + u32 entryCount; + u32 entrySize; + type::Hex entryCrc32; + + GPTEntry entries[entryCount] @ addressof(this) + (entryLba - currentLba) * SectorSize; +}; + +struct PartitionEntry { + PartitionStatus status; + CHS chsFirstSectorAddress; + MBRPartitionType type; + CHS chsLastSectorAddress; + u32 lbaFirstSectorAddress; + u32 numSectors; + + u64 partitionOffset = lbaFirstSectorAddress * SectorSize [[export]]; + match (type) { + (MBRPartitionType::Empty): continue; + (MBRPartitionType::FAT32_LBA | MBRPartitionType::FAT32_CHS): + FAT32Partition fat32Partition @ partitionOffset; + (MBRPartitionType::NTFS): { + char magic[8] @ partitionOffset + 3; + if (magic == "NTFS ") + NTFSPartition ntfsPartition @ partitionOffset; + else if (magic == "EXFAT ") + ExFATPartition exfatPartiton @ partitionOffset; + else + std::error("Unknown MBR partition with NTFS Type ID"); + } + (MBRPartitionType::GPTProtective): + GUIDPartitionTable gpt @ partitionOffset; + + (_): std::error(std::format("Unknown MBR Partition Type {}", type)); + } +}; + +struct MasterBootRecord { + u8 bootstrapCodeArea1[218]; + padding[2]; + u8 originalPhysicalDrive; + DiskTimeStamp diskTimeStamp; + u8 bootstrapCodeArea2[216]; + u32 diskSignature; + DiskProtection diskProtection; + PartitionEntry partitionEntries[4]; + u16 bootSignature; +}; + +MasterBootRecord mbr @ 0x00; diff --git a/patterns/refs.hexpat b/patterns/fs/refs.hexpat similarity index 100% rename from patterns/refs.hexpat rename to patterns/fs/refs.hexpat diff --git a/patterns/partition_table.hexpat b/patterns/partition_table.hexpat deleted file mode 100644 index 1596678..0000000 --- a/patterns/partition_table.hexpat +++ /dev/null @@ -1,75 +0,0 @@ -#pragma author RadxaYuntian -#pragma description Master Boot Record & GUID Partition Table -#pragma endian little - -import type.base; -import type.guid; - -#ifndef SECTOR_SIZE -#define SECTOR_SIZE 512 -#endif - -bitfield CHSAddress { - u8 head; - sector : 6; - cylinder: 10; -}; - -struct MBREntry { - u8 status; - CHSAddress first_chs; - u8 type; - CHSAddress last_chs; - u32 first_lba; - u32 sector_count; -}; - -struct MasterBootRecord { - padding[446]; - MBREntry entries[4]; - type::Hex boot_signature; - padding[SECTOR_SIZE - 512]; -}; - -bitfield GPTAttributes { - bool platform_required : 1; - padding : 59; - bool read_only : 1; - bool shadow_copy : 1; - bool hidden : 1; - bool no_drive_letter : 1; -}; - -struct GPTEntry { - type::GUID partition_type; - type::GUID partition_guid; - u64 first_lba; - u64 last_lba; - GPTAttributes attribute; - char16 partition_name[36]; - padding[parent.entry_size - 128]; -}; - -struct GUIDPartitionTable { - char signature[8]; // "EFI PART" - u16 version_minor; - u16 version_major; - u32 header_size; - type::Hex header_crc32; // from 0x0 to 0x5b - u32 reserved; // must be 0 - u64 current_lba; - u64 backup_lba; - u64 first_usable_lba; - u64 last_usable_lba; - type::GUID disk_guid; - u64 entry_lba; - u32 entry_count; - u32 entry_size; - type::Hex entry_crc32; - padding[SECTOR_SIZE - 92]; // end of first LBA - padding[SECTOR_SIZE * (entry_lba - current_lba - 1)]; // non-GPT data - GPTEntry entries[entry_count]; -}; - -MasterBootRecord mbr @ 0 * SECTOR_SIZE; -GUIDPartitionTable gpt @ 1 * SECTOR_SIZE; diff --git a/tests/patterns/test_data/partition_table.hexpat.bin b/tests/patterns/test_data/partition_table.hexpat.bin deleted file mode 100644 index c75fa79..0000000 Binary files a/tests/patterns/test_data/partition_table.hexpat.bin and /dev/null differ