>From 952b21e8da57938da65596a5aec047375b60dd87 Mon Sep 17 00:00:00 2001 From: Joshua Roys Date: Wed, 26 Oct 2011 12:30:06 -0400 Subject: [PATCH] Add support for Phoenix FFV layouts Signed-off-by: Joshua Roys --- phoenix.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 268 insertions(+), 13 deletions(-) diff --git a/phoenix.c b/phoenix.c index 2da84b7..b1dedf1 100644 --- a/phoenix.c +++ b/phoenix.c @@ -66,6 +66,22 @@ PhoenixModuleNames[] = { {'J', "SmartCardPAS"}, }; +struct PhoenixID { + char Name[6]; + uint16_t Flags; + uint16_t Length; +}; + +struct PhoenixFFVModule { + uint8_t Signature; + uint8_t Flags; + uint16_t Checksum; + uint16_t LengthLo; + uint8_t LengthHi; + uint8_t Compression; + char Name[16]; +}; + static char * PhoenixModuleNameGet(char Id) { @@ -238,6 +254,244 @@ PhoenixModule(unsigned char *BIOSImage, int BIOSLength, int Offset) return le32toh(Module->Previous); } +static int +PhoenixExtractFFV(unsigned char *BIOSImage, int BIOSLength, int Offset) +{ + struct PhoenixFFVCompressionHeader { + uint16_t TotalLengthLo; + uint8_t TotalLengthHi; + uint8_t Unk1; + + uint16_t PackedLenLo; + uint8_t PackedLenHi; + uint8_t Unk2; + + uint16_t RealLenLo; + uint8_t RealLenHi; + uint8_t Unk3; + } *CompHeader; + struct PhoenixFFVModule *Module; + char Name[16], filename[24]; + char *ModuleName; + int fd; + uint32_t Length, PackedLen, RealLen; + unsigned char *RealData; + + Module = (struct PhoenixFFVModule *) (BIOSImage + Offset); + + if (Module->Signature != 0xF8) { + /* ignore and move on to the next byte... */ + return 1; + } + + Length = (le16toh(Module->LengthHi) << 16) | Module->LengthLo; + if ((Offset + ((le16toh(Module->LengthHi) << 16) | Module->LengthLo)) > BIOSLength) { + fprintf(stderr, "Error: Module overruns buffer at 0x%05X\n", + Offset); + return 1; + } + + if (Module->Compression == 0xF0) { + strcpy(Name, "GAP"); + filename[0] = '\0'; + } + else { + /* get rid of the pesky 0xFF in the middle of the name */ + memcpy(Name, Module->Name, 8); + memcpy(Name + 8, Module->Name + 9, 7); + Name[15] = '\0'; + + if (Name[0] == '_') { + ModuleName = PhoenixModuleNameGet(Name[1]); + if (ModuleName) { + snprintf(filename, sizeof(filename), "%s_%c%c.rom", ModuleName, Name[2], Name[3]); + } + else { + snprintf(filename, sizeof(filename), "%s.rom", Name); + } + } + else { + strncpy(filename, Name, sizeof(filename)); + } + } + + printf("\t%-15s (%08X-%08X) %08X %02X %02X %s\n", + Name, Offset, Offset + Length, Length, Module->Flags, Module->Compression, filename + ); + + switch (Module->Compression) { + case 0xF0: + break; + + case 0x02: + if (Name[1] == 'G') { + break; + } + else { + CompHeader = (struct PhoenixFFVCompressionHeader *) (BIOSImage + Offset + 0x18); + } + /* some blocks have a (8 byte?) header we need to skip */ + if (CompHeader->TotalLengthLo != Length - 0x18) { + CompHeader = (struct PhoenixFFVCompressionHeader *) + ((unsigned char *)CompHeader + CompHeader->TotalLengthLo); + } + PackedLen = (CompHeader->PackedLenHi << 16) | CompHeader->PackedLenLo; + RealLen = (CompHeader->RealLenHi << 16) | CompHeader->RealLenLo; + RealData = MMapOutputFile(filename, RealLen); + if (!RealData) { + fprintf(stderr, "Failed to mmap file for uncompressed data.\n"); + break; + } + LH5Decode((unsigned char *)CompHeader + sizeof(struct PhoenixFFVCompressionHeader), + PackedLen, RealData, RealLen); + munmap(RealData, RealLen); + break; + + case 0x01: + fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", filename, strerror(errno)); + break; + } + write(fd, BIOSImage + Offset + 0x18, Length - 0x18); + close(fd); + break; + + default: + fprintf(stderr, "\t\tUnsupported compression type!\n"); + break; + } + + return Length; +} + +void +PhoenixFFVDirectory(unsigned char *BIOSImage, int BIOSLength, int Offset) +{ + struct PhoenixVolumeDirEntry { + /* these are stored little endian */ + uint32_t guid1; + uint16_t guid2; + uint16_t guid3; + /* these are big endian */ + uint16_t guid4; + /*uint48_t guid5;*/ + uint16_t guid5; + uint32_t guid6; + + uint32_t Base; + uint32_t Length; + }; + struct PhoenixVolumeDir { + uint16_t Unk1; + uint16_t Unk2; + uint32_t Length; + struct PhoenixVolumeDirEntry Modules[]; + } *Volume; + struct PhoenixFFVModule *Module; + char Name[16], guid[37]; + int fd, HoleNum = 0; + uint32_t Base, Length, NumModules, ModNum; + + Module = (struct PhoenixFFVModule *) (BIOSImage + Offset); + Volume = (struct PhoenixVolumeDir *) (BIOSImage + Offset + 0x18); + + if (Module->Signature != 0xF8) { + fprintf(stderr, "Error: Invalid module signature at 0x%05X\n", + Offset); + return; + } + + Length = (le16toh(Module->LengthHi) << 16) | Module->LengthLo; + if ((Offset + Length) > BIOSLength) { + fprintf(stderr, "Error: Module overruns buffer at 0x%05X\n", + Offset); + return; + } + + /* get rid of the pesky 0xFF in the middle of the name */ + memcpy(Name, Module->Name, 8); + memcpy(Name + 8, Module->Name + 9, 7); + Name[15] = '\0'; + if (strcmp(Name, "volumedir.bin2")) { + fprintf(stderr, "FFV points to something other than volumedir.bin2: %s\n", Name); + return; + } + + NumModules = (Volume->Length - 8) / sizeof(struct PhoenixVolumeDirEntry); + printf("FFV modules: %u\n", NumModules); + + for (ModNum = 0; ModNum < NumModules; ModNum++) + { + sprintf(guid, "%08X-%04X-%04X-%04X-%04X%08X", + le32toh(Volume->Modules[ModNum].guid1), + le16toh(Volume->Modules[ModNum].guid2), + le16toh(Volume->Modules[ModNum].guid3), + be16toh(Volume->Modules[ModNum].guid4), + be16toh(Volume->Modules[ModNum].guid5), + be32toh(Volume->Modules[ModNum].guid6) + ); + Base = Volume->Modules[ModNum].Base & (BIOSLength - 1); + Length = Volume->Modules[ModNum].Length; + printf("[%2u]: (%08X-%08X) %s\n", + ModNum, Base, Base + Length, guid + ); + + if (!strcmp(guid, "FED91FBA-D37B-4EEA-8729-2EF29FB37A78")) { + /* FFV modules */ + Offset = Base; + while (Offset < Base + Length) { + Offset += PhoenixExtractFFV(BIOSImage, BIOSLength, Offset); + } + } + else if (!strcmp(guid, "FD21E8FD-2525-4A95-BB90-47EC5763FF9E")) { + /* Extended System Configuration Data (and similar?) */ + printf("\tESCD\n"); + fd = open("ESCD.bin", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open ESCD.bin: %s\n\n", strerror(errno)); + continue; + } + write(fd, BIOSImage + Base, Length); + close(fd); + } + else if (!strcmp(guid, "F6AE0F63-5F8C-4316-A2EA-76B9AF762756")) { + /* Raw BIOS code */ + printf("\tHole (raw code)\n"); + snprintf(Name, sizeof(Name), "hole_%02x.bin", HoleNum++); + fd = open(Name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Error: unable to open %s: %s\n\n", Name, strerror(errno)); + continue; + } + write(fd, BIOSImage + Base, Length); + close(fd); + } + else { + fprintf(stderr, "\tUnknown FFV module GUID!\n"); + } + } + + return; +} + +Bool +PhoenixFFV(unsigned char *BIOSImage, int BIOSLength, struct PhoenixID *FFV) +{ + uint32_t Offset; + + Offset = le32toh(*((uint32_t *) (((char *) FFV) + 0xA))) & (BIOSLength - 1); + + if (!Offset) { + fprintf(stderr, "BCPFFV module offset is NULL.\n"); + return FALSE; + } + + PhoenixFFVDirectory(BIOSImage, BIOSLength, Offset); + + return TRUE; +} + /* * */ @@ -245,11 +499,7 @@ Bool PhoenixExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, uint32_t Offset1, uint32_t BCPSegmentOffset) { - struct PhoenixID { - char Name[6]; - uint16_t Flags; - uint16_t Length; - } *ID; + struct PhoenixID *ID, *SYS = NULL, *FFV = NULL; uint32_t Offset; printf("Found Phoenix BIOS \"%s\"\n", (char *) (BIOSImage + Offset1)); @@ -263,10 +513,12 @@ PhoenixExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, ID->Name[4], ID->Name[5], le16toh(ID->Flags), le16toh(ID->Length)); #endif if (!strncmp(ID->Name, "BCPSYS", 6)) - break; + SYS = ID; + else if (!strncmp(ID->Name, "BCPFFV", 6)) + FFV = ID; } - if (!ID->Name[0] || ((void *) ID >= (void *) (BIOSImage + BIOSLength))) { + if (!SYS) { fprintf(stderr, "Error: Failed to locate BCPSYS offset.\n"); return FALSE; } @@ -275,21 +527,24 @@ PhoenixExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset, { char Date[9], Time[9], Version[9]; - strncpy(Date, ((char *) ID) + 0x0F, 8); + strncpy(Date, ((char *) SYS) + 0x0F, 8); Date[8] = 0; - strncpy(Time, ((char *) ID) + 0x18, 8); + strncpy(Time, ((char *) SYS) + 0x18, 8); Time[8] = 0; - strncpy(Version, ((char *) ID) + 0x37, 8); + strncpy(Version, ((char *) SYS) + 0x37, 8); Version[8] = 0; printf("Version \"%s\", created on %s at %s.\n", Version, Date, Time); } - Offset = le32toh(*((uint32_t *) (((char *) ID) + 0x77))); + Offset = le32toh(*((uint32_t *) (((char *) SYS) + 0x77))); Offset &= (BIOSLength - 1); if (!Offset) { - fprintf(stderr, "Error: retrieved invalid Modules offset.\n"); - return FALSE; + fprintf(stderr, "BCPSYS module offset is NULL.\n"); + if (!FFV) { + return FALSE; + } + return PhoenixFFV(BIOSImage, BIOSLength, FFV); } while (Offset) { -- 1.7.1