Added decoding of El Torito boot catalog.

This commit is contained in:
2017-10-09 00:32:17 +01:00
parent 97b4ebe76a
commit 42d8ff32ce
3 changed files with 285 additions and 5 deletions

View File

@@ -37,6 +37,8 @@ namespace DiscImageChef.Filesystems.ISO9660
{
readonly string IsoMagic = "CD001";
readonly string HighSierraMagic = "CDROM";
const ushort ElToritoMagic = 0xAA55;
const int ElToritoEntrySize = 32;
[Flags]
enum FileFlags : byte
@@ -76,5 +78,39 @@ namespace DiscImageChef.Filesystems.ISO9660
ISO1539 = 1,
ControlContained = 2,
}
enum ElToritoIndicator : byte
{
Header = 1,
Extension = 0x44,
Bootable = 0x88,
MoreHeaders = 0x90,
LastHeader = 0x91
}
enum ElToritoPlatform : byte
{
x86 = 0,
PowerPC = 1,
Macintosh = 2
}
enum ElToritoEmulation : byte
{
None = 0,
Md2hd = 1,
Mf2hd = 2,
Mf2ed = 3,
Hdd = 4
}
[Flags]
enum ElToritoFlags : byte
{
Reserved = 0x10,
Continued = 0x20,
ATAPI = 0x40,
SCSI = 0x08
}
}
}

View File

@@ -93,6 +93,7 @@ namespace DiscImageChef.Filesystems.ISO9660
PrimaryVolumeDescriptor? jolietvd = null;
BootRecord? bvd = null;
HighSierraPrimaryVolumeDescriptor? hsvd = null;
ElToritoBootRecord? torito = null;
// ISO9660 is designed for 2048 bytes/sector devices
if(imagePlugin.GetSectorSize() < 2048)
@@ -143,7 +144,7 @@ namespace DiscImageChef.Filesystems.ISO9660
switch(VDType)
{
case 0: // TODO
case 0:
{
bvd = new BootRecord();
IntPtr ptr = Marshal.AllocHGlobal(2048);
@@ -154,7 +155,14 @@ namespace DiscImageChef.Filesystems.ISO9660
BootSpec = "Unknown";
if(CurrentEncoding.GetString(bvd.Value.system_id).Substring(0, 23) == "EL TORITO SPECIFICATION")
{
BootSpec = "El Torito";
torito = new ElToritoBootRecord();
ptr = Marshal.AllocHGlobal(2048);
Marshal.Copy(vd_sector, hs_off, ptr, 2048 - hs_off);
torito = (ElToritoBootRecord)Marshal.PtrToStructure(ptr, typeof(ElToritoBootRecord));
Marshal.FreeHGlobal(ptr);
}
break;
}
@@ -278,9 +286,9 @@ namespace DiscImageChef.Filesystems.ISO9660
ISOMetadata.AppendLine("This is a Sega Dreamcast disc.");
ISOMetadata.AppendLine(Decoders.Sega.Dreamcast.Prettify(Dreamcast));
}
ISOMetadata.AppendLine("--------------------------------");
ISOMetadata.AppendLine("------------------------------");
ISOMetadata.AppendLine("VOLUME DESCRIPTOR INFORMATION:");
ISOMetadata.AppendLine("--------------------------------");
ISOMetadata.AppendLine("------------------------------");
ISOMetadata.AppendFormat("System identifier: {0}", decodedVD.SystemIdentifier).AppendLine();
ISOMetadata.AppendFormat("Volume identifier: {0}", decodedVD.VolumeIdentifier).AppendLine();
ISOMetadata.AppendFormat("Volume set identifier: {0}", decodedVD.VolumeSetIdentifier).AppendLine();
@@ -304,9 +312,9 @@ namespace DiscImageChef.Filesystems.ISO9660
if(jolietvd != null)
{
ISOMetadata.AppendLine("---------------------------------------");
ISOMetadata.AppendLine("-------------------------------------");
ISOMetadata.AppendLine("JOLIET VOLUME DESCRIPTOR INFORMATION:");
ISOMetadata.AppendLine("---------------------------------------");
ISOMetadata.AppendLine("-------------------------------------");
ISOMetadata.AppendFormat("System identifier: {0}", decodedJolietVD.SystemIdentifier).AppendLine();
ISOMetadata.AppendFormat("Volume identifier: {0}", decodedJolietVD.VolumeIdentifier).AppendLine();
ISOMetadata.AppendFormat("Volume set identifier: {0}", decodedJolietVD.VolumeSetIdentifier).AppendLine();
@@ -328,6 +336,166 @@ namespace DiscImageChef.Filesystems.ISO9660
ISOMetadata.AppendFormat("Volume has always been effective.").AppendLine();
}
if(torito != null)
{
vd_sector = imagePlugin.ReadSector(torito.Value.catalog_sector + partition.Start);
Checksums.SHA1Context sha1Ctx = new Checksums.SHA1Context();
sha1Ctx.Init();
byte[] boot_image;
int torito_off = 0;
if(vd_sector[torito_off] != 1)
goto exit_torito;
ElToritoValidationEntry valentry = new ElToritoValidationEntry();
IntPtr ptr = Marshal.AllocHGlobal(ElToritoEntrySize);
Marshal.Copy(vd_sector, torito_off, ptr, ElToritoEntrySize);
valentry = (ElToritoValidationEntry)Marshal.PtrToStructure(ptr, typeof(ElToritoValidationEntry));
Marshal.FreeHGlobal(ptr);
if(valentry.signature != ElToritoMagic)
goto exit_torito;
torito_off += ElToritoEntrySize;
ElToritoInitialEntry initial_entry = new ElToritoInitialEntry();
ptr = Marshal.AllocHGlobal(ElToritoEntrySize);
Marshal.Copy(vd_sector, torito_off, ptr, ElToritoEntrySize);
initial_entry = (ElToritoInitialEntry)Marshal.PtrToStructure(ptr, typeof(ElToritoInitialEntry));
Marshal.FreeHGlobal(ptr);
initial_entry.boot_type = (ElToritoEmulation)((byte)initial_entry.boot_type & 0xF);
boot_image = imagePlugin.ReadSectors(initial_entry.load_rba + partition.Start, initial_entry.sector_count);
ISOMetadata.AppendLine("----------------------");
ISOMetadata.AppendLine("EL TORITO INFORMATION:");
ISOMetadata.AppendLine("----------------------");
ISOMetadata.AppendLine("Initial entry:");
ISOMetadata.AppendFormat("\tDeveloper ID: {0}", CurrentEncoding.GetString(valentry.developer_id)).AppendLine();
if(initial_entry.bootable == ElToritoIndicator.Bootable)
{
ISOMetadata.AppendFormat("\tBootable on {0}", valentry.platform_id).AppendLine();
ISOMetadata.AppendFormat("\tBootable image starts at sector {0} and runs for {1} sectors", initial_entry.load_rba, initial_entry.sector_count).AppendLine();
if(valentry.platform_id == ElToritoPlatform.x86)
ISOMetadata.AppendFormat("\tBootable image will be loaded at segment {0:X4}h", initial_entry.load_seg == 0 ? 0x7C0 : initial_entry.load_seg).AppendLine();
else
ISOMetadata.AppendFormat("\tBootable image will be loaded at 0x{0:X8}", (uint)initial_entry.load_seg * 10).AppendLine();
switch(initial_entry.boot_type)
{
case ElToritoEmulation.None:
ISOMetadata.AppendLine("\tImage uses no emulation");
break;
case ElToritoEmulation.Md2hd:
ISOMetadata.AppendLine("\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy");
break;
case ElToritoEmulation.Mf2hd:
ISOMetadata.AppendLine("\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy");
break;
case ElToritoEmulation.Mf2ed:
ISOMetadata.AppendLine("\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy");
break;
default:
ISOMetadata.AppendFormat("\tImage uses unknown emulation type {0}", (byte)initial_entry.boot_type).AppendLine();
break;
}
ISOMetadata.AppendFormat("\tSystem type: 0x{0:X2}", initial_entry.system_type).AppendLine();
ISOMetadata.AppendFormat("\tBootable image's SHA1: {0}", sha1Ctx.Data(boot_image, out byte[] hash)).AppendLine();
}
else
ISOMetadata.AppendLine("\tNot bootable");
torito_off += ElToritoEntrySize;
int section_counter = 2;
while(torito_off < vd_sector.Length && (vd_sector[torito_off] == (byte)ElToritoIndicator.Header || vd_sector[torito_off] == (byte)ElToritoIndicator.LastHeader))
{
ElToritoSectionHeaderEntry section_header = new ElToritoSectionHeaderEntry();
ptr = Marshal.AllocHGlobal(ElToritoEntrySize);
Marshal.Copy(vd_sector, torito_off, ptr, ElToritoEntrySize);
section_header = (ElToritoSectionHeaderEntry)Marshal.PtrToStructure(ptr, typeof(ElToritoSectionHeaderEntry));
Marshal.FreeHGlobal(ptr);
torito_off += ElToritoEntrySize;
ISOMetadata.AppendFormat("Boot section {0}:", section_counter);
ISOMetadata.AppendFormat("\tSection ID: {0}", CurrentEncoding.GetString(section_header.identifier)).AppendLine();
for(int entry_counter = 1; entry_counter <= section_header.entries && torito_off < vd_sector.Length; entry_counter++)
{
ElToritoSectionEntry section_entry = new ElToritoSectionEntry();
ptr = Marshal.AllocHGlobal(ElToritoEntrySize);
Marshal.Copy(vd_sector, torito_off, ptr, ElToritoEntrySize);
section_entry = (ElToritoSectionEntry)Marshal.PtrToStructure(ptr, typeof(ElToritoSectionEntry));
Marshal.FreeHGlobal(ptr);
torito_off += ElToritoEntrySize;
ISOMetadata.AppendFormat("\tEntry {0}:", entry_counter);
if(section_entry.bootable == ElToritoIndicator.Bootable)
{
boot_image = imagePlugin.ReadSectors(section_entry.load_rba + partition.Start, section_entry.sector_count);
ISOMetadata.AppendFormat("\t\tBootable on {0}", section_header.platform_id).AppendLine();
ISOMetadata.AppendFormat("\t\tBootable image starts at sector {0} and runs for {1} sectors", section_entry.load_rba, section_entry.sector_count).AppendLine();
if(valentry.platform_id == ElToritoPlatform.x86)
ISOMetadata.AppendFormat("\t\tBootable image will be loaded at segment {0:X4}h", section_entry.load_seg == 0 ? 0x7C0 : section_entry.load_seg).AppendLine();
else
ISOMetadata.AppendFormat("\t\tBootable image will be loaded at 0x{0:X8}", (uint)section_entry.load_seg * 10).AppendLine();
switch((ElToritoEmulation)((byte)section_entry.boot_type & 0xF))
{
case ElToritoEmulation.None:
ISOMetadata.AppendLine("\t\tImage uses no emulation");
break;
case ElToritoEmulation.Md2hd:
ISOMetadata.AppendLine("\t\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy");
break;
case ElToritoEmulation.Mf2hd:
ISOMetadata.AppendLine("\t\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy");
break;
case ElToritoEmulation.Mf2ed:
ISOMetadata.AppendLine("\t\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy");
break;
default:
ISOMetadata.AppendFormat("\t\tImage uses unknown emulation type {0}", (byte)initial_entry.boot_type).AppendLine();
break;
}
ISOMetadata.AppendFormat("\t\tSelection criteria type: {0}", section_entry.selection_criteria_type).AppendLine();
ISOMetadata.AppendFormat("\t\tSystem type: 0x{0:X2}", section_entry.system_type).AppendLine();
ISOMetadata.AppendFormat("\t\tBootable image's SHA1: {0}", sha1Ctx.Data(boot_image, out byte[] hash)).AppendLine();
}
else
ISOMetadata.AppendLine("\t\tNot bootable");
ElToritoFlags flags = (ElToritoFlags)((byte)section_entry.boot_type & 0xF0);
if(flags.HasFlag(ElToritoFlags.ATAPI))
ISOMetadata.AppendLine("\t\tImage contains ATAPI drivers");
if(flags.HasFlag(ElToritoFlags.SCSI))
ISOMetadata.AppendLine("\t\tImage contains SCSI drivers");
if(flags.HasFlag(ElToritoFlags.Continued))
{
while(true && torito_off < vd_sector.Length)
{
ElToritoSectionEntryExtension section_extension = new ElToritoSectionEntryExtension();
ptr = Marshal.AllocHGlobal(ElToritoEntrySize);
Marshal.Copy(vd_sector, torito_off, ptr, ElToritoEntrySize);
section_extension = (ElToritoSectionEntryExtension)Marshal.PtrToStructure(ptr, typeof(ElToritoSectionEntryExtension));
Marshal.FreeHGlobal(ptr);
torito_off += ElToritoEntrySize;
if(!section_extension.extension_flags.HasFlag(ElToritoFlags.Continued))
break;
}
}
}
if(section_header.header_id == ElToritoIndicator.LastHeader)
break;
}
}
exit_torito:
xmlFSType.Type = HighSierra ? "High Sierra Format" : "ISO9660";
if(jolietvd != null)

View File

@@ -181,6 +181,22 @@ namespace DiscImageChef.Filesystems.ISO9660
public byte[] boot_use;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ElToritoBootRecord
{
public byte type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[] id;
public byte version;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] system_id;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] boot_id;
public uint catalog_sector;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1974)]
public byte[] boot_use;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct PartitionDescriptor
{
@@ -272,6 +288,66 @@ namespace DiscImageChef.Filesystems.ISO9660
public ushort app_use_len_be;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ElToritoValidationEntry
{
public ElToritoIndicator header_id;
public ElToritoPlatform platform_id;
public ushort reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)]
public byte[] developer_id;
public ushort checksum;
public ushort signature;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ElToritoInitialEntry
{
public ElToritoIndicator bootable;
public ElToritoEmulation boot_type;
public ushort load_seg;
public byte system_type;
public byte reserved1;
public ushort sector_count;
public uint load_rba;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] reserved2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ElToritoSectionHeaderEntry
{
public ElToritoIndicator header_id;
public ElToritoPlatform platform_id;
public ushort entries;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)]
public byte[] identifier;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ElToritoSectionEntry
{
public ElToritoIndicator bootable;
public ElToritoEmulation boot_type;
public ushort load_seg;
public byte system_type;
public byte reserved1;
public ushort sector_count;
public uint load_rba;
public byte selection_criteria_type;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)]
public byte[] selection_criterias;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ElToritoSectionEntryExtension
{
public ElToritoIndicator extension_indicator;
public ElToritoFlags extension_flags;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
public byte[] selection_criterias;
}
struct DecodedVolumeDescriptor
{
public string SystemIdentifier;