//------------------------------------------------ //--- 010 Editor v7.0 Binary Template // // File: Drive.bt // Authors: SweetScape Software, Benjamin Vernoux // Version: 1.0 // Purpose: Parse logical and physical drives including // MBR, FAT16, FAT32, NTFS and extended partitions. // Can display subdirectories and data for individual // files. Note that some NTFS drives // may not work properly (see file comments). // Category: Drives // File Mask: Drive*,Physical* // ID Bytes: [+510] 55 AA // History: // 1.0 2016-05-13 SweetScape Software: Added NTFS support, // parsing FAT files and subdirectories, // extended partitions. Created from original // MBRTemplateFAT.bt file with major rework // and merged information from other templates. // 0.1 2013-01-14 B Vernoux: Initial release. // // NTFS IMPORTANT NOTE: // Not all NTFS drives can be parsed correctly. Some NTFS // sectors use a special Fixup value where the last two // bytes of a sector are copied to a different location // in order to detect bad sectors. This template cannot // properly handle the Fixup values and if the Fixup // value hits critical information the template may stop. //------------------------------------------------ LittleEndian(); local int NumDrives = 0; //################################################################ // MBR - Master Boot Record (contains partition information) //################################################################ // Partition Types typedef enum { EMPTY = 0, FAT_12 = 1, XENIX_ROOT = 2, XENIX_USR = 3, FAT_16_INF32MB = 4, EXTENDED = 5, FAT_16 = 6, NTFS_HPFS = 7, AIX = 8, AIX_BOOT = 9, OS2_BOOT_MGR = 10, PRI_FAT32_INT13 = 11, EXT_FAT32_INT13 = 12, SILICON_SAFE = 13, EXT_FAT16_INT13 = 14, WIN95_EXT_PARTITION = 15, OPUS = 16, FAT_12_HIDDEN = 17, COMPAQ_DIAG = 18, FAT_16_HIDDEN_INF32MB = 20, FAT_16_HIDDEN = 22, NTFS_HPFS_HIDDEN = 23, AST_SMARTSLEEP_PARTITION = 24, OSR2_FAT32 = 27, OSR2_FAT32_LBA = 28, HIDDEN_FAT16_LBA = 30, NEC_DOS = 36, PQSERVICE_ROUTERBOOT = 39, ATHEOS_FILE_SYSTEM = 42, NOS = 50, JFS_ON_OS2_OR_ECS = 53, THEOS_2GB = 56, PLAN_9_THEOS_SPANNED = 57, THEOS_4GB = 58, THEOS_EXTENDED = 59, PARTITIONMAGIC_RECOVERY = 60, HIDDEN_NETWARE = 61, VENIX = 64, LINUX_PPC_PREP = 65, LINUX_SWAP = 66, LINUX_NATIVE = 67, GOBACK = 68, BOOT_US_EUMEL_ELAN = 69, EUMEL_ELAN_1 = 70, EUMEL_ELAN_2 = 71, EUMEL_ELAN_3 = 72, OBERON = 76, QNX4_X = 77, QNX4_X_2ND_PART = 78, QNX4_X_3RD_PART_OBERON = 79, ONTRACK_LYNX_OBERON = 80, ONTRACK_NOVELL = 81, CP_M_MICROPORT_SYSV_AT = 82, DISK_MANAGER_AUX3 = 83, DISK_MANAGER_DDO = 84, EZ_DRIVE = 85, GOLDEN_BOW_EZ_BIOS = 86, DRIVEPRO_VNDI = 87, PRIAM_EDISK = 92, SPEEDSTOR = 97, GNU_HURD = 99, NOVEL1 = 100, NETWARE_386 = 101, NETWARE_SMS_PARTITION = 102, NOVELL_1 = 103, NOVELL_2 = 104, NETWARE_NSS = 105, DISKSECURE_MULTI_BOOT = 112, V7_X86 = 114, PC_IX = 117, M2FS_M2CS_VNDI = 119, XOSL_FS = 120, MINUX_OLD = 128, MINUX_LINUX = 129, LINUX_SWAP_2 = 130, LINUX_NATIVE_2 = 131, OS2_HIDDEN_HIBERNATION = 132, LINUX_EXTENDED = 133, OLD_LINUX_RAID_FAT16 = 134, NTFS_VOLUME_SET = 135, LINUX_PLAINTEXT_TABLE = 136, LINUX_KERNEL_AIR_BOOT = 138, FAULT_TOLERANT_FAT32 = 139, FAULT_TOLERANT_FAT32_INT13H = 140, FREE_FDISK_FAT12 = 141, LINUX_LOGICAL_VOLUME_MANAGER = 142, FREE_FDISK_PRIMARY_FAT16 = 144, FREE_FDISK_EXTENDED = 145, FREE_FDISK_LARGE_FAT16 = 146, AMOEBA = 147, AMOEBA_BBT = 148, MIT_EXOPC = 149, CHRP_ISO_9660 = 150, FREE_FDISK_FAT32 = 151, FREE_FDISK_FAT32_LBA = 152, DCE376 = 153, FREE_FDISK_FAT16_LBA = 154, FREE_FDISK_EXTENDED_LBA = 155, FORTHOS = 158, BSD_OS = 159, LAPTOP_HIBERNATION = 160, LAPTOP_HIBERNATION_HP = 161, HP_EXPANSION_SPEEDSTOR_1 = 163, HP_EXPANSION_SPEEDSTOR_2 = 164, BSD_386 = 165, OPENBSD_SPEEDSTOR = 166, NEXTSTEP = 167, MAC_OS_X = 168, NETBSD = 169, OLIVETTI = 170, MAC_OS_X_BOOT_GO = 171, RISC_OS_ADFS = 173, SHAGOS = 174, SHAGOS_SWAP_MACOS_X_HFS = 175, BOOTSTAR_DUMMY = 176, HP_EXPANSION_QNX = 177, QNX_POWER_SAFE = 178, HP_EXPANSION_QNX_2 = 179, HP_EXPANSION_SPEEDSTOR_3 = 180, HP_EXPANSION_FAT16 = 182, BSDI_FS = 183, BSDI_SWAP = 184, BOOT_WIZARD_HIDDEN = 187, ACRONIS_BACKUP = 188, BONNYDOS_286 = 189, SOLARIS_8_BOOT = 190, NEW_SOLARIS = 191, CTOS_REAL_32_DR_DOS = 192, DRDOS_SECURED = 193, HIDDEN_LINUX_SWAP = 195, DRDOS_SECURED_FAT16 = 196, DRDOS_SECURED_EXTENDED = 197, DRDOS_SECURED_FAT16_STRIPE = 198, SYRINX = 199, DR_DOS_8_1 = 200, DR_DOS_8_2 = 201, DR_DOS_8_3 = 202, DR_DOS_7_SECURED_FAT32_CHS = 203, DR_DOS_7_SECURED_FAT32_LBA = 204, CTOS_MEMDUMP = 205, DR_DOS_7_FAT16X = 206, DR_DOS_7_SECURED_EXT_DOS = 207, REAL_32_SECURE = 208, OLD_MULTIUSER_FAT12 = 209, OLD_MULTIUSER_FAT16 = 212, OLD_MULTIUSER_EXTENDED = 213, OLD_MULTIUSER_FAT16_2 = 214, CP_M_86 = 216, NON_FS_DATA_POWERCOPY_BACKUP = 218, CP_M = 219, HIDDEN_CTOS_MEMDUMP = 221, DELL_POWEREDGE_UTIL = 222, DG_UX_DISK_MANAGER_BOOTIT = 223, ACCESS_DOS = 225, DOS_R_O = 227, SPEEDSTOR_FAT16_EXTENDED = 228, STORAGE_DIMENSIONS_SPEEDSTOR = 230, LUKS = 232, RUFUS_EXTRA_FREEDESKTOP = 234, BEOS_BFS = 235, SKYOS_SKYFS = 236, LEGACY_MBR_EFI_HEADER = 238, EFI_FS = 239, LINUX_PA_RISC_BOOT = 240, STORAGE_DIMENSIONS_SPEEDSTOR_2 = 241, DOS_SECONDARY = 242, SPEEDSTOR_LARGE_PROLOGUE = 244, PROLOGUE_MULTI_VOLUME = 245, STORAGE_DIMENSIONS_SPEEDSTOR_3 = 246, DDRDRIVE_SOLID_STATE_FS = 247, PCACHE = 249, BOCHS = 250, VMWARE_FILE_SYSTEM = 251, VMWARE_SWAP = 252, LINUX_RAID = 253, SPEEDSTOR_LANSTEP_LINUX = 254, BBT = 255 } SYSTEMID; // Media descriptor typedef enum { FLOPPY = 0xf0, HARD_DRIVE = 0xf8, FLOPPY_320K_1 = 0xfa, FLOPPY_640K = 0xfb, FLOPPY_180K = 0xfc, FLOPPY_360K = 0xfd, FLOPPY_160K = 0xfe, FLOPPY_320K_2 = 0xff, } MEDIA; // Boot Indicator Values typedef enum { NOBOOT = 0, SYSTEM_PARTITION = 128, } BOOTINDICATOR; // Partition Entry typedef struct { BOOTINDICATOR BootIndicator; UBYTE StartingHead; WORD StartingSectCylinder; // Need Bit fields SYSTEMID SystemID; UBYTE EndingHead; WORD EndingSectCylinder; // Need Bit fields DWORD RelativeSector; DWORD TotalSectors; } PARTITION_ENTRY ; // Show SystemID beside each partition string ReadPARTITION_ENTRY( PARTITION_ENTRY &p ) { string s = EnumToString( p.SystemID ); if( Strlen(s) == 0 ) SPrintf( s, "(%d)", p.SystemID ); return s; } // MBR - Master Boot Record typedef struct { UBYTE BootCode[446]; PARTITION_ENTRY partitions[4]; WORD EndOfSectorMarker ; } MASTER_BOOT_RECORD ; // Extended partition typedef struct { UBYTE Empty[446]; PARTITION_ENTRY partitions[4]; WORD EndOfSectorMarker ; } EXTENDED_PARTITION ; //################################################################ // FAT16 Drives //################################################################ // Forward definition; struct FAT_DIRECTORY; // FAT 16 Boot sector typedef struct { UBYTE jmp[3]; CHAR OemName[8]; typedef struct FAT16_BPB { USHORT BytesPerSector; UBYTE SectorsPerCluster; USHORT ReservedSectors; UBYTE NumberOfCopiesOfFats; USHORT MaxRootDirEntries; USHORT NumberOfSectors; MEDIA MediaDescriptor ; USHORT SectorsPerFAT; USHORT SectorsPerTrack; USHORT NumHeadsPerCylinder; ULONG NumHiddenSectors; ULONG NumSectorInPartition; }; FAT16_BPB bpb_fat16; USHORT LogicDriveNumber; UBYTE extBootSignature ; ULONG SerialNumber ; CHAR VolumeLabel[11]; CHAR FileSystem[8]; UBYTE ExecutableCode[448]; WORD EndOfSectorMarker ; } FAT16_BOOTSECTOR ; // Display the volume label beside the boot sector string ReadBOOTSECTOR_FAT16( FAT16_BOOTSECTOR &boot ) { return boot.VolumeLabel; } // FAT16 FAT Table typedef enum { HARD_DISK = 0xfff8, FLOPPY_DISK = 0xfff0 } FAT16_MEDIATYPE; typedef enum { PARTITION_NOT_IN_USE = 0xffff, PARTITION_IN_USE = 0xfff7 } FAT16_PARTITIONSTATE; typedef enum { FREE_CLUSTER = 0x0000, RESERVED_0001 = 0x0001, RESERVED_FFF0 = 0xFFF0, RESERVED_FFF1 = 0xFFF1, RESERVED_FFF2 = 0xFFF2, RESERVED_FFF3 = 0xFFF3, RESERVED_FFF4 = 0xFFF4, RESERVED_FFF5 = 0xFFF5, RESERVED_FFF6 = 0xFFF6, BAD_CLUSTER = 0xFFF7, USED_LAST_CLUSTER_FFF8 = 0xFFF8, USED_LAST_CLUSTER_FFF9 = 0xFFF9, USED_LAST_CLUSTER_FFFA = 0xFFFA, USED_LAST_CLUSTER_FFFB = 0xFFFB, USED_LAST_CLUSTER_FFFC = 0xFFFC, USED_LAST_CLUSTER_FFFD = 0xFFFD, USED_LAST_CLUSTER_FFFE = 0xFFFE, USED_LAST_CLUSTER_FFFF = 0xFFFF } FAT16_CLUSTERINFO; // FAT 16 Table typedef struct (quad SizeOfFatTableInSectors, UBYTE NumberOfCopiesOfFats) { local unsigned char ClusterEntrySize=2; if(NumberOfCopiesOfFats==1) { FAT16_MEDIATYPE MediaType; FAT16_PARTITIONSTATE PartitionState; FAT16_CLUSTERINFO Cluster[ (((SizeOfFatTableInSectors*512)/ClusterEntrySize)-ClusterEntrySize) ]; } else if(NumberOfCopiesOfFats==2) { FAT16_MEDIATYPE MediaType; FAT16_PARTITIONSTATE PartitionState; FAT16_CLUSTERINFO Cluster[ ((((SizeOfFatTableInSectors*512)/ClusterEntrySize)-ClusterEntrySize)/NumberOfCopiesOfFats)-1 ]; FAT16_MEDIATYPE MediaTypeCopy; FAT16_PARTITIONSTATE PartitionStateCopy; FAT16_CLUSTERINFO ClusterCopy[ ((((SizeOfFatTableInSectors*512)/ClusterEntrySize)-ClusterEntrySize)/NumberOfCopiesOfFats)-1 ]; } } FAT16_FAT_TABLE; // Define a FAT 16 Drive typedef struct { local int DriveNum = NumDrives++; // keep track of the index of this drive local quad FATTableFilePos; local quad DataAreaFilePos; local DWORD ClusterSize; local DWORD ClusterEntrySize; local quad FATTableSector; local quad FATTableSizeInSectors; local quad RootDirEntrySector; local quad RootDirEntryFilePos; local quad DataAreaSector; local quad CurrentPosSector=FTell()/512; // FAT16 Boot sector FAT16_BOOTSECTOR boot_fat16 ; // Calculate offsets FATTableSector=CurrentPosSector+boot_fat16.bpb_fat16.ReservedSectors; RootDirEntrySector=FATTableSector+(boot_fat16.bpb_fat16.SectorsPerFAT*2); RootDirEntryFilePos=RootDirEntrySector*512; FATTableFilePos=FATTableSector*512; FATTableSizeInSectors=RootDirEntrySector-FATTableSector; DataAreaSector=RootDirEntrySector+((boot_fat16.bpb_fat16.MaxRootDirEntries*32)/boot_fat16.bpb_fat16.BytesPerSector); DataAreaFilePos=DataAreaSector*512; ClusterSize=boot_fat16.bpb_fat16.BytesPerSector*boot_fat16.bpb_fat16.SectorsPerCluster; ClusterEntrySize=2; // FAT16 FAT Table FSeek( FATTableFilePos ); FAT16_FAT_TABLE table(FATTableSizeInSectors,boot_fat16.bpb_fat16.NumberOfCopiesOfFats) ; // FAT Directory Entry FSeek( RootDirEntryFilePos ); FAT_DIRECTORY root_dir; } FAT16_DRIVE ; //################################################################ // FAT32 Drives //################################################################ // FAT 32 Boot sector typedef struct { BYTE jmp[3]; CHAR OemName[8]; typedef struct FAT32_BPB { WORD BytesPerSector; BYTE SectorsPerCluster; WORD ReservedSectors; BYTE NumberOfFATs; WORD RootEntries; WORD TotalSectors; MEDIA Media; WORD SectorsPerFAT; WORD SectorsPerTrack; WORD HeadsPerCylinder; DWORD HiddenSectors; DWORD TotalSectorsBig; DWORD SectorsPerFAT; WORD Flags; WORD Version; DWORD RootCluster; WORD InfoSector; WORD BootBackupStart; BYTE Reserved[12]; }; FAT32_BPB bpb_fat32; BYTE DriveNumber; BYTE Unused; BYTE ExtBootSignature ; DWORD SerialNumber ; CHAR VolumeLabel[11]; CHAR FileSystem[8]; UBYTE BootCode[420]; WORD EndOfSectorMarker ; } FAT32_BOOTSECTOR ; // Display the volume label beside the boot sector string ReadFAT32_BOOTSECTOR( FAT32_BOOTSECTOR &boot ) { return boot.VolumeLabel; } // FAT32 FAT Table // Warning on FAT32 only 28bit contain value // 4 high bit (MSB) are reserved for future use. // It is why following FAT32 enum could be wrong if 4 high bit reserved are different from 0x0 or 0xf typedef enum { HARD_DISK_FAT32 = 0x0ffffff8, FLOPPY_DISK_FAT32 = 0x0ffffff0 } FAT32_MEDIATYPE; typedef enum { PARTITION_NOT_IN_USE_FAT32 = 0xffffffff, PARTITION_IN_USE_FAT32 = 0xfffffff7 } FAT32_PARTITIONSTATE; typedef enum { FREE_CLUSTER_FAT32 = 0x00000000, RESERVED_0001_FAT32 = 0x00000001, RESERVED_FFF0_FAT32 = 0x0FFFFFF0, RESERVED_FFF1_FAT32 = 0x0FFFFFF1, RESERVED_FFF2_FAT32 = 0x0FFFFFF2, RESERVED_FFF3_FAT32 = 0x0FFFFFF3, RESERVED_FFF4_FAT32 = 0x0FFFFFF4, RESERVED_FFF5_FAT32 = 0x0FFFFFF5, RESERVED_FFF6_FAT32 = 0x0FFFFFF6, BAD_CLUSTER_FAT32 = 0x0FFFFFF7, USED_LAST_CLUSTER_FFF8_FAT32 = 0x0FFFFFF8, USED_LAST_CLUSTER_FFF9_FAT32 = 0x0FFFFFF9, USED_LAST_CLUSTER_FFFA_FAT32 = 0x0FFFFFFA, USED_LAST_CLUSTER_FFFB_FAT32 = 0x0FFFFFFB, USED_LAST_CLUSTER_FFFC_FAT32 = 0x0FFFFFFC, USED_LAST_CLUSTER_FFFD_FAT32 = 0x0FFFFFFD, USED_LAST_CLUSTER_FFFE_FAT32 = 0x0FFFFFFE, USED_LAST_CLUSTER_FFFF_FAT32 = 0x0FFFFFFF } FAT32_CLUSTERINFO; // FAT 32 Table typedef struct (quad SizeOfFatTableInSectors, UBYTE NumberOfCopiesOfFats) { local unsigned char ClusterEntrySize=4; if(NumberOfCopiesOfFats==1) { FAT32_MEDIATYPE MediaType; FAT32_PARTITIONSTATE PartitionState; FAT32_CLUSTERINFO Cluster[ (((SizeOfFatTableInSectors*512)/ClusterEntrySize)-ClusterEntrySize) ]; } else if(NumberOfCopiesOfFats==2) { FAT32_MEDIATYPE MediaType; FAT32_PARTITIONSTATE PartitionState; FAT32_CLUSTERINFO Cluster[ ((((SizeOfFatTableInSectors*512)/ClusterEntrySize)-ClusterEntrySize)/NumberOfCopiesOfFats)-0 ]; FAT32_MEDIATYPE MediaTypeCopy; FAT32_PARTITIONSTATE PartitionStateCopy; FAT32_CLUSTERINFO ClusterCopy[ ((((SizeOfFatTableInSectors*512)/ClusterEntrySize)-ClusterEntrySize)/NumberOfCopiesOfFats)-0 ]; } } FAT32_FAT_TABLE; // Define a FAT 32 Drive typedef struct { local int DriveNum = NumDrives++; // keep track of the index of this drive local quad FATTableFilePos; local quad DataAreaFilePos; local DWORD ClusterSize; local DWORD ClusterEntrySize; local quad FATTableSector; local quad FATTableSizeInSectors; local quad RootDirEntrySector; local quad RootDirEntryFilePos; local quad DataAreaSector; local quad CurrentPosSector=FTell()/512; // FAT32 Boot sector FAT32_BOOTSECTOR boot_fat32 ; // Calculate offsets FATTableSector=CurrentPosSector+boot_fat32.bpb_fat32.ReservedSectors; RootDirEntrySector=FATTableSector+(boot_fat32.bpb_fat32.SectorsPerFAT*2); RootDirEntryFilePos=RootDirEntrySector*512; FATTableFilePos=FATTableSector*512; FATTableSizeInSectors=RootDirEntrySector-FATTableSector; DataAreaSector=RootDirEntrySector; DataAreaFilePos=DataAreaSector*512; ClusterSize=boot_fat32.bpb_fat32.BytesPerSector*boot_fat32.bpb_fat32.SectorsPerCluster; ClusterEntrySize=4; // FAT32 FAT Table FSeek( FATTableFilePos ); FAT32_FAT_TABLE table(FATTableSizeInSectors,boot_fat32.bpb_fat32.NumberOfFATs); // FAT32 Directory Entry FSeek( RootDirEntryFilePos ); FAT_DIRECTORY root_dir; } FAT32_DRIVE ; //################################################################ // FAT Directory (Shared between FAT16 and FAT32) //################################################################ // FAT Directory Attribute typedef enum { NoneOrFile = 0, ReadOnly = 1, // bit0 Hidden = 2, // bit1 ReadOnlyHidden = 3, System = 4, // bit2 ReadOnlySystem = 5, HiddenSystem0 = 6, ReadOnlyHiddenSystem = 7, VolumeID = 8, // bit3 ReadOnlyVolume = 9, HiddenSystem1 = 10, ReadOnlySystemVolume0 = 11, SystemVolume = 12, ReadOnlySystemVolume1 = 13, HiddenSystemVolume = 14, LFN_Entry = 15, Directory = 16, // bit4 DirectoryHidden = 18, DirectoryHiddenSystem = 23, Archive = 32, // bit5 ArchiveReadOnly = 33, ArchiveHidden = 34, VolumeIDArchive = 40, DirectoryArchiveHidden = 50 }FAT_ATTR_TYPE; // FAT Short directory entry typedef struct // Normal-Short structure { CHAR Name[8]; // Blank-padded name CHAR Extension[3]; //Blank-padded extension FAT_ATTR_TYPE Attribute; // See FAT_ATTR_TYPE enum only 5 valid bit (from 0 to 4) UBYTE Reserved; UBYTE CreateTime10ms; //10-ms units "Create Time" refinement DOSTIME CreateTime; DOSDATE CreateDate; DOSDATE AccessDate; USHORT HighCluster; // Used on FAT32 only DOSTIME UpdateTime; DOSDATE UpdateDate; USHORT Cluster; ULONG FileSizeInBytes; //File size in bytes (always zero for directories). } FAT_SHORTENTRY ; string ReadFAT_SHORTENTRY( FAT_SHORTENTRY &f ) { string s; if(f.Name[0]==0) { return "Last Dir Entry Empty"; } // Short Entry if( (f.Attribute == NoneOrFile) || (f.Attribute == ReadOnly) || (f.Attribute == Hidden) || (f.Attribute == ReadOnlyHidden) || (f.Attribute == HiddenSystem1) || (f.Attribute == ReadOnlySystemVolume0) || (f.Attribute == LFN_Entry) || (f.Attribute == Archive) || (f.Attribute == ArchiveReadOnly) || (f.Attribute == ArchiveHidden) ) { SPrintf(s, "%s.%s (%s)",f.Name,f.Extension,EnumToString(f.Attribute)); } else { SPrintf(s, "%s%s (%s)",f.Name,f.Extension,EnumToString(f.Attribute)); } if( (uchar)f.Name[0] == 0xE5 ) return "**Erased name:" + s; else return s; } typedef struct { UBYTE LFN_RecSeqNum : 6; // bit0-5 LFN sequence number (1..63) UBYTE Last_LFN_record : 1; // bit6 Last LFN record in the sequence UBYTE LFN_Erased : 1; // bit7 LFN record is an erased long name entry or maybe if it is part of an erased long name? }tLFN_RecordSeqNum; // FAT Long directory entry typedef struct { typedef union ulfn { tLFN_RecordSeqNum LFN_RecordSeqNum; // LFN Record Sequence Number unsigned char char0; }ULFN; ULFN LFN; wchar_t UnicodeChar1[5]; //5 UNICODE characters, LFN first part. FAT_ATTR_TYPE Attribute; // This field contains the special value of 0Fh, which indicates an LFN entry. UBYTE Reserved; UBYTE ChkShortName; // Checksum of short name entry, used to validate that the LFN entry belongs to the short name entry following. According to Ralf Brown's interrupt list, the checksum is computed by adding up all the short name characters and rotating the intermediate value right by one bit position before adding each character. wchar_t UnicodeChar2[6]; //6 UNICODE characters, LFN 2nd part. USHORT Cluster; //Initial cluster number, which is always zero for LFN entries. wchar_t UnicodeChar3[2]; //2 UNICODE characters, LFN 3rd part. } FAT_LONGENTRY; struct FAT_FILE_DATA; // Forward definition // Move the read position to the start of the given cluster void FAT_JumpToCluster( DWORD cluster, int DriveNum ) { FSeek( drive[DriveNum].DataAreaFilePos + (cluster-2)*drive[DriveNum].ClusterSize ); } // Return the cluster the address belongs to DWORD FAT_AddressToCluster( UQUAD address, int DriveNum ) { return (DWORD)((address - drive[DriveNum].DataAreaFilePos) / drive[DriveNum].ClusterSize) + 2; } // Special local variables used when defining FAT dir entries // these are used because the dir entry may not be contiguous // (in different clusters) local UQUAD FATDirEntryStart; local UQUAD FATDirEntryEnd; // end of contiguous area local WORD FATIsDirEntryContiguous; // Check for directory reads that go off the end of a cluster - // need to jump to the next cluster if so void FAT_CheckDirEntryRead( int DriveNum ) { // Record positions if( FATIsDirEntryContiguous ) FATDirEntryEnd = FTell(); FATDirEntryStart = FTell(); if( ((FATDirEntryStart - drive[DriveNum].DataAreaFilePos) % drive[DriveNum].ClusterSize == 0) && (FATDirEntryStart > drive[DriveNum].DataAreaFilePos) ) { // Hit the end of the cluster - must jump to the next // cluster and mark as non-contiguous FATIsDirEntryContiguous = false; local DWORD Cluster = FAT_AddressToCluster( FATDirEntryStart, DriveNum ) - 1; Cluster = drive[DriveNum].table.Cluster[ Cluster-2 ]; FAT_JumpToCluster( Cluster, DriveNum ); FATDirEntryStart = FTell(); } } // FAT Directory Entry typedef struct { // Copy offset info from parent local int DriveNum = parentof(this).DriveNum; // keep track of which drive this belongs to local int i; local DWORD Cluster; // Read Long/Short directory entries FATIsDirEntryContiguous = true; if(ReadByte(FTell()+11)==0x0f) // LFN Entry { local unsigned char NumberOfLFNEntry; local unsigned char dirname0; dirname0=ReadByte(FTell()); if( !(dirname0==0x00) ) { if( dirname0==0xE5 ) // Empty/Erased { for(i=0;i<63;i++) { dirname0=ReadByte(FTell()); if( !(dirname0==0xE5) ) // Check still Empty/Erased ? break; if(ReadByte(FTell()+11)!=0x0f) // Check is still LFN ? break; FAT_LONGENTRY long_entry; FAT_CheckDirEntryRead( DriveNum ); } } else { FAT_LONGENTRY long_entry; NumberOfLFNEntry=long_entry.LFN.LFN_RecordSeqNum.LFN_RecSeqNum-1; for(i=0;i 0 ) { // Define data for this file FAT_JumpToCluster( Cluster, DriveNum ); if( (uchar)short_entry.Name[0] == 0xE5) FAT_FILE_DATA possibleDeletedData; // try to show the deleted information - may not be accurate else FAT_FILE_DATA data; // Define the sub-directory if( (short_entry.Attribute & Directory) && (short_entry.Name != ". ") && (short_entry.Name != ".. ") && ((uchar)short_entry.Name[0] != 0xE5) ) { // Define a sub-directory FAT_JumpToCluster( Cluster, DriveNum ); FAT_DIRECTORY sub_dir; } } } FSeek( FATDirEntryEnd ); } FAT_DIR_ENTRY ; // Show file name beside directory entry wstring ReadFAT_DIR_ENTRY( FAT_DIR_ENTRY &f ) { local unsigned short i; local unsigned short NumberOfLFNEntry; local wstring str; if( exists( f.long_entry ) ) { if(f.long_entry[0].LFN.LFN_RecordSeqNum.LFN_Erased==1) { // Entry deleted str+="**Erased name:"; // Count number of erased entry for(i=0;i<63;i++) { if(exists(f.long_entry[i].LFN.char0)) { if(f.long_entry[i].LFN.char0!=0xE5) { break; } }else { break; } } NumberOfLFNEntry=i-1; }else { // Long Entry //str+="Name:"; NumberOfLFNEntry=f.long_entry[0].LFN.LFN_RecordSeqNum.LFN_RecSeqNum-1; } for(i=NumberOfLFNEntry;i>0;i--) { str+=f.long_entry[i].UnicodeChar1; str+=f.long_entry[i].UnicodeChar2; str+=f.long_entry[i].UnicodeChar3; } str+=f.long_entry[0].UnicodeChar1; str+=f.long_entry[0].UnicodeChar2; str+=f.long_entry[0].UnicodeChar3; // End string at 0xFFFF local int endPos = WStrchr( str, 0xFFFF ); if( endPos != -1 ) str[endPos] = 0; // Add attribute info if( exists(f.short_entry) ) { str += " (" + EnumToString(f.short_entry.Attribute) + ")"; if( (f.short_entry.Attribute & Directory) && ((UBYTE)f.short_entry.Name[0] != 0xE5) ) str = "/" + str; } return str; } else if( exists (f.short_entry) ) { // Return text using just the short entry str = ReadFAT_SHORTENTRY( f.short_entry ); if( (f.short_entry.Attribute & Directory) && ((UBYTE)f.short_entry.Name[0] != 0xE5) ) str = "/" + str; return str; } else return ""; } // FAT Cluster of data in a file typedef struct (uint size, ULONG lengthLeft ) { if( (lengthLeft >= size) || (lengthLeft <= 0) ) UBYTE data[ size ]; else { UBYTE data[ lengthLeft ]; UBYTE slack[ size - lengthLeft ]; } } FAT_FILE_CLUSTER; // Extract starting cluster from a dir entry DWORD FAT_CalculateCluster( FAT_SHORTENTRY &entry, int DriveNum ) { if( drive[DriveNum].ClusterEntrySize == 4 ) return ((DWORD)entry.HighCluster << 16) | entry.Cluster; // fat32 else return entry.Cluster; // fat16 } // FAT File data displayed as clusters typedef struct { local int DriveNum = parentof(this).DriveNum; // keep track of which drive this belongs to local DWORD ClusterSize = drive[DriveNum].ClusterSize; local DWORD ClusterEntrySize = drive[DriveNum].ClusterEntrySize; local DWORD Cluster = FAT_CalculateCluster( parentof(this).short_entry, DriveNum ); local DWORD FirstCluster = Cluster; local DWORD MaxCluster = (ClusterEntrySize == 2) ? 0xFFF0 : 0x0FFFFFF0; local ULONG SizeLeft = parentof(this).short_entry.FileSizeInBytes; // Define clusters while(1) { // Create one cluster FAT_JumpToCluster( Cluster, DriveNum ); FAT_FILE_CLUSTER cluster( ClusterSize, SizeLeft ); if( SizeLeft < ClusterSize ) SizeLeft = 0; else SizeLeft -= ClusterSize; // Jump to the next cluster Cluster = drive[DriveNum].table.Cluster[ Cluster-2 ]; if( (Cluster == 0) || (Cluster >= MaxCluster) || (Cluster == FirstCluster) ) break; } } FAT_FILE_DATA ; // Use on-demand parsing for file data - do not load the data until the hierarchy is opened. // By default we just assume this is 1024 in length because the actual data may not // be contiguous on disk. int SizeFAT_FILE_DATA( FAT_FILE_DATA &dir ) { return 1024; } // FAT Directory Entry List typedef struct { local int DriveNum = parentof(this).DriveNum; // keep track of which drive this belongs to // Define all file entries FATDirEntryStart = FTell(); while(1) { FAT_DIR_ENTRY direntry; if(direntry.short_entry.Name[0]==0) // End of Directory Entry break; FSeek( FATDirEntryStart ); // needed for non-contiguous directories } } FAT_DIRECTORY ; // Use on-demand parsing for directories - do not load the data until the hierarchy is opened. // By default we just assume this is 1024 in length because the actual data may not // be contiguous on disk. int SizeFAT_DIRECTORY( FAT_DIRECTORY &dir ) { return 1024; } //################################################################ // NTFS Drives //################################################################ // Forward definition struct NTFS_FILE_RECORD; // NTFS Boot sector struct NTFS_BOOTSECTOR { BYTE jmp[3]; // Jump Instruction CHAR OEMName[8]; // OEM Identifier typedef struct NTFS_BPB { WORD BytesPerSector; BYTE SectorsPerCluster; WORD ReservedSectors; UBYTE Zero[3]; WORD NotUsed; MEDIA MediaDescriptor ; WORD Zero; WORD SectorsPerTrack; WORD HeadsPerCylinder; DWORD HiddenSectors; DWORD NotUsed; DWORD NotUsed; UQUAD TotalSectors; UQUAD LogicalClusterMFT; UQUAD LogicalClusterMFTMirror; DWORD ClustersPerFileRecSegment; DWORD ClustersPerIndexBlock; UQUAD SerialNumber ; DWORD Checksum; }; NTFS_BPB bpb_ntfs; BYTE BootCode[426]; WORD EndOfSectorMarker ; }; typedef enum { STANDARD_INFORMATION = 0x10, ATTRIBUTE_LIST = 0x20, FILE_NAME = 0x30, OBJECT_ID = 0x40, SECURITY_DESCRIPTOR = 0x50, VOLUME_NAME = 0x60, VOLUME_INFORMATION = 0x70, DATA = 0x80, INDEX_ROOT = 0x90, INDEX_ALLOCATION = 0xA0, BITMAP = 0xB0, REPARSE_POINT = 0xC0, EA_INFORMATION = 0xD0, EA = 0xE0, PROPERTY_SET = 0xF0, LOGGED_UTILITY_STREAM = 0x100 } NTFS_ATTR_TYPE; // NTFS Standard Information typedef struct { FILETIME CreationTime; FILETIME ModifiedTime; FILETIME MFTChangedTime; FILETIME FileReadTime; DWORD DosPermissions; DWORD MaximumVersions; DWORD VersionNumber; DWORD ClassID; DWORD OwnerID; DWORD SecurityID; UQUAD QuotaCharged; UQUAD UpdateSequenceNumber; } NTFS_ATTR_STANDARD; // NTFS Namspace - type of file name typedef enum { NAMESPACE_POSIX = 0, NAMESPACE_WIN32 = 1, NAMESPACE_DOS = 2, NAMESPACE_WIN32DOS = 3 } NTFS_NAMESPACE; // NTFS File Flags typedef struct { DWORD ReadOnly : 1; DWORD Hidden : 1; DWORD System : 1; DWORD : 1; // Unused DWORD : 1; DWORD Archive : 1; DWORD Device : 1; DWORD Normal : 1; DWORD Temp : 1; DWORD Sparse : 1; DWORD Reparse : 1; DWORD Compressed : 1; DWORD Offline : 1; DWORD NotIndexed : 1; DWORD Encrypted : 1; DWORD : 13; DWORD Directory : 1; DWORD IndexView : 1; } NTFS_FILE_FLAGS ; // Show some information beside the flag name string ReadNTFS_FILE_FLAGS( NTFS_FILE_FLAGS &flags ) { string s; if( flags.Directory ) s += "Directory "; if( flags.ReadOnly ) s += "ReadOnly "; if( flags.Hidden ) s += "Hidden "; if( flags.System ) s += "System "; return s; } // NTFS File Name Information typedef struct { local int64 start = FTell(); BitfieldDisablePadding(); UQUAD FileRecordNumber : 48; UQUAD SequenceNumber : 16; BitfieldEnablePadding(); FILETIME CreationTime; FILETIME ModifiedTime; FILETIME MFTChangedTime; FILETIME FileReadTime; UQUAD AllocateSize; UQUAD RealSize; NTFS_FILE_FLAGS Flags; DWORD Reparse; UBYTE FileNameLength; NTFS_NAMESPACE Namespace; if( FileNameLength > 0 ) wchar_t FileName[ FileNameLength ]; } NTFS_ATTR_FILE_NAME; // NTFS Volume Name typedef struct (int length) { wchar_t VolumeName[length/2]; } NTFS_ATTR_VOLUME_NAME; // NTFS Volume Information typedef struct { UQUAD Empty1; UCHAR MajorVersion; UCHAR MinorVersion; WORD Flags; DWORD Empty2; } NTFS_ATTR_VOLUME_INFO; // NTFS Index Root typedef struct { DWORD AttributeType; DWORD CollationRule; DWORD IndexAllocationEntrySize; DWORD ClustersPerIndexRecord; DWORD FirstIndexEntryOffset; DWORD IndexEntriesSize; DWORD IndexEntriesAllocated; DWORD HasLargeIndex : 1; DWORD : 31; } NTFS_ATTR_INDEX_ROOT; // NTFS Index Entry Header typedef struct { UQUAD FileRecordNumber : 48; UQUAD SequenceNumber : 16; WORD IndexEntryLength; WORD StreamLength; UBYTE HasSubNode : 1; UBYTE IsLastEntry : 1; UBYTE : 6; UBYTE Padding[3]; } NTFS_ATTR_INDEX_ENTRY_HEADER; // NTFS Index Entry typedef struct { local int64 start = FTell(); NTFS_ATTR_INDEX_ENTRY_HEADER header; if( !header.IsLastEntry && (header.StreamLength > 0) ) UBYTE Stream[header.StreamLength]; if( header.HasSubNode ) { FSeek( start + header.IndexEntryLength - 8 ); UQUAD SubNode; } // Jump to the end of the record FSeek( start + header.IndexEntryLength ); } NTFS_ATTR_INDEX_ENTRY; // NTFS File Name Index Entry typedef struct { local int64 start = FTell(); NTFS_ATTR_INDEX_ENTRY_HEADER header; if( !header.IsLastEntry && (header.StreamLength > 0) ) NTFS_ATTR_FILE_NAME fileNameAttr; if( header.HasSubNode ) { FSeek( start + header.IndexEntryLength - 8 ); UQUAD SubNode; } // Jump to the end of the record FSeek( start + header.IndexEntryLength ); } NTFS_INDEX_FILE_NAME_ENTRY ; // Display file name and subnode beside index entry wstring ReadNTFS_INDEX_FILE_NAME_ENTRY( NTFS_INDEX_FILE_NAME_ENTRY &fn ) { string s; if( exists( fn.fileNameAttr ) ) s = fn.fileNameAttr.FileName; if( fn.header.HasSubNode ) { string subnode; SPrintf( subnode, " (Subnode %d)", fn.SubNode ); s += subnode; } return s; } // NTFS Run - define a block of data (starting VCN and size) typedef struct { UBYTE LengthFieldSize : 4; UBYTE OffsetFieldSize : 4; BitfieldDisablePadding(); UQUAD RunLength : LengthFieldSize*8; UQUAD LCNOffset : OffsetFieldSize*8; // Logical Cluster Number BitfieldEnablePadding(); } NTFS_RUN; // NTFS Run list - define where file data exists on disk typedef struct ( UQUAD maxPos ) { while( (ReadUByte( FTell() ) != 0) && (FTell() < maxPos) ) NTFS_RUN run; } NTFS_RUN_LIST; // NTFS File Index typedef struct { local int64 startPos = FTell(); struct NTFS_INDEX_HEADER { UCHAR Magic[4]; WORD UpdateSequenceOffset; WORD UpdateSequenceSize; UQUAD LogFileSequence; UQUAD VCN; // Virtual Cluster Number DWORD IndexEntriesOffset; DWORD IndexEntriesSize; DWORD IndexEntriesAllocated; DWORD HasChildren; WORD UpdateSequence; if( UpdateSequenceSize > 0 ) WORD UpdateSequenceArray[UpdateSequenceSize]; } header; // Create the list of file name entries here if( header.Magic == "INDX" ) { FSeek( startPos + 0x18 + header.IndexEntriesOffset ); local int i; do { NTFS_INDEX_FILE_NAME_ENTRY entry; } while( (!entry.header.IsLastEntry) && (entry.header.IndexEntryLength > 0) && (FTell() - startPos < header.IndexEntriesSize + header.IndexEntriesOffset) ); } } NTFS_FILE_INDEX; // Convert from an lcn (logical cluster number) to an address in the file UQUAD NTFS_LCNToAddress( UQUAD lcn, int DriveNum ) { return drive[DriveNum].DriveStart + lcn * drive[DriveNum].ClusterSize; } // NTFS File Block - store a block of data in a file typedef struct (int size, UQUAD lengthLeft) { if( lengthLeft >= size ) UBYTE data[ size ]; else { UBYTE data[ lengthLeft ]; UBYTE slack[ size - lengthLeft ]; } } NTFS_FILE_BLOCK; // NTFS File data - stores information from the file on disk // as a series of blocks - could be located in different places // in the file. typedef struct { local int i; local UQUAD vcn = 0; local UQUAD RunLength; local int DriveNum = parentof(parentof(parentof(this))).DriveNum; local UQUAD LengthLeft = parentof(this).header.AttributeSize; while( exists( parentof(this).runList.run[i] ) ) { // Create the block of data using the run list FSeek( NTFS_LCNToAddress( NTFS_VCNToLCN( parentof(this).runList, vcn ), DriveNum ) ); RunLength = parentof(this).runList.run[i].RunLength; vcn += RunLength; NTFS_FILE_BLOCK block( RunLength * drive[DriveNum].ClusterSize, LengthLeft ); LengthLeft -= RunLength * drive[DriveNum].ClusterSize; i++; } } NTFS_FILE_DATA ; // set size as 1024 as could be disjoint structures // NTFS Attribute typedef struct { local int64 start = FTell(); struct NTFS_ATTRIBUTE_HEADER { NTFS_ATTR_TYPE Type; DWORD Length; UBYTE NonResident; UBYTE NameLength; WORD NameOffset; WORD IsCompressed : 1; WORD : 13; WORD IsEncrypted : 1; WORD IsSparse : 1; WORD AttributeID; if( NonResident ) { UQUAD StartingVCN; UQUAD LastVCN; WORD DataRunsOffset; WORD CompressionUnitSize; UBYTE Padding[4]; UQUAD AttributeAllocated; UQUAD AttributeSize; UQUAD StreamDataSize; } else { DWORD AttributeLength; WORD AttributeOffset; UBYTE IndexedFlag; UBYTE Padding; } if( NameLength > 0 ) wchar_t Name[NameLength]; } header; if( !header.NonResident ) { // Resident attributes if( header.Type == STANDARD_INFORMATION ) NTFS_ATTR_STANDARD standardInformation; else if( header.Type == FILE_NAME ) NTFS_ATTR_FILE_NAME fileName; else if( header.Type == VOLUME_NAME ) NTFS_ATTR_VOLUME_NAME volumeName( header.AttributeLength ); else if( header.Type == VOLUME_INFORMATION ) NTFS_ATTR_VOLUME_INFO volumeInfo; else if( header.Type == INDEX_ROOT ) { // Stores the root nodes of the btree index for file names NTFS_ATTR_INDEX_ROOT indexRoot; do { NTFS_INDEX_FILE_NAME_ENTRY indexEntry; } while( !indexEntry.header.IsLastEntry && (indexEntry.header.IndexEntryLength > 0) && (FTell() - start < header.Length) ); } else if( header.Type == DATA ) { // File data is stored directly in the header for small files if( header.AttributeLength > 0 ) ubyte fileData[ header.AttributeLength ]; } } else { // Non-resident attribute - data is in a different place in the // drive and use runlist to locate NTFS_RUN_LIST runList( start + header.Length ); if( (header.Type == INDEX_ALLOCATION) && (parentof(this).header.IsDirectory) ) { // Stores an index (btree) of file names local int i; for( i = 0; i <= header.LastVCN; i++ ) { FSeek( NTFS_LCNToAddress( NTFS_VCNToLCN( runList, i ), parentof(parentof(this)).DriveNum ) ); if( ReadString( FTell(), 4 ) != "INDX" ) // make sure header is there break; NTFS_FILE_INDEX index; } } else if( exists(runList.run[0].LCNOffset) ) { // Generic data - use the file data struct to read the data FSeek( NTFS_LCNToAddress( runList.run[0].LCNOffset, parentof(parentof(this)).DriveNum ) ); NTFS_FILE_DATA data; } } // Jump to the end of the record FSeek( start + header.Length ); } NTFS_ATTRIBUTE ; // Display data beside the attribute string ReadNTFS_ATTRIBUTE( NTFS_ATTRIBUTE &attr ) { string s; if( attr.header.Type == FILE_NAME ) SPrintf( s, "%s = %s", EnumToString( attr.header.Type ), attr.fileName.FileName ); else if( attr.header.Type == VOLUME_NAME ) SPrintf( s, "%s = %s", EnumToString( attr.header.Type ), attr.volumeName.VolumeName ); else s = EnumToString( attr.header.Type ); if( attr.header.NonResident ) s += " (Non-Resident)"; return s; } // Used to sign-extend run values local UQUAD NTFS_SignExtend[8] = { 0xffffffffffffff00, 0xffffffffffff0000, 0xffffffffff000000, 0xffffffff00000000, 0xffffff0000000000, 0xffff000000000000, 0xff00000000000000, 0x0000000000000000 }; // Function to convert from a virtual cluster number (file cluster number) // to a logical cluster number (drive cluster number). The file contains a // number of runs and check which run the vcn belongs to. UQUAD NTFS_VCNToLCN( NTFS_RUN_LIST &runlist, UQUAD vcn ) { local int i; local UQUAD offset = 0; while( exists( runlist.run[i] ) ) { // Move ahead using LCNOffset if( ReadUByte( startof(runlist.run[i].LCNOffset)+runlist.run[i].OffsetFieldSize-1 ) >= 0x80 ) { // Offset is negative - sign extend offset += (QUAD)(runlist.run[i].LCNOffset + NTFS_SignExtend[runlist.run[i].OffsetFieldSize-1] ); } else { // Offset is positive offset += runlist.run[i].LCNOffset; } // Check if vcn is in this run if( vcn < runlist.run[i].RunLength ) return vcn + offset; else { vcn -= runlist.run[i].RunLength; i++; } } return 0xFFFFFFFFFFFFFFFFL; // not found } // Function to iterate through the file name index (btree) // and list the files UQUAD NTFS_ListFiles( NTFS_INDEX_FILE_NAME_ENTRY &entry, NTFS_ATTRIBUTE &allocation, int DriveNum, int level, UQUAD previousFile ) { // Step into sub-node - watch for infinite recursion local int k = 0; if( entry.header.HasSubNode && (level < 10) ) { while( 1 ) { previousFile = NTFS_ListFiles( allocation.index[ entry.SubNode ].entry[k], allocation, DriveNum, level+1, previousFile ); if( allocation.index[ entry.SubNode ].entry[k].header.IsLastEntry ) break; k++; } } // Create file record if( entry.header.StreamLength > 0 ) { // Avoid creating the same file twice if( previousFile != entry.header.FileRecordNumber ) { // Locate the file position - have to use the runlist of the mft previousFile = entry.header.FileRecordNumber; local int dataAttr = NTFS_FindAttribute( drive[DriveNum].mft.mft[0], DATA ); local int fileRecsPerCluster = drive[DriveNum].ClusterSize / 1024; local UQUAD lcn = NTFS_VCNToLCN( drive[DriveNum].mft.mft[0].attribute[dataAttr].runList, entry.header.FileRecordNumber/fileRecsPerCluster ); FSeek( NTFS_LCNToAddress( lcn, DriveNum ) + (entry.header.FileRecordNumber % fileRecsPerCluster)*1024 ); if( ReadString( FTell(), 4 ) == "FILE" ) // make sure header is there NTFS_FILE_RECORD file; else DWORD invalid; // file record not found } } return previousFile; } // Find an attribute in a file record int NTFS_FindAttribute( NTFS_FILE_RECORD &rec, NTFS_ATTR_TYPE type ) { local int i; while( exists(rec.attribute[i]) ) { if( rec.attribute[i].header.Type == type ) return i; i++; } return -1; // not found } // NTFS Directory typedef struct { local int DriveNum = parentof(parentof(this)).DriveNum; // keep track of which drive this belongs to local int dirRoot = NTFS_FindAttribute( parentof(this), INDEX_ROOT ); local int dirAllocation = NTFS_FindAttribute( parentof(this), INDEX_ALLOCATION ); local UQUAD previousFile = 0xFFFFFFFFFFFFFFFFL; local int i = 0; local int64 start = FTell(); while( 1 ) { // Iterate through the b-tree index and create the list of files if( dirAllocation < 0 ) dirAllocation = 0; // no allocation - everything stored in INDEX_ROOT previousFile = NTFS_ListFiles( parentof(this).attribute[dirRoot].indexEntry[i], attribute[dirAllocation], DriveNum, 0, previousFile ); if( parentof(this).attribute[dirRoot].indexEntry[i].header.IsLastEntry ) break; i++; } FSeek( start + 1024 ); } NTFS_DIRECTORY ; // NTFS File Record typedef struct { local int64 start = FTell(); // Header info struct FILE_RECORD_HEADER_NTFS { BYTE Magic[4]; WORD UpdateSequenceOffset; WORD UpdateSequenceSize; UQUAD LogFileSequenceNumber; WORD SequenceNumber; WORD HardLinkCount; WORD FirstAttributeOffset; WORD InUse : 1; WORD IsDirectory : 1; DWORD UsedSize; DWORD AllocateSize; UQUAD FileReference; WORD NextAttributeID; WORD Align; DWORD MFTRecordNumber; UBYTE EndTag[2]; UBYTE FixupArray[6]; } header; // Check header if( header.Magic != "FILE" ) return; // Read list of attributes while( (ReadUInt(FTell()) != 0xffffffff) && (FTell() - start < 1024) ) NTFS_ATTRIBUTE attribute; // Add padding if necessary if( FTell() - start < 1024 ) UBYTE padding[ 1024 - (FTell() - start) ]; // Check if this is a directory local int attrRoot = NTFS_FindAttribute( this, INDEX_ROOT ); if( (attrRoot >= 0) && (header.IsDirectory) ) { FSeek( start ); NTFS_DIRECTORY sub_dir; } // Jump to the end of the record FSeek( start + 1024 ); } NTFS_FILE_RECORD ; //, size=1024>; // Display file name beside the file record wstring ReadNTFS_FILE_RECORD( NTFS_FILE_RECORD &fr ) { string name; local int attrNum = NTFS_FindAttribute( fr, FILE_NAME ); if( attrNum != -1 ) { // Skip short DOS names if( (fr.attribute[attrNum].fileName.Namespace == NAMESPACE_DOS) && exists( fr.attribute[attrNum+1].fileName.FileName ) ) attrNum++; name = fr.attribute[attrNum].fileName.FileName; // Check for hidden, system local int hidden = fr.attribute[attrNum].fileName.Flags.Hidden; local int system = fr.attribute[attrNum].fileName.Flags.System; if( hidden || system ) { name += " ("; if( hidden ) { name += "Hidden"; if( system ) name += " "; } if( system ) name += "System"; name += ")"; } // Check if this is a directory if( fr.header.IsDirectory ) name = "/" + name; // Check if this is deleted if( !fr.header.InUse ) name = "**Erased name:" + name; } return name; } // NTFS Master File Table (MFT) typedef struct { local int DriveNum = parentof(this).DriveNum; // keep track of which drive this belongs to NTFS_FILE_RECORD mft[16] ; } NTFS_MASTER_FILE_TABLE ; // Display the volume label beside the master file table string ReadNTFS_MASTER_FILE_TABLE( NTFS_MASTER_FILE_TABLE &mft ) { if( exists( mft.mft[3] ) ) { local int volAttr = NTFS_FindAttribute( mft.mft[3], VOLUME_NAME ); if( volAttr != -1 ) return mft.mft[3].attribute[ volAttr ].volumeName.VolumeName; } return ""; } // Define an NTFS Drive typedef struct { local int DriveNum = NumDrives++; // keep track of the index of this drive local int64 DriveStart = FTell(); local DWORD ClusterSize; // NTFS Boot sector NTFS_BOOTSECTOR boot_ntfs ; // Master File Table (MFT) ClusterSize = boot_ntfs.bpb_ntfs.BytesPerSector * boot_ntfs.bpb_ntfs.SectorsPerCluster; FSeek( DriveStart + boot_ntfs.bpb_ntfs.LogicalClusterMFT * ClusterSize ); NTFS_MASTER_FILE_TABLE mft; // Root directory - jump to '.' in the master file table // this is repeated from the mft but makes it easier to find here FSeek( startof(mft) + 5*1024 ); NTFS_FILE_RECORD root_dir; } NTFS_DRIVE ; //################################################################ // Unknown Drives //################################################################ // For unknown drive - define a union of known headers // and hope it is one of those typedef union { local int DriveNum = NumDrives++ ; // keep track of the index of this drive struct MASTER_BOOT_RECORD mbr; struct FAT16_BOOTSECTOR boot_fat16; struct FAT32_BOOTSECTOR boot_fat32; struct NTFS_BOOTSECTOR boot_ntfs; } DRIVE_NOT_SUPPORTED; //################################################################ // Detect drive type //################################################################ // Function to detect different types of drives int AutoDetectDrive( int SystemID ) { local int i; local int64 startPos; // Detect drive by using the SystemID from the partition table if( SystemID != -1 ) { if( (SystemID==FAT_16_INF32MB) || (SystemID==FAT_16) || (SystemID==EXT_FAT16_INT13) ) { // Found FAT16 drive FAT16_DRIVE drive; return true; } else if( (SystemID==PRI_FAT32_INT13) || (SystemID==EXT_FAT32_INT13) ) { // Found FAT32 drive FAT32_DRIVE drive; return true; } else if( SystemID==NTFS_HPFS ) { // Found NTFS drive NTFS_DRIVE drive; return true; } else if( (SystemID==EXTENDED) || (SystemID==WIN95_EXT_PARTITION) || (SystemID==DRDOS_SECURED_EXTENDED) ) { // Extended partition startPos = FTell(); EXTENDED_PARTITION extended ; // Find the extended drives for( i = 0; i < 2; i++ ) { if( extended.partitions[i].SystemID != EMPTY ) { FSeek( startPos + extended.partitions[i].RelativeSector*(INT64)512 ); if( !AutoDetectDrive( extended.partitions[i].SystemID ) ) DRIVE_NOT_SUPPORTED drive; } } return true; } } // Detect by reading data from the header startPos = FTell(); if( ReadString( startPos + 0x36, 8 ) == "FAT16 " ) { // Found FAT16 drive FAT16_DRIVE drive; return true; } else if( ReadString( startPos + 0x52, 8 ) == "FAT32 " ) { // Found FAT32 drive FAT32_DRIVE drive; return true; } else if( ReadString( startPos + 0x3, 8 ) == "NTFS " ) { // Found NTFS drive NTFS_DRIVE drive; return true; } return false; // not found } // Check for EndOfSectorMarker (present on MBR/FAT16/FAT32/NTFS) if( ReadUShort(510)!=0xAA55 ) { Warning( "File/Disk is not a valid MBR/FAT16/FAT32/NTFS. Template stopped." ); return -1; } // Check for different drive types if( AutoDetectDrive(-1) == false ) { // Auto detect the MBR local unsigned short mbr_boot_ok=0; local unsigned short i; local uchar BootIndicator, SystemID; for(i=0;i<4;i++) { // Check BootIndicator and SystemID BootIndicator = ReadUByte( 0x1BE + i*10h ); SystemID = ReadUByte( 0x1C2 + i*10h ); if( (BootIndicator==SYSTEM_PARTITION || BootIndicator==NOBOOT) && (SystemID!=EMPTY) ) { if(mbr_boot_ok==0) { MASTER_BOOT_RECORD boot_mbr ; mbr_boot_ok=1; } // Jump to Partition FSeek(boot_mbr.partitions[i].RelativeSector*(INT64)512); if( !AutoDetectDrive( SystemID ) ) DRIVE_NOT_SUPPORTED drive; } } // Could not find MBR - drive is not supported if( !mbr_boot_ok ) DRIVE_NOT_SUPPORTED drive; }