add FFSv3 support with large files and large sections

This commit is contained in:
Alex Matrosov 2017-12-10 16:54:40 -08:00
parent 75225ecc28
commit 73019876cf
6 changed files with 212 additions and 133 deletions

View File

@ -1,6 +1,6 @@
/* uefipatch_main.cpp
Copyright (c) 2015, Nikolaj Schlej. All rights reserved.
Copyright (c) 2017, LongSoft. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
@ -19,8 +19,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
a.setOrganizationName("CodeRush");
a.setOrganizationDomain("coderush.me");
a.setOrganizationName("LongSoft");
a.setOrganizationDomain("longsoft.me");
a.setApplicationName("UEFIPatch");
UEFIPatch w;
@ -31,7 +31,7 @@ int main(int argc, char *argv[])
result = w.patchFromFile(a.arguments().at(1));
}
else {
std::cout << "UEFIPatch 0.3.9 - UEFI image file patching utility" << std::endl << std::endl <<
std::cout << "UEFIPatch 0.3.10 - UEFI image file patching utility" << std::endl << std::endl <<
"Usage: UEFIPatch image_file" << std::endl << std::endl <<
"Patches will be read from patches.txt file\n";
return ERR_SUCCESS;

23
ffs.cpp
View File

@ -30,7 +30,10 @@ const QVector<QByteArray> FFSv3Volumes =
const UINT8 ffsAlignmentTable[] =
{ 0, 4, 7, 9, 10, 12, 15, 16 };
UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize)
const UINT8 ffsAlignment2Table[] =
{ 17, 18, 19, 20, 21, 22, 23, 24 };
UINT8 calculateSum8(const UINT8* buffer, UINT32 bufferSize)
{
if (!buffer)
return 0;
@ -40,7 +43,15 @@ UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize)
while (bufferSize--)
counter += buffer[bufferSize];
return (UINT8)0x100 - counter;
return counter;
}
UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize)
{
if (!buffer)
return 0;
return (UINT8)0x100 - calculateSum8(buffer, bufferSize);
}
UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize)
@ -120,6 +131,8 @@ QString fileTypeToQString(const UINT8 type)
case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE: return QObject::tr("Volume image");
case EFI_FV_FILETYPE_COMBINED_SMM_DXE: return QObject::tr("Combined SMM/DXE");
case EFI_FV_FILETYPE_SMM_CORE: return QObject::tr("SMM core");
case EFI_FV_FILETYPE_SMM_STANDALONE: return QObject::tr("SMM standalone");
case EFI_FV_FILETYPE_SMM_CORE_STANDALONE: return QObject::tr("SMM core standalone");
case EFI_FV_FILETYPE_PAD: return QObject::tr("Pad");
default: return QObject::tr("Unknown");
};
@ -155,10 +168,10 @@ UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header)
if (!header)
return 0;
const bool extended = false;
/*if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) {
bool extended = false;
if (uint24ToUint32(header->Size) == EFI_SECTION2_IS_USED) {
extended = true;
}*/
}
switch (header->Type)
{

14
ffs.h
View File

@ -292,7 +292,7 @@ UINT8 Type;
UINT8 Attributes;
UINT8 Size[3]; // Set to 0xFFFFFF
UINT8 State;
UINT32 ExtendedSize;
UINT64 ExtendedSize;
} EFI_FFS_FILE_HEADER2;
// Standard data checksum, used if FFS_ATTRIB_CHECKSUM is clear
@ -314,6 +314,8 @@ UINT32 ExtendedSize;
#define EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE 0x0B
#define EFI_FV_FILETYPE_COMBINED_SMM_DXE 0x0C
#define EFI_FV_FILETYPE_SMM_CORE 0x0D
#define EFI_FV_FILETYPE_SMM_STANDALONE 0x0E
#define EFI_FV_FILETYPE_SMM_CORE_STANDALONE 0x0F
#define EFI_FV_FILETYPE_OEM_MIN 0xC0
#define EFI_FV_FILETYPE_OEM_MAX 0xDF
#define EFI_FV_FILETYPE_DEBUG_MIN 0xE0
@ -326,6 +328,7 @@ UINT32 ExtendedSize;
#define FFS_ATTRIB_TAIL_PRESENT 0x01 // Valid only for revision 1 volumes
#define FFS_ATTRIB_RECOVERY 0x02 // Valid only for revision 1 volumes
#define FFS_ATTRIB_LARGE_FILE 0x01 // Valid only for FFSv3 volumes
#define FFS_ATTRIB_DATA_ALIGNMENT2 0x02 // Valid only for revision 2 volumes
#define FFS_ATTRIB_FIXED 0x04
#define FFS_ATTRIB_DATA_ALIGNMENT 0x38
#define FFS_ATTRIB_CHECKSUM 0x40
@ -333,6 +336,9 @@ UINT32 ExtendedSize;
// FFS alignment table
extern const UINT8 ffsAlignmentTable[];
// Extended FFS alignment table
extern const UINT8 ffsAlignment2Table[];
// File states
#define EFI_FILE_HEADER_CONSTRUCTION 0x01
#define EFI_FILE_HEADER_VALID 0x02
@ -360,7 +366,9 @@ const QByteArray EFI_FFS_PAD_FILE_GUID
// FFS size conversion routines
extern VOID uint32ToUint24(UINT32 size, UINT8* ffsSize);
extern UINT32 uint24ToUint32(const UINT8* ffsSize);
// FFS file 8bit checksum calculation routine
// FFS file 8bit checksum calculation routines
extern UINT8 calculateSum8(const UINT8* buffer, UINT32 bufferSize);
extern UINT8 calculateChecksum8(const UINT8* buffer, UINT32 bufferSize);
//*****************************************************************************
@ -528,7 +536,7 @@ typedef EFI_COMMON_SECTION_HEADER2 EFI_FIRMWARE_VOLUME_IMAGE_SECTION2;
typedef EFI_COMMON_SECTION_HEADER EFI_USER_INTERFACE_SECTION;
typedef EFI_COMMON_SECTION_HEADER2 EFI_USER_INTERFACE_SECTION2;
//Section routines
// Section routines
extern UINT32 sizeOfSectionHeader(const EFI_COMMON_SECTION_HEADER* header);
//*****************************************************************************

View File

@ -287,15 +287,9 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & intelImage, QModelIndex & in
const FLASH_DESCRIPTOR_COMPONENT_SECTION* componentSection = (const FLASH_DESCRIPTOR_COMPONENT_SECTION*)calculateAddress8(descriptor, descriptorMap->ComponentBase);
// Check descriptor version by getting hardcoded value of FlashParameters.ReadClockFrequency
UINT8 descriptorVersion = 0;
if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Old descriptor
UINT8 descriptorVersion = 2; // Skylake+ by default
if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_20MHZ) // Old descriptor
descriptorVersion = 1;
else if (componentSection->FlashParameters.ReadClockFrequency == FLASH_FREQUENCY_17MHZ) // Skylake+ descriptor
descriptorVersion = 2;
else {
msg(tr("parseIntelImage: unknown descriptor version with ReadClockFrequency %1h").hexarg(componentSection->FlashParameters.ReadClockFrequency));
return ERR_INVALID_FLASH_DESCRIPTOR;
}
// ME region
QByteArray me;
@ -983,15 +977,16 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
else
headerSize = volumeHeader->HeaderLength;
// Sanity check after some new crazy MSI images
// Sanity check after some crazy MSI images
headerSize = ALIGN8(headerSize);
// Check for volume structure to be known
bool volumeIsUnknown = true;
// Check for FFS v2 volume
if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) {
volumeIsUnknown = false;
// Check for FFS v2/v3 volume
UINT8 subtype = Subtypes::UnknownVolume;
if (FFSv2Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))){
subtype = Subtypes::Ffs2Volume;
}
else if (FFSv3Volumes.contains(QByteArray::fromRawData((const char*)volumeHeader->FileSystemGuid.Data, sizeof(EFI_GUID)))) {
subtype = Subtypes::Ffs3Volume;
}
// Check attributes
@ -1063,10 +1058,10 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
// Add tree item
QByteArray header = volume.left(headerSize);
QByteArray body = volume.mid(headerSize, volumeSize - headerSize);
index = model->addItem(Types::Volume, volumeIsUnknown ? Subtypes::UnknownVolume : Subtypes::Ffs2Volume, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parent, mode);
index = model->addItem(Types::Volume, subtype, COMPRESSION_ALGORITHM_NONE, name, text, info, header, body, parent, mode);
// Show messages
if (volumeIsUnknown) {
if (subtype == Subtypes::UnknownVolume) {
msg(tr("parseVolume: unknown file system %1").arg(guidToQString(volumeHeader->FileSystemGuid)), index);
// Do not parse unknown volumes
return ERR_SUCCESS;
@ -1100,19 +1095,42 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
break;
}
result = getFileSize(volume, fileOffset, fileSize);
if (result)
return result;
QByteArray tempFile = volume.mid(fileOffset, sizeof(EFI_FFS_FILE_HEADER));
const EFI_FFS_FILE_HEADER* tempFileHeader = (const EFI_FFS_FILE_HEADER*)tempFile.constData();
UINT32 fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER);
fileSize = uint24ToUint32(tempFileHeader->Size);
if (volumeHeader->Revision > 1 && (tempFileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
// Check if it's possibly the latest file in the volume
if (volumeSize - fileOffset < sizeof(EFI_FFS_FILE_HEADER2)) {
// No files are possible after this point
// All the rest is either free space or non-UEFI data
QByteArray rest = volume.right(volumeSize - fileOffset);
if (rest.count(empty) == rest.size()) { // It's a free space
model->addItem(Types::FreeSpace, 0, COMPRESSION_ALGORITHM_NONE, tr("Volume free space"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index);
}
else { //It's non-UEFI data
QModelIndex dataIndex = model->addItem(Types::Padding, Subtypes::DataPadding, COMPRESSION_ALGORITHM_NONE, tr("Non-UEFI data"), "", tr("Full size: %1h (%2)").hexarg(rest.size()).arg(rest.size()), QByteArray(), rest, index);
msg(tr("parseVolume: non-UEFI data found in volume's free space"), dataIndex);
}
// Exit from loop
break;
}
// Check file size to be at least size of EFI_FFS_FILE_HEADER
if (fileSize < sizeof(EFI_FFS_FILE_HEADER)) {
fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER2);
tempFile = volume.mid(fileOffset, sizeof(EFI_FFS_FILE_HEADER2));
const EFI_FFS_FILE_HEADER2* tempFileHeader2 = (const EFI_FFS_FILE_HEADER2*)tempFile.constData();
fileSize = (UINT32)tempFileHeader2->ExtendedSize;
}
// Check file size to be at least size of the header
if (fileSize < fileHeaderSize) {
msg(tr("parseVolume: volume has FFS file with invalid size"), index);
return ERR_INVALID_FILE;
}
QByteArray file = volume.mid(fileOffset, fileSize);
QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER));
QByteArray header = file.left(fileHeaderSize);
// If we are at empty space in the end of volume
if (header.count(empty) == header.size()) {
// Check free space to be actually free
@ -1152,8 +1170,11 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
// Check file alignment
const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)header.constData();
UINT8 alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
UINT32 alignment = (UINT32)pow(2.0, alignmentPower);
if ((fileOffset + sizeof(EFI_FFS_FILE_HEADER)) % alignment)
if (volumeHeader->Revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT2))
alignmentPower = ffsAlignment2Table[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
UINT32 alignment = (UINT32)(1UL << alignmentPower);
if ((fileOffset + fileHeaderSize) % alignment)
msgUnalignedFile = true;
// Check file GUID
@ -1165,7 +1186,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
// Parse file
QModelIndex fileIndex;
result = parseFile(file, fileIndex, empty == '\xFF' ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index);
result = parseFile(file, fileIndex, volumeHeader->Revision, empty == '\xFF' ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index);
if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME)
msg(tr("parseVolume: FFS file parsing failed with error \"%1\"").arg(errorMessage(result)), index);
@ -1183,16 +1204,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, co
return ERR_SUCCESS;
}
UINT8 FfsEngine::getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize)
{
if ((UINT32)volume.size() < fileOffset + sizeof(EFI_FFS_FILE_HEADER))
return ERR_INVALID_VOLUME;
const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)(volume.constData() + fileOffset);
fileSize = uint24ToUint32(fileHeader->Size);
return ERR_SUCCESS;
}
UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity, const QModelIndex & parent, const UINT8 mode)
UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const UINT8 revision, const UINT8 erasePolarity, const QModelIndex & parent, const UINT8 mode)
{
bool msgInvalidHeaderChecksum = false;
bool msgInvalidDataChecksum = false;
@ -1200,43 +1212,32 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U
bool msgInvalidType = false;
// Populate file header
if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER))
return ERR_INVALID_FILE;
const EFI_FFS_FILE_HEADER* fileHeader = (const EFI_FFS_FILE_HEADER*)file.constData();
// Check file state
// Construct empty byte for this file
char empty = erasePolarity ? '\xFF' : '\x00';
// Check header checksum
// Get file header
QByteArray header = file.left(sizeof(EFI_FFS_FILE_HEADER));
QByteArray tempHeader = header;
EFI_FFS_FILE_HEADER* tempFileHeader = (EFI_FFS_FILE_HEADER*)(tempHeader.data());
tempFileHeader->IntegrityCheck.Checksum.Header = 0;
tempFileHeader->IntegrityCheck.Checksum.File = 0;
UINT8 calculated = calculateChecksum8((const UINT8*)tempFileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1);
if (fileHeader->IntegrityCheck.Checksum.Header != calculated)
if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
if ((UINT32)file.size() < sizeof(EFI_FFS_FILE_HEADER2))
return ERR_INVALID_FILE;
header = file.left(sizeof(EFI_FFS_FILE_HEADER2));
}
// Check header checksum
UINT8 calculatedHeader = 0x100 -(calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->IntegrityCheck.Checksum.Header - fileHeader->IntegrityCheck.Checksum.File - fileHeader->State);
if (fileHeader->IntegrityCheck.Checksum.Header != calculatedHeader)
msgInvalidHeaderChecksum = true;
// Check data checksum
// Data checksum must be calculated
if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) {
UINT32 bufferSize = file.size() - sizeof(EFI_FFS_FILE_HEADER);
// Exclude file tail from data checksum calculation
if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)
bufferSize -= sizeof(UINT16);
calculated = calculateChecksum8((const UINT8*)(file.constData() + sizeof(EFI_FFS_FILE_HEADER)), bufferSize);
if (fileHeader->IntegrityCheck.Checksum.File != calculated)
msgInvalidDataChecksum = true;
}
// Data checksum must be one of predefined values
else if (fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM && fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2)
msgInvalidDataChecksum = true;
// Get file body
QByteArray body = file.right(file.size() - sizeof(EFI_FFS_FILE_HEADER));
QByteArray body = file.mid(header.size());
// Check for file tail presence
QByteArray tail;
if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)
{
if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) {
//Check file tail;
tail = body.right(sizeof(UINT16));
UINT16 tailValue = *(UINT16*)tail.constData();
@ -1247,48 +1248,47 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U
body = body.left(body.size() - sizeof(UINT16));
}
// Check data checksum
// Data checksum must be calculated
UINT8 calculatedData = 0;
if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) {
calculatedData = calculateChecksum8((const UINT8*)body.constData(), body.size());
if (fileHeader->IntegrityCheck.Checksum.File != calculatedData)
msgInvalidDataChecksum = true;
}
// Data checksum must be one of predefined values
else if ((revision == 1 && fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM)
|| fileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM2)
msgInvalidDataChecksum = true;
// Parse current file by default
bool parseCurrentFile = true;
bool parseCurrentFile = false;
bool parseAsBios = false;
// Check file type
switch (fileHeader->Type)
{
switch (fileHeader->Type) {
case EFI_FV_FILETYPE_ALL:
parseAsBios = true;
break;
case EFI_FV_FILETYPE_RAW:
parseAsBios = true;
break;
case EFI_FV_FILETYPE_FREEFORM:
break;
case EFI_FV_FILETYPE_SECURITY_CORE:
break;
case EFI_FV_FILETYPE_PEI_CORE:
break;
case EFI_FV_FILETYPE_DXE_CORE:
break;
case EFI_FV_FILETYPE_PEIM:
break;
case EFI_FV_FILETYPE_DRIVER:
break;
case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER:
break;
case EFI_FV_FILETYPE_APPLICATION:
break;
case EFI_FV_FILETYPE_SMM:
break;
case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE:
break;
case EFI_FV_FILETYPE_COMBINED_SMM_DXE:
break;
case EFI_FV_FILETYPE_SMM_CORE:
break;
case EFI_FV_FILETYPE_SMM_STANDALONE:
case EFI_FV_FILETYPE_SMM_CORE_STANDALONE:
case EFI_FV_FILETYPE_PAD:
parseCurrentFile = true;
break;
default:
msgInvalidType = true;
parseCurrentFile = false;
};
// Check for empty file
@ -1310,25 +1310,27 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const U
else
name = parseAsNonEmptyPadFile ? tr("Non-empty pad-file") : tr("Pad-file");
info = tr("File GUID: %1\nType: %2h\nAttributes: %3h\nFull size: %4h (%5)\nHeader size: %6h (%7)\nBody size: %8h (%9)\nState: %10h")
info = tr("File GUID: %1\nType: %2h\nAttributes: %3h\nFull size: %4h (%5)\nHeader size: %6h (%7)\nBody size: %8h (%9)\nState: %10h\nHeader checksum: %11h\nData checksum: %12h")
.arg(guidToQString(fileHeader->Name))
.hexarg2(fileHeader->Type, 2)
.hexarg2(fileHeader->Attributes, 2)
.hexarg(header.size() + body.size() + tail.size()).arg(header.size() + body.size() + tail.size())
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size())
.hexarg2(fileHeader->State, 2);
.hexarg2(fileHeader->State, 2)
.hexarg2(fileHeader->IntegrityCheck.Checksum.Header, 2)
.hexarg2(fileHeader->IntegrityCheck.Checksum.File, 2);
// Add tree item
index = model->addItem(Types::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, parent, mode);
// Show messages
if (msgInvalidHeaderChecksum)
msg(tr("parseFile: invalid header checksum"), index);
msg(tr("parseFile: invalid header checksum %1h, should be %2h").hexarg2(fileHeader->IntegrityCheck.Checksum.Header, 2).hexarg2(calculatedHeader, 2), index);
if (msgInvalidDataChecksum)
msg(tr("parseFile: invalid data checksum"), index);
msg(tr("parseFile: invalid data checksum %1h, should be %2h").hexarg2(fileHeader->IntegrityCheck.Checksum.File, 2).hexarg2(calculatedData, 2), index);
if (msgInvalidTailValue)
msg(tr("parseFile: invalid tail value"), index);
msg(tr("parseFile: invalid tail value %1h").hexarg(*(UINT16*)tail.data()), index);
if (msgInvalidType)
msg(tr("parseFile: unknown file type %1h").arg(fileHeader->Type, 2), index);
@ -1382,8 +1384,15 @@ UINT8 FfsEngine::getSectionSize(const QByteArray & file, const UINT32 sectionOff
{
if ((UINT32)file.size() < sectionOffset + sizeof(EFI_COMMON_SECTION_HEADER))
return ERR_INVALID_FILE;
const EFI_COMMON_SECTION_HEADER* sectionHeader = (const EFI_COMMON_SECTION_HEADER*)(file.constData() + sectionOffset);
sectionSize = uint24ToUint32(sectionHeader->Size);
// This may introduce a very rare error with a non-extended section of size equal to 0xFFFFFF
if (sectionSize != 0xFFFFFF)
return ERR_SUCCESS;
const EFI_COMMON_SECTION_HEADER2* sectionHeader2 = (const EFI_COMMON_SECTION_HEADER2*)(file.constData() + sectionOffset);
sectionSize = sectionHeader2->ExtendedSize;
return ERR_SUCCESS;
}
@ -1670,7 +1679,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, c
}
}
else if (certificateHeader->CertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
info += tr("\nSignature type: PCKS7");
info += tr("\nSignature type: PKCS7");
// TODO: show signature info in Information panel
}
else {
@ -2219,28 +2228,51 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte
if (header.size() != sizeof(EFI_FFS_FILE_HEADER))
return ERR_INVALID_FILE;
QByteArray newHeader = header;
QByteArray newBody = body;
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newHeader.data();
QByteArray newObject = header + body;
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)newObject.data();
// Determine correct file header size
bool largeFile = false;
UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER);
if (revision == 2 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
largeFile = true;
headerSize = sizeof(EFI_FFS_FILE_HEADER2);
}
QByteArray newHeader = newObject.left(headerSize);
QByteArray newBody = newObject.mid(headerSize);
// Check if the file has a tail
UINT8 tailSize = fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT ? sizeof(UINT16) : 0;
UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0;
if (tailSize) {
// Remove the tail, it will then be added back for revision 1 volumes
newBody = newBody.left(newBody.size() - tailSize);
// Remove the attribute for rev2+ volumes
if (revision != 1) {
fileHeader->Attributes &= ~(FFS_ATTRIB_TAIL_PRESENT);
}
}
// Correct file size
uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + newBody.size() + tailSize, fileHeader->Size);
if (!largeFile) {
if (newBody.size() >= 0xFFFFFF) {
return ERR_INVALID_FILE;
}
uint32ToUint24(headerSize + newBody.size() + tailSize, fileHeader->Size);
}
else {
uint32ToUint24(0xFFFFFF, fileHeader->Size);
EFI_FFS_FILE_HEADER2* fileHeader2 = (EFI_FFS_FILE_HEADER2*)newHeader.data();
fileHeader2->ExtendedSize = headerSize + newBody.size() + tailSize;
}
// Set file state
UINT8 state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION;
if (erasePolarity)
state = ~state;
fileHeader->State = state;
// Recalculate header checksum
fileHeader->IntegrityCheck.Checksum.Header = 0;
fileHeader->IntegrityCheck.Checksum.File = 0;
fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)fileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1);
fileHeader->IntegrityCheck.Checksum.Header = 0x100 - (calculateSum8((const UINT8*)newHeader.constData(), headerSize) - fileHeader->State);
// Recalculate data checksum, if needed
if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM)
@ -2254,23 +2286,17 @@ UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByte
created.append(newBody);
// Append tail, if needed
if (revision ==1 && tailSize) {
if (revision == 1 && tailSize) {
UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header;
UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File;
created.append(ht).append(ft);
}
// Set file state
UINT8 state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION;
if (erasePolarity)
state = ~state;
fileHeader->State = state;
// Prepend header
created.prepend(newHeader);
// Parse file
result = parseFile(created, fileIndex, erasePolarity ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index, mode);
result = parseFile(created, fileIndex, revision, erasePolarity ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index, mode);
if (result && result != ERR_VOLUMES_NOT_FOUND && result != ERR_INVALID_VOLUME)
return result;
@ -2428,17 +2454,20 @@ UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, co
}
else if (model->type(parent) == Types::File) {
type = Types::Section;
const EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData();
const EFI_COMMON_SECTION_HEADER* commonHeader = (const EFI_COMMON_SECTION_HEADER*)object.constData();
headerSize = sizeOfSectionHeader(commonHeader);
}
else if (model->type(parent) == Types::Section) {
type = Types::Section;
const EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)object.constData();
const EFI_COMMON_SECTION_HEADER* commonHeader = (const EFI_COMMON_SECTION_HEADER*)object.constData();
headerSize = sizeOfSectionHeader(commonHeader);
}
else
return ERR_NOT_IMPLEMENTED;
if ((UINT32)object.size() < headerSize)
return ERR_BUFFER_TOO_SMALL;
return create(index, type, object.left(headerSize), object.right(object.size() - headerSize), mode, Actions::Insert);
}
@ -2873,6 +2902,9 @@ UINT8 FfsEngine::constructPadFile(const QByteArray &guid, const UINT32 size, con
if (size < sizeof(EFI_FFS_FILE_HEADER) || erasePolarity == ERASE_POLARITY_UNKNOWN)
return ERR_INVALID_PARAMETER;
if (size >= 0xFFFFFF) // TODO: large file support
return ERR_INVALID_PARAMETER;
pad = QByteArray(size - guid.size(), erasePolarity == ERASE_POLARITY_TRUE ? '\xFF' : '\x00');
pad.prepend(guid);
EFI_FFS_FILE_HEADER* header = (EFI_FFS_FILE_HEADER*)pad.data();
@ -3265,6 +3297,7 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon
model->subtype(index.child(i, 0)) == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)){
QModelIndex peiFile = index.child(i, 0);
UINT32 sectionOffset = sizeof(EFI_FFS_FILE_HEADER);
// BUGBUG: this parsing is bad and doesn't support large files, but it needs to be performed only for very old images with uncompressed DXE volumes, so whatever
// Search for PE32 or TE section
for (int j = 0; j < model->rowCount(peiFile); j++) {
if (model->subtype(peiFile.child(j, 0)) == EFI_SECTION_PE32 ||
@ -3330,6 +3363,9 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon
continue;
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)file.data();
UINT32 fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER);
if (volumeHeader->Revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE))
fileHeaderSize = sizeof(EFI_FFS_FILE_HEADER2);
// Pad file
if (fileHeader->Type == EFI_FV_FILETYPE_PAD) {
@ -3357,8 +3393,8 @@ UINT8 FfsEngine::reconstructVolume(const QModelIndex & index, QByteArray & recon
UINT8 alignmentPower;
UINT32 alignmentBase;
alignmentPower = ffsAlignmentTable[(fileHeader->Attributes & FFS_ATTRIB_DATA_ALIGNMENT) >> 3];
alignment = (UINT32)pow(2.0, alignmentPower);
alignmentBase = header.size() + offset + sizeof(EFI_FFS_FILE_HEADER);
alignment = (UINT32)(1UL <<alignmentPower);
alignmentBase = header.size() + offset + fileHeaderSize;
if (alignmentBase % alignment) {
// File will be unaligned if added as is, so we must add pad file before it
// Determine pad file size
@ -3577,7 +3613,7 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision,
model->action(index) == Actions::Rebuild) {
QByteArray header = model->header(index);
EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*)header.data();
// Check erase polarity
if (erasePolarity == ERASE_POLARITY_UNKNOWN) {
msg(tr("reconstructFile: unknown erase polarity"), index);
@ -3635,6 +3671,10 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision,
// File contains sections
else {
UINT32 offset = 0;
UINT32 headerSize = sizeof(EFI_FFS_FILE_HEADER);
if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
headerSize = sizeof(EFI_FFS_FILE_HEADER2);
}
for (int i = 0; i < model->rowCount(index); i++) {
// Align to 4 byte boundary
@ -3646,7 +3686,7 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision,
}
// Calculate section base
UINT32 sectionBase = base ? base + sizeof(EFI_FFS_FILE_HEADER) + offset : 0;
UINT32 sectionBase = base ? base + headerSize + offset : 0;
// Reconstruct section
QByteArray section;
@ -3668,13 +3708,22 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision,
// Correct file size
UINT8 tailSize = (revision == 1 && (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT)) ? sizeof(UINT16) : 0;
uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize, fileHeader->Size);
if (revision > 1 && (fileHeader->Attributes & FFS_ATTRIB_LARGE_FILE)) {
uint32ToUint24(EFI_SECTION2_IS_USED, fileHeader->Size);
EFI_FFS_FILE_HEADER2* fileHeader2 = (EFI_FFS_FILE_HEADER2*) fileHeader;
fileHeader2->ExtendedSize = sizeof(EFI_FFS_FILE_HEADER2) + reconstructed.size() + tailSize;
} else {
if (sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize > 0xFFFFFF) {
msg(tr("reconstructFile: resulting file size is too big"), index);
return ERR_INVALID_FILE;
}
uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + reconstructed.size() + tailSize, fileHeader->Size);
}
// Recalculate header checksum
fileHeader->IntegrityCheck.Checksum.Header = 0;
fileHeader->IntegrityCheck.Checksum.File = 0;
fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((const UINT8*)fileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1);
fileHeader->IntegrityCheck.Checksum.Header = 0x100 - (calculateSum8((const UINT8*)header.constData(), header.size()) - fileHeader->State);
}
// Use current file body
else
@ -3690,7 +3739,7 @@ UINT8 FfsEngine::reconstructFile(const QModelIndex& index, const UINT8 revision,
fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2;
// Append tail, if needed
if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) {
if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) {
UINT8 ht = ~fileHeader->IntegrityCheck.Checksum.Header;
UINT8 ft = ~fileHeader->IntegrityCheck.Checksum.File;
reconstructed.append(ht).append(ft);
@ -3732,6 +3781,10 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base,
model->action(index) == Actions::Rebase) {
QByteArray header = model->header(index);
EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*)header.data();
bool extended = false;
if(uint24ToUint32(commonHeader->Size) == 0xFFFFFF) {
extended = true;
}
// Reconstruct section with children
if (model->rowCount(index)) {
@ -3832,7 +3885,13 @@ UINT8 FfsEngine::reconstructSection(const QModelIndex& index, const UINT32 base,
}
// Correct section size
uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size);
if (extended) {
EFI_COMMON_SECTION_HEADER2 * extHeader = (EFI_COMMON_SECTION_HEADER2*) commonHeader;
extHeader->ExtendedSize = header.size() + reconstructed.size();
uint32ToUint24(0xFFFFFF, commonHeader->Size);
} else {
uint32ToUint24(header.size() + reconstructed.size(), commonHeader->Size);
}
}
// Leaf section
else

View File

@ -70,7 +70,7 @@ public:
UINT8 parseEcRegion(const QByteArray & ec, QModelIndex & index, const QModelIndex & parent, const UINT8 mode = CREATE_MODE_APPEND);
UINT8 parseBios(const QByteArray & bios, const QModelIndex & parent = QModelIndex());
UINT8 parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND);
UINT8 parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND);
UINT8 parseFile(const QByteArray & file, QModelIndex & index, const UINT8 revision = 2, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND);
UINT8 parseSections(const QByteArray & body, const QModelIndex & parent = QModelIndex());
UINT8 parseSection(const QByteArray & section, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND);
@ -117,7 +117,6 @@ private:
UINT8 parseDepexSection(const QByteArray & body, QString & parsed);
UINT8 findNextVolume(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & nextVolumeOffset);
UINT8 getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize);
UINT8 getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize);
UINT8 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize);
// Reconstruction helpers

View File

@ -17,7 +17,7 @@
UEFITool::UEFITool(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::UEFITool),
version(tr("0.21.5"))
version(tr("0.22.0"))
{
clipboard = QApplication::clipboard();