mirror of
https://github.com/LongSoft/UEFITool.git
synced 2024-11-22 07:58:22 +08:00
1170 lines
61 KiB
C++
Executable File
1170 lines
61 KiB
C++
Executable File
/* meparser.cpp
|
|
|
|
Copyright (c) 2016, Nikolaj Schlej. 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
|
|
http://opensource.org/licenses/bsd-license.php.
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include <map>
|
|
|
|
#include "meparser.h"
|
|
#include "parsingdata.h"
|
|
#include "utility.h"
|
|
|
|
#ifdef U_ENABLE_ME_PARSING_SUPPORT
|
|
|
|
UString meBpdtEntryTypeToUString(UINT16 type) {
|
|
switch (type) {
|
|
case 0: return UString("OEM SMIP");
|
|
case 1: return UString("CSE RBE");
|
|
case 2: return UString("CSE BUP");
|
|
case 3: return UString("uCode");
|
|
case 4: return UString("IBB");
|
|
case 5: return UString("S-BPDT");
|
|
case 6: return UString("OBB");
|
|
case 7: return UString("CSE Main");
|
|
case 8: return UString("ISH");
|
|
case 9: return UString("CSE IDLM");
|
|
case 10: return UString("IFP Override");
|
|
case 11: return UString("Debug Tokens");
|
|
case 12: return UString("USF Phy Config");
|
|
case 13: return UString("USF GPP LUN ID");
|
|
case 14: return UString("PMC");
|
|
case 15: return UString("iUnit");
|
|
case 16: return UString("NVM Config");
|
|
case 17: return UString("UEP");
|
|
case 18: return UString("WLAN uCode");
|
|
case 19: return UString("LOCL Sprites");
|
|
case 20: return UString("OEM Key Manifest");
|
|
case 21: return UString("Defaults/FITC.cfg");
|
|
case 22: return UString("PAVP");
|
|
case 23: return UString("TCSS FW IOM");
|
|
case 24: return UString("TCSS FW PHY");
|
|
case 25: return UString("TCSS TBT");
|
|
default: return usprintf("Unknown %u", type);
|
|
}
|
|
}
|
|
|
|
UString meExtensionTypeToUstring(UINT32 type) {
|
|
switch (type) {
|
|
case 0: return UString("System Info");
|
|
case 1: return UString("Init Script");
|
|
case 2: return UString("Feature Permissions");
|
|
case 3: return UString("Partition Info");
|
|
case 4: return UString("Shared Lib Attributes");
|
|
case 5: return UString("Process Attributes");
|
|
case 6: return UString("Thread Attributes");
|
|
case 7: return UString("Device Type");
|
|
case 8: return UString("MMIO Range");
|
|
case 9: return UString("Spec File Producer");
|
|
case 10: return UString("Module Attributes");
|
|
case 11: return UString("Locked Ranges");
|
|
case 12: return UString("Client System Info");
|
|
case 13: return UString("User Info");
|
|
case 14: return UString("Key Manifest");
|
|
case 15: return UString("Signed Package Info");
|
|
case 16: return UString("Anto-cloning SKU ID");
|
|
case 18: return UString("Intel IMR Info");
|
|
case 20: return UString("RCIP Info");
|
|
case 21: return UString("Secure Token");
|
|
case 22: return UString("IFWI Partition Manifest");
|
|
default: return usprintf("Unknown %u", type);
|
|
}
|
|
}
|
|
|
|
struct ME_FPT_PARTITION_INFO {
|
|
ME_FPT_ENTRY ptEntry;
|
|
UINT8 type;
|
|
UModelIndex index;
|
|
friend bool operator< (const ME_FPT_PARTITION_INFO & lhs, const ME_FPT_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; }
|
|
};
|
|
|
|
struct ME_BPDT_PARTITION_INFO {
|
|
ME_BPDT_ENTRY ptEntry;
|
|
UINT8 type;
|
|
UModelIndex index;
|
|
friend bool operator< (const ME_BPDT_PARTITION_INFO & lhs, const ME_BPDT_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset < rhs.ptEntry.Offset; }
|
|
};
|
|
|
|
struct ME_CPD_PARTITION_INFO {
|
|
ME_BPDT_CPD_ENTRY ptEntry;
|
|
UINT8 type;
|
|
UModelIndex index;
|
|
friend bool operator< (const ME_CPD_PARTITION_INFO & lhs, const ME_CPD_PARTITION_INFO & rhs){ return lhs.ptEntry.Offset.Offset < rhs.ptEntry.Offset.Offset; }
|
|
};
|
|
|
|
USTATUS MeParser::parseMeRegionBody(const UModelIndex & index)
|
|
{
|
|
// Sanity check
|
|
if (!index.isValid())
|
|
return U_INVALID_PARAMETER;
|
|
|
|
// Obtain ME region
|
|
UByteArray meRegion = model->body(index);
|
|
|
|
// Check region size
|
|
if ((UINT32)meRegion.size() < ME_ROM_BYPASS_VECTOR_SIZE + sizeof(UINT32)) {
|
|
msg(usprintf("%s: ME region too small to fit ROM bypass vector", __FUNCTION__), index);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Check ME signature to determine it's version
|
|
// ME v11 and older layout
|
|
if (meRegion.left(sizeof(UINT32)) == ME_FPT_HEADER_SIGNATURE || meRegion.mid(ME_ROM_BYPASS_VECTOR_SIZE, sizeof(UINT32)) == ME_FPT_HEADER_SIGNATURE) {
|
|
UModelIndex ptIndex;
|
|
return parseFptRegion(meRegion, index, ptIndex);
|
|
}
|
|
|
|
// CannonLake 1.6+ layout (IFWI)
|
|
// Check region size
|
|
if ((UINT32)meRegion.size() < sizeof(ME_IFWI_LAYOUT_HEADER)) {
|
|
msg(usprintf("%s: ME region too small to fit IFWI layout header", __FUNCTION__), index);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
const ME_IFWI_LAYOUT_HEADER* regionHeader = (const ME_IFWI_LAYOUT_HEADER*)meRegion.constData();
|
|
// Check region size
|
|
if ((UINT32)meRegion.size() < regionHeader->DataPartitionOffset + sizeof(UINT32)) {
|
|
msg(usprintf("%s: ME region too small to fit IFWI layout header", __FUNCTION__), index);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
// Data partition always points to FPT header
|
|
if (meRegion.mid(regionHeader->DataPartitionOffset, sizeof(UINT32)) == ME_FPT_HEADER_SIGNATURE) {
|
|
UModelIndex ptIndex;
|
|
return parseIfwiRegion(meRegion, index, ptIndex);
|
|
}
|
|
|
|
// Something else entirely
|
|
msg(usprintf("%s: unknown ME region format", __FUNCTION__), index);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
USTATUS MeParser::parseIfwiRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
// Add header
|
|
UByteArray header = region.left(sizeof(ME_IFWI_LAYOUT_HEADER));
|
|
const ME_IFWI_LAYOUT_HEADER* ifwiHeader = (const ME_IFWI_LAYOUT_HEADER*)region.constData();
|
|
|
|
UString name = UString("IFWI header");
|
|
UString info = usprintf("Full size: %Xh (%u)\n"
|
|
"Data partition offset: %Xh\nData partition length: %Xh\n"
|
|
"Boot1 partition offset: %Xh\nBoot1 partition length: %Xh\n"
|
|
"Boot2 partition offset: %Xh\nBoot2 partition length: %Xh\n"
|
|
"Boot3 partition offset: %Xh\nBoot3 partition length: %Xh",
|
|
header.size(), header.size(),
|
|
ifwiHeader->DataPartitionOffset, ifwiHeader->DataPartitionSize,
|
|
ifwiHeader->Boot1Offset, ifwiHeader->Boot1Size,
|
|
ifwiHeader->Boot2Offset, ifwiHeader->Boot2Size,
|
|
ifwiHeader->Boot3Offset, ifwiHeader->Boot3Size);
|
|
// Add tree item
|
|
index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent);
|
|
|
|
// TODO: this requires better parsing using a similar approach as in other things: get all, sort, check for paddings/intersections
|
|
|
|
// Add padding after header
|
|
if (ifwiHeader->DataPartitionOffset > sizeof(ME_IFWI_LAYOUT_HEADER)) {
|
|
UByteArray padding = region.mid(sizeof(ME_IFWI_LAYOUT_HEADER), ifwiHeader->DataPartitionOffset - sizeof(ME_IFWI_LAYOUT_HEADER));
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
|
// Add tree item
|
|
model->addItem(sizeof(ME_IFWI_LAYOUT_HEADER), Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent);
|
|
}
|
|
|
|
// Add data partition
|
|
UByteArray dataPartition = region.mid(ifwiHeader->DataPartitionOffset, ifwiHeader->DataPartitionSize);
|
|
name = UString("Data partition");
|
|
info = usprintf("Full size: %Xh (%u)", dataPartition.size(), dataPartition.size());
|
|
UModelIndex dataPartitionIndex = model->addItem(ifwiHeader->DataPartitionOffset, Types::IfwiPartition, Subtypes::DataIfwiPartition, name, UString(), info, UByteArray(), dataPartition, UByteArray(), Fixed, parent);
|
|
UModelIndex dataPartitionFptRegionIndex;
|
|
parseFptRegion(dataPartition, dataPartitionIndex, dataPartitionFptRegionIndex);
|
|
|
|
// Add padding after data partition
|
|
if (ifwiHeader->Boot1Offset > ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize) {
|
|
UByteArray padding = region.mid(ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize, ifwiHeader->Boot1Offset - ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize);
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
|
// Add tree item
|
|
model->addItem(ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent);
|
|
}
|
|
else if (ifwiHeader->Boot1Offset < ifwiHeader->DataPartitionOffset + ifwiHeader->DataPartitionSize) {
|
|
msg(usprintf("%s: invalid Boot1 partition offset", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Add Boot1 partition
|
|
UByteArray bpdt1Partition = region.mid(ifwiHeader->Boot1Offset, ifwiHeader->Boot1Size);
|
|
name = UString("Boot1 partition");
|
|
info = usprintf("Full size: %Xh (%u)", bpdt1Partition.size(), bpdt1Partition.size());
|
|
UModelIndex bpdt1PartitionIndex = model->addItem(ifwiHeader->Boot1Offset, Types::IfwiPartition, Subtypes::BootIfwiPartition, name, UString(), info, UByteArray(), bpdt1Partition, UByteArray(), Fixed, parent);
|
|
UModelIndex bpdt1PartitionBpdtRegionIndex;
|
|
parseBpdtRegion(bpdt1Partition, bpdt1PartitionIndex, bpdt1PartitionBpdtRegionIndex);
|
|
|
|
// Add padding after Boot1 partition
|
|
if (ifwiHeader->Boot2Offset > ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size) {
|
|
UByteArray padding = region.mid(ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size, ifwiHeader->Boot2Offset - ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size);
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
|
// Add tree item
|
|
model->addItem(ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent);
|
|
}
|
|
else if (ifwiHeader->Boot2Offset < ifwiHeader->Boot1Offset + ifwiHeader->Boot1Size) {
|
|
msg(usprintf("%s: invalid Boot2 partition offset", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Add Boot2 partition
|
|
UByteArray bpdt2Partition = region.mid(ifwiHeader->Boot2Offset, ifwiHeader->Boot2Size);
|
|
name = UString("Boot2 partition");
|
|
info = usprintf("Full size: %Xh (%u)", bpdt2Partition.size(), bpdt2Partition.size());
|
|
UModelIndex bpdt2PartitionIndex = model->addItem(ifwiHeader->Boot2Offset, Types::IfwiPartition, Subtypes::BootIfwiPartition, name, UString(), info, UByteArray(), bpdt2Partition, UByteArray(), Fixed, parent);
|
|
UModelIndex bpdt2PartitionBpdtRegionIndex;
|
|
parseBpdtRegion(bpdt2Partition, bpdt2PartitionIndex, bpdt2PartitionBpdtRegionIndex);
|
|
|
|
// TODO: add Boot3 if needed
|
|
// Add padding at the end
|
|
if ((UINT32)region.size() > ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size) {
|
|
UByteArray padding = region.mid(ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size, (UINT32)region.size() - ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size);
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
|
// Add tree item
|
|
model->addItem(ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, parent);
|
|
}
|
|
else if ((UINT32)region.size() < ifwiHeader->Boot2Offset + ifwiHeader->Boot2Size) {
|
|
msg(usprintf("%s: Boot2 partition is located outside of the region", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
// Check region size
|
|
if ((UINT32)region.size() < sizeof(ME_FPT_HEADER)) {
|
|
msg(usprintf("%s: region too small to fit FPT header", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Populate partition table header
|
|
const ME_FPT_HEADER* ptHeader = (const ME_FPT_HEADER*)region.constData();
|
|
UINT32 romBypassVectorSize = 0;
|
|
if (region.left(sizeof(UINT32)) != ME_FPT_HEADER_SIGNATURE) {
|
|
// Adjust the header to skip ROM bypass vector
|
|
romBypassVectorSize = ME_ROM_BYPASS_VECTOR_SIZE;
|
|
ptHeader = (const ME_FPT_HEADER*)(region.constData() + romBypassVectorSize);
|
|
}
|
|
|
|
// Check region size again
|
|
UINT32 ptBodySize = ptHeader->NumEntries * sizeof(ME_FPT_ENTRY);
|
|
UINT32 ptSize = romBypassVectorSize + sizeof(ME_FPT_HEADER) + ptBodySize;
|
|
if ((UINT32)region.size() < ptSize) {
|
|
msg(usprintf("%s: ME region too small to fit partition table", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Recalculate checksum
|
|
UByteArray tempHeader = UByteArray((const char*)ptHeader, sizeof(ME_FPT_HEADER));
|
|
ME_FPT_HEADER* tempPtHeader = (ME_FPT_HEADER*)tempHeader.data();
|
|
tempPtHeader->Checksum = 0;
|
|
UINT8 calculated = calculateChecksum8((const UINT8*)tempPtHeader, sizeof(ME_FPT_HEADER));
|
|
bool msgInvalidPtHeaderChecksum = (calculated != ptHeader->Checksum);
|
|
|
|
// Get info
|
|
UByteArray header = region.left(romBypassVectorSize + sizeof(ME_FPT_HEADER));
|
|
UByteArray body = region.mid(header.size(), ptBodySize);
|
|
|
|
UString name = UString("FPT partition table");
|
|
UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\nHeader version: %02Xh\nEntry version: %02Xh\n"
|
|
"Header length: %02Xh\nTicks to add: %04Xh\nTokens to add: %04Xh\nUMA size: %Xh\nFlash layout: %Xh\nFITC version: %u.%u.%u.%u\nChecksum: %02Xh, ",
|
|
ptSize, ptSize,
|
|
header.size(), header.size(),
|
|
ptBodySize, ptBodySize,
|
|
ptHeader->NumEntries,
|
|
ptHeader->HeaderVersion,
|
|
ptHeader->EntryVersion,
|
|
ptHeader->HeaderLength,
|
|
ptHeader->TicksToAdd,
|
|
ptHeader->TokensToAdd,
|
|
ptHeader->UmaSize,
|
|
ptHeader->FlashLayout,
|
|
ptHeader->FitcMajor, ptHeader->FitcMinor, ptHeader->FitcHotfix, ptHeader->FitcBuild,
|
|
ptHeader->Checksum) + (ptHeader->Checksum == calculated ? UString("valid") : usprintf("invalid, should be %02Xh", calculated));
|
|
|
|
// Add tree item
|
|
index = model->addItem(0, Types::FptStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
// Show messages
|
|
if (msgInvalidPtHeaderChecksum) {
|
|
msg(usprintf("%s: FPT partition table header checksum is invalid", __FUNCTION__), index);
|
|
}
|
|
|
|
// Add partition table entries
|
|
std::vector<ME_FPT_PARTITION_INFO> partitions;
|
|
UINT32 offset = header.size();
|
|
const ME_FPT_ENTRY* firstPtEntry = (const ME_FPT_ENTRY*)(region.constData() + offset);
|
|
for (UINT8 i = 0; i < ptHeader->NumEntries; i++) {
|
|
// Populate entry header
|
|
const ME_FPT_ENTRY* ptEntry = firstPtEntry + i;
|
|
|
|
// Get info
|
|
name = usprintf("%c%c%c%c", ptEntry->PartitionName[0], ptEntry->PartitionName[1], ptEntry->PartitionName[2], ptEntry->PartitionName[3]);
|
|
info = usprintf("Full size: %Xh (%u)\nPartition offset: %Xh\nPartition length: %Xh\nPartition type: %02Xh",
|
|
sizeof(ME_FPT_ENTRY), sizeof(ME_FPT_ENTRY),
|
|
ptEntry->Offset,
|
|
ptEntry->Length,
|
|
ptEntry->PartitionType);
|
|
|
|
// Add tree item
|
|
const UINT8 type = (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Length != 0 && ptEntry->EntryValid != 0xFF) ? Subtypes::ValidFptEntry : Subtypes::InvalidFptEntry;
|
|
UModelIndex entryIndex = model->addItem(offset, Types::FptEntry, type, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(ME_FPT_ENTRY)), UByteArray(), Fixed, index);
|
|
|
|
// Adjust offset
|
|
offset += sizeof(ME_FPT_ENTRY);
|
|
|
|
// Add valid partitions
|
|
if (type == Subtypes::ValidFptEntry) { // Skip absent and invalid partitions
|
|
// Add to partitions vector
|
|
ME_FPT_PARTITION_INFO partition;
|
|
partition.type = Types::FptPartition;
|
|
partition.ptEntry = *ptEntry;
|
|
partition.index = entryIndex;
|
|
partitions.push_back(partition);
|
|
}
|
|
}
|
|
|
|
make_partition_table_consistent:
|
|
// Sort partitions by offset
|
|
std::sort(partitions.begin(), partitions.end());
|
|
|
|
// Check for intersections and paddings between partitions
|
|
ME_FPT_PARTITION_INFO padding;
|
|
|
|
// Check intersection with the partition table header
|
|
if (partitions.front().ptEntry.Offset < ptSize) {
|
|
msg(usprintf("%s: ME partition has intersection with ME partition table, skipped", __FUNCTION__),
|
|
partitions.front().index);
|
|
partitions.erase(partitions.begin());
|
|
goto make_partition_table_consistent;
|
|
}
|
|
// Check for padding between partition table and the first partition
|
|
else if (partitions.front().ptEntry.Offset > ptSize) {
|
|
padding.ptEntry.Offset = ptSize;
|
|
padding.ptEntry.Length = partitions.front().ptEntry.Offset - ptSize;
|
|
padding.type = Types::Padding;
|
|
partitions.insert(partitions.begin(), padding);
|
|
}
|
|
// Check for intersections/paddings between partitions
|
|
for (size_t i = 1; i < partitions.size(); i++) {
|
|
UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Length;
|
|
|
|
// Check that current region is fully present in the image
|
|
if ((UINT32)partitions[i].ptEntry.Offset + (UINT32)partitions[i].ptEntry.Length > (UINT32)region.size()) {
|
|
if ((UINT32)partitions[i].ptEntry.Offset >= (UINT32)region.size()) {
|
|
msg(usprintf("%s: FPT partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: FPT partition can't fit into the region, truncated", __FUNCTION__), partitions[i].index);
|
|
partitions[i].ptEntry.Length = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset;
|
|
}
|
|
}
|
|
|
|
// Check for intersection with previous partition
|
|
if (partitions[i].ptEntry.Offset < previousPartitionEnd) {
|
|
// Check if current partition is located inside previous one
|
|
if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) {
|
|
msg(usprintf("%s: FPT partition is located inside another FPT partition, skipped", __FUNCTION__),
|
|
partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: FPT partition intersects with prevous one, skipped", __FUNCTION__),
|
|
partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
}
|
|
|
|
// Check for padding between current and previous partitions
|
|
else if (partitions[i].ptEntry.Offset > previousPartitionEnd) {
|
|
padding.ptEntry.Offset = previousPartitionEnd;
|
|
padding.ptEntry.Length = partitions[i].ptEntry.Offset - previousPartitionEnd;
|
|
padding.type = Types::Padding;
|
|
std::vector<ME_FPT_PARTITION_INFO>::iterator iter = partitions.begin();
|
|
std::advance(iter, i);
|
|
partitions.insert(iter, padding);
|
|
}
|
|
}
|
|
// Check for padding after the last region
|
|
if ((UINT32)partitions.back().ptEntry.Offset + (UINT32)partitions.back().ptEntry.Length < (UINT32)region.size()) {
|
|
padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Length;
|
|
padding.ptEntry.Length = region.size() - padding.ptEntry.Offset;
|
|
padding.type = Types::Padding;
|
|
partitions.push_back(padding);
|
|
}
|
|
|
|
// Partition map is consistent
|
|
for (size_t i = 0; i < partitions.size(); i++) {
|
|
UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Length);
|
|
if (partitions[i].type == Types::FptPartition) {
|
|
UModelIndex partitionIndex;
|
|
// Get info
|
|
name = usprintf("%c%c%c%c", partitions[i].ptEntry.PartitionName[0], partitions[i].ptEntry.PartitionName[1], partitions[i].ptEntry.PartitionName[2], partitions[i].ptEntry.PartitionName[3]);
|
|
info = usprintf("Full size: %Xh (%u)\nPartition type: %02Xh\n",
|
|
partition.size(), partition.size(),
|
|
partitions[i].ptEntry.PartitionType);
|
|
|
|
// Add tree item
|
|
UINT8 type = Subtypes::CodeFptPartition + partitions[i].ptEntry.PartitionType;
|
|
partitionIndex = model->addItem(partitions[i].ptEntry.Offset, Types::FptPartition, type, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
if (type == Subtypes::CodeFptPartition && partition.left(sizeof(UINT32)) == ME_CPD_SIGNATURE) {
|
|
// Parse conde partition contents
|
|
UModelIndex ptIndex;
|
|
parseCodePartitionDirectory(partition, partitions[i].ptEntry.Offset, partitionIndex, ptIndex);
|
|
}
|
|
}
|
|
else if (partitions[i].type == Types::Padding) {
|
|
// Get info
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", partition.size(), partition.size());
|
|
|
|
// Add tree item
|
|
model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
}
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS MeParser::parseBpdtRegion(const UByteArray & region, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
// Check region size
|
|
if ((UINT32)region.size() < sizeof(ME_BPDT_HEADER)) {
|
|
msg(usprintf("%s: BPDT region too small to fit partition table header", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Populate partition table header
|
|
const ME_BPDT_HEADER* ptHeader = (const ME_BPDT_HEADER*)region.constData();
|
|
|
|
// Check region size again
|
|
UINT32 ptBodySize = ptHeader->NumEntries * sizeof(ME_BPDT_ENTRY);
|
|
UINT32 ptSize = sizeof(ME_BPDT_HEADER) + ptBodySize;
|
|
if ((UINT32)region.size() < ptSize) {
|
|
msg(usprintf("%s: BPDT region too small to fit BPDT partition table", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Get info
|
|
UByteArray header = region.left(sizeof(ME_BPDT_HEADER));
|
|
UByteArray body = region.mid(sizeof(ME_BPDT_HEADER), ptBodySize);
|
|
|
|
UString name = UString("BPDT partition table");
|
|
UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\nVersion: %Xh\n"
|
|
"IFWI version: %Xh\nFITC version: %u.%u.%u.%u",
|
|
ptSize, ptSize,
|
|
header.size(), header.size(),
|
|
ptBodySize, ptBodySize,
|
|
ptHeader->NumEntries,
|
|
ptHeader->Version,
|
|
ptHeader->IfwiVersion,
|
|
ptHeader->FitcMajor, ptHeader->FitcMinor, ptHeader->FitcHotfix, ptHeader->FitcBuild);
|
|
|
|
// Add tree item
|
|
index = model->addItem(0, Types::BpdtStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
// Adjust offset
|
|
UINT32 offset = sizeof(ME_FPT_HEADER);
|
|
|
|
// Add partition table entries
|
|
std::vector<ME_BPDT_PARTITION_INFO> partitions;
|
|
const ME_BPDT_ENTRY* firstPtEntry = (const ME_BPDT_ENTRY*)(region.constData() + sizeof(ME_BPDT_HEADER));
|
|
for (UINT8 i = 0; i < ptHeader->NumEntries; i++) {
|
|
// Populate entry header
|
|
const ME_BPDT_ENTRY* ptEntry = firstPtEntry + i;
|
|
|
|
// Get info
|
|
name = meBpdtEntryTypeToUString(ptEntry->Type);
|
|
info = usprintf("Full size: %Xh (%u)\nType: %Xh\nPartition offset: %Xh\nPartition length: %Xh",
|
|
sizeof(ME_BPDT_ENTRY), sizeof(ME_BPDT_ENTRY),
|
|
ptEntry->Type,
|
|
ptEntry->Offset,
|
|
ptEntry->Length) +
|
|
UString("\nSplit sub-partition first part: ") + (ptEntry->SplitSubPartitionFirstPart ? "Yes" : "No") +
|
|
UString("\nSplit sub-partition second part: ") + (ptEntry->SplitSubPartitionSecondPart ? "Yes" : "No") +
|
|
UString("\nCode sub-partition: ") + (ptEntry->CodeSubPartition ? "Yes" : "No") +
|
|
UString("\nUMA cachable: ") + (ptEntry->UmaCachable ? "Yes" : "No");
|
|
|
|
// Add tree item
|
|
UModelIndex entryIndex = model->addItem(offset, Types::BpdtEntry, 0, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(ME_BPDT_ENTRY)), UByteArray(), Fixed, index);
|
|
|
|
// Adjust offset
|
|
offset += sizeof(ME_BPDT_ENTRY);
|
|
|
|
if (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Length != 0) {
|
|
// Add to partitions vector
|
|
ME_BPDT_PARTITION_INFO partition;
|
|
partition.type = Types::FptPartition;
|
|
partition.ptEntry = *ptEntry;
|
|
partition.index = entryIndex;
|
|
partitions.push_back(partition);
|
|
}
|
|
}
|
|
|
|
// Add padding if there's no partions to add
|
|
if (partitions.size() == 0) {
|
|
UByteArray partition = region.mid(ptSize);
|
|
|
|
// Get info
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)",
|
|
partition.size(), partition.size());
|
|
|
|
// Add tree item
|
|
model->addItem(ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
make_partition_table_consistent:
|
|
// Sort partitions by offset
|
|
std::sort(partitions.begin(), partitions.end());
|
|
|
|
// Check for intersections and paddings between partitions
|
|
ME_BPDT_PARTITION_INFO padding;
|
|
|
|
// Check intersection with the partition table header
|
|
if (partitions.front().ptEntry.Offset < ptSize) {
|
|
msg(usprintf("%s: BPDT partition has intersection with BPDT partition table, skipped", __FUNCTION__),
|
|
partitions.front().index);
|
|
partitions.erase(partitions.begin());
|
|
goto make_partition_table_consistent;
|
|
}
|
|
// Check for padding between partition table and the first partition
|
|
else if (partitions.front().ptEntry.Offset > ptSize) {
|
|
padding.ptEntry.Offset = ptSize;
|
|
padding.ptEntry.Length = partitions.front().ptEntry.Offset - padding.ptEntry.Offset;
|
|
padding.type = Types::Padding;
|
|
partitions.insert(partitions.begin(), padding);
|
|
}
|
|
// Check for intersections/paddings between partitions
|
|
for (size_t i = 1; i < partitions.size(); i++) {
|
|
UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset + partitions[i - 1].ptEntry.Length;
|
|
|
|
// Check that partition is fully present in the image
|
|
if ((UINT64)partitions[i].ptEntry.Offset + (UINT64)partitions[i].ptEntry.Length > (UINT64)region.size()) {
|
|
if ((UINT64)partitions[i].ptEntry.Offset >= (UINT64)region.size()) {
|
|
msg(usprintf("%s: BPDT partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: BPDT partition can't fit into it's region, truncated", __FUNCTION__), partitions[i].index);
|
|
partitions[i].ptEntry.Length = (UINT32)region.size() - (UINT32)partitions[i].ptEntry.Offset;
|
|
}
|
|
}
|
|
|
|
// Check for intersection with previous partition
|
|
if (partitions[i].ptEntry.Offset < previousPartitionEnd) {
|
|
// Check if current partition is located inside previous one
|
|
if (partitions[i].ptEntry.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) {
|
|
msg(usprintf("%s: BPDT partition is located inside another BPDT partition, skipped", __FUNCTION__),
|
|
partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: BPDT partition intersects with prevous one, skipped", __FUNCTION__),
|
|
partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
}
|
|
|
|
// Check for padding between current and previous partitions
|
|
else if (partitions[i].ptEntry.Offset > previousPartitionEnd) {
|
|
padding.ptEntry.Offset = previousPartitionEnd;
|
|
padding.ptEntry.Length = partitions[i].ptEntry.Offset - previousPartitionEnd;
|
|
padding.type = Types::Padding;
|
|
std::vector<ME_BPDT_PARTITION_INFO>::iterator iter = partitions.begin();
|
|
std::advance(iter, i);
|
|
partitions.insert(iter, padding);
|
|
}
|
|
}
|
|
|
|
// Check for padding after the last region
|
|
if ((UINT64)partitions.back().ptEntry.Offset + (UINT64)partitions.back().ptEntry.Length < (UINT64)region.size()) {
|
|
padding.ptEntry.Offset = partitions.back().ptEntry.Offset + partitions.back().ptEntry.Length;
|
|
padding.ptEntry.Length = region.size() - padding.ptEntry.Offset;
|
|
padding.type = Types::Padding;
|
|
partitions.push_back(padding);
|
|
}
|
|
|
|
// Partition map is consistent
|
|
for (size_t i = 0; i < partitions.size(); i++) {
|
|
if (partitions[i].type == Types::FptPartition) {
|
|
// Get info
|
|
UString name = meBpdtEntryTypeToUString(partitions[i].ptEntry.Type);
|
|
UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Length);
|
|
UByteArray signature = partition.left(sizeof(UINT32));
|
|
|
|
UString info = usprintf("Full size: %Xh (%u)\nType: %Xh",
|
|
partition.size(), partition.size(),
|
|
partitions[i].ptEntry.Type) +
|
|
UString("\nSplit sub-partition first part: ") + (partitions[i].ptEntry.SplitSubPartitionFirstPart ? "Yes" : "No") +
|
|
UString("\nSplit sub-partition second part: ") + (partitions[i].ptEntry.SplitSubPartitionSecondPart ? "Yes" : "No") +
|
|
UString("\nCode sub-partition: ") + (partitions[i].ptEntry.CodeSubPartition ? "Yes" : "No") +
|
|
UString("\nUMA cachable: ") + (partitions[i].ptEntry.UmaCachable ? "Yes" : "No");
|
|
|
|
if (signature == ME_CPD_SIGNATURE) {
|
|
const ME_CPD_HEADER* cpdHeader = (const ME_CPD_HEADER*)partition.constData();
|
|
name = usprintf("%c%c%c%c", cpdHeader->ShortName[0], cpdHeader->ShortName[1], cpdHeader->ShortName[2], cpdHeader->ShortName[3]);
|
|
UString text = meBpdtEntryTypeToUString(partitions[i].ptEntry.Type);
|
|
|
|
// Add tree item
|
|
UModelIndex ptIndex = model->addItem(partitions[i].ptEntry.Offset, Types::BpdtPartition, 0, name, text, info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
|
|
// Parse contents
|
|
UModelIndex cpdIndex;
|
|
parseCodePartitionDirectory(partition, partitions[i].ptEntry.Offset, ptIndex, cpdIndex);
|
|
}
|
|
else {
|
|
// Add tree item
|
|
model->addItem(partitions[i].ptEntry.Offset, Types::BpdtEntry, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
}
|
|
}
|
|
else if (partitions[i].type == Types::Padding) {
|
|
UByteArray partition = region.mid(partitions[i].ptEntry.Offset, partitions[i].ptEntry.Length);
|
|
|
|
// Get info
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)",
|
|
partition.size(), partition.size());
|
|
|
|
// Add tree item
|
|
model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
}
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
|
|
USTATUS MeParser::parseCodePartitionDirectory(const UByteArray & directory, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
|
|
{
|
|
// Check directory size
|
|
if ((UINT32)directory.size() < sizeof(ME_CPD_HEADER)) {
|
|
msg(usprintf("%s: CPD too small to fit partition table header", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Populate partition table header
|
|
const ME_CPD_HEADER* cpdHeader = (const ME_CPD_HEADER*)directory.constData();
|
|
|
|
// Check directory size again
|
|
UINT32 ptBodySize = cpdHeader->NumEntries * sizeof(ME_BPDT_CPD_ENTRY);
|
|
UINT32 ptSize = sizeof(ME_CPD_HEADER) + ptBodySize;
|
|
if ((UINT32)directory.size() < ptSize) {
|
|
msg(usprintf("%s: CPD too small to fit partition table", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
|
|
// Get info
|
|
UByteArray header = directory.left(sizeof(ME_CPD_HEADER));
|
|
UByteArray body = directory.mid(sizeof(ME_CPD_HEADER));
|
|
UString name = usprintf("CPD partition table");
|
|
UString info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u\n"
|
|
"Header version: %u\nEntry version: %u\nHeader checksum: %02Xh",
|
|
directory.size(), directory.size(),
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
cpdHeader->NumEntries,
|
|
cpdHeader->HeaderVersion,
|
|
cpdHeader->EntryVersion,
|
|
cpdHeader->HeaderChecksum);
|
|
|
|
// Add tree item
|
|
index = model->addItem(localOffset, Types::CpdStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
// Add partition table entries
|
|
std::vector<ME_CPD_PARTITION_INFO> partitions;
|
|
UINT32 offset = sizeof(ME_CPD_HEADER);
|
|
const ME_BPDT_CPD_ENTRY* firstCpdEntry = (const ME_BPDT_CPD_ENTRY*)(body.constData());
|
|
for (UINT32 i = 0; i < cpdHeader->NumEntries; i++) {
|
|
// Populate entry header
|
|
const ME_BPDT_CPD_ENTRY* cpdEntry = firstCpdEntry + i;
|
|
UByteArray entry((const char*)cpdEntry, sizeof(ME_BPDT_CPD_ENTRY));
|
|
|
|
// Get info
|
|
name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c",
|
|
cpdEntry->EntryName[0], cpdEntry->EntryName[1], cpdEntry->EntryName[2], cpdEntry->EntryName[3],
|
|
cpdEntry->EntryName[4], cpdEntry->EntryName[5], cpdEntry->EntryName[6], cpdEntry->EntryName[7],
|
|
cpdEntry->EntryName[8], cpdEntry->EntryName[9], cpdEntry->EntryName[10], cpdEntry->EntryName[11]);
|
|
info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ",
|
|
entry.size(), entry.size(),
|
|
cpdEntry->Offset.Offset,
|
|
cpdEntry->Length)
|
|
+ (cpdEntry->Offset.HuffmanCompressed ? "Yes" : "No");
|
|
|
|
// Add tree item
|
|
UModelIndex entryIndex = model->addItem(offset, Types::CpdEntry, 0, name, UString(), info, UByteArray(), entry, UByteArray(), Fixed, index);
|
|
|
|
// Adjust offset
|
|
offset += sizeof(ME_BPDT_CPD_ENTRY);
|
|
|
|
if (cpdEntry->Offset.Offset != 0 && cpdEntry->Length != 0) {
|
|
// Add to partitions vector
|
|
ME_CPD_PARTITION_INFO partition;
|
|
partition.type = Types::CpdPartition;
|
|
partition.ptEntry = *cpdEntry;
|
|
partition.index = entryIndex;
|
|
partitions.push_back(partition);
|
|
}
|
|
}
|
|
|
|
// Add padding if there's no partions to add
|
|
if (partitions.size() == 0) {
|
|
UByteArray partition = directory.mid(ptSize);
|
|
|
|
// Get info
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)",
|
|
partition.size(), partition.size());
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset + ptSize, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
// Sort partitions by offset
|
|
std::sort(partitions.begin(), partitions.end());
|
|
|
|
// Because lenghts for all Huffmann-compressed partitions mean nothing at all, we need to split all partitions into 2 classes:
|
|
// 1. CPD manifest (should be the first)
|
|
// 2. Metadata entries (should begin right after partition manifest and end before any code partition)
|
|
UINT32 i = 1;
|
|
while (i < partitions.size()) {
|
|
name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c",
|
|
partitions[i].ptEntry.EntryName[0], partitions[i].ptEntry.EntryName[1], partitions[i].ptEntry.EntryName[2], partitions[i].ptEntry.EntryName[3],
|
|
partitions[i].ptEntry.EntryName[4], partitions[i].ptEntry.EntryName[5], partitions[i].ptEntry.EntryName[6], partitions[i].ptEntry.EntryName[7],
|
|
partitions[i].ptEntry.EntryName[8], partitions[i].ptEntry.EntryName[9], partitions[i].ptEntry.EntryName[10], partitions[i].ptEntry.EntryName[11]);
|
|
|
|
// Check if the current entry is metadata entry
|
|
if (!name.contains(".met")) {
|
|
// No need to parse further, all metadata partitions are parsed
|
|
break;
|
|
}
|
|
|
|
// Parse into data block, find Module Attributes extension, and get compressed size from there
|
|
UINT32 offset = 0;
|
|
UINT32 length = 0xFFFFFFFF; // Special guardian value
|
|
UByteArray partition = directory.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length);
|
|
while (offset < (UINT32)partition.size()) {
|
|
const ME_CPD_EXTENTION_HEADER* extHeader = (const ME_CPD_EXTENTION_HEADER*) (partition.constData() + offset);
|
|
if (extHeader->Length <= ((UINT32)partition.size() - offset)) {
|
|
if (extHeader->Type == 10) { //TODO: replace with defines
|
|
const ME_CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const ME_CPD_EXT_MODULE_ATTRIBUTES*)(partition.constData() + offset);
|
|
length = attrHeader->CompressedSize;
|
|
}
|
|
offset += extHeader->Length;
|
|
}
|
|
else break;
|
|
}
|
|
|
|
// Search down for corresponding code partition
|
|
// Construct it's name by replacing last 4 non-zero butes of the name with zeros
|
|
UINT32 j = 0;
|
|
for (UINT32 k = 11; k > 0 && j < 4; k--) {
|
|
if (name[k] != '\x00') {
|
|
name[k] = '\x00';
|
|
j++;
|
|
}
|
|
}
|
|
|
|
// Search
|
|
j = i + 1;
|
|
while (j < partitions.size()) {
|
|
if (name == usprintf("%c%c%c%c%c%c%c%c%c%c%c%c",
|
|
partitions[j].ptEntry.EntryName[0], partitions[j].ptEntry.EntryName[1], partitions[j].ptEntry.EntryName[2], partitions[j].ptEntry.EntryName[3],
|
|
partitions[j].ptEntry.EntryName[4], partitions[j].ptEntry.EntryName[5], partitions[j].ptEntry.EntryName[6], partitions[j].ptEntry.EntryName[7],
|
|
partitions[j].ptEntry.EntryName[8], partitions[j].ptEntry.EntryName[9], partitions[j].ptEntry.EntryName[10], partitions[j].ptEntry.EntryName[11])) {
|
|
// Found it, update it's Length if needed
|
|
if (partitions[j].ptEntry.Offset.HuffmanCompressed) {
|
|
partitions[j].ptEntry.Length = length;
|
|
}
|
|
else if (length != 0xFFFFFFFF && partitions[j].ptEntry.Length != length) {
|
|
msg(usprintf("%s: partition size mismatch between partition table (%Xh) and partition metadata (%Xh)", __FUNCTION__,
|
|
partitions[j].ptEntry.Length, length), partitions[j].index);
|
|
partitions[j].ptEntry.Length = length; // Believe metadata
|
|
}
|
|
// No need to search further
|
|
break;
|
|
}
|
|
// Check the next partition
|
|
j++;
|
|
}
|
|
// Check the next partition
|
|
i++;
|
|
}
|
|
|
|
make_partition_table_consistent:
|
|
// Sort partitions by offset
|
|
std::sort(partitions.begin(), partitions.end());
|
|
|
|
// Check for intersections and paddings between partitions
|
|
ME_CPD_PARTITION_INFO padding;
|
|
|
|
// Check intersection with the partition table header
|
|
if (partitions.front().ptEntry.Offset.Offset < ptSize) {
|
|
msg(usprintf("%s: CPD partition has intersection with CPD partition table, skipped", __FUNCTION__),
|
|
partitions.front().index);
|
|
partitions.erase(partitions.begin());
|
|
goto make_partition_table_consistent;
|
|
}
|
|
// Check for padding between partition table and the first partition
|
|
else if (partitions.front().ptEntry.Offset.Offset > ptSize) {
|
|
padding.ptEntry.Offset.Offset = ptSize;
|
|
padding.ptEntry.Length = partitions.front().ptEntry.Offset.Offset - padding.ptEntry.Offset.Offset;
|
|
padding.type = Types::Padding;
|
|
partitions.insert(partitions.begin(), padding);
|
|
}
|
|
// Check for intersections/paddings between partitions
|
|
for (size_t i = 1; i < partitions.size(); i++) {
|
|
UINT32 previousPartitionEnd = partitions[i - 1].ptEntry.Offset.Offset + partitions[i - 1].ptEntry.Length;
|
|
|
|
// Check that current region is fully present in the image
|
|
if ((UINT64)partitions[i].ptEntry.Offset.Offset + (UINT64)partitions[i].ptEntry.Length > (UINT64)directory.size()) {
|
|
if ((UINT64)partitions[i].ptEntry.Offset.Offset >= (UINT64)directory.size()) {
|
|
msg(usprintf("%s: CPD partition is located outside of the opened image, skipped", __FUNCTION__), partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: CPD partition can't fit into it's region, truncated", __FUNCTION__), partitions[i].index);
|
|
partitions[i].ptEntry.Length = (UINT32)directory.size() - (UINT32)partitions[i].ptEntry.Offset.Offset;
|
|
}
|
|
}
|
|
|
|
// Check for intersection with previous partition
|
|
if (partitions[i].ptEntry.Offset.Offset < previousPartitionEnd) {
|
|
// Check if current partition is located inside previous one
|
|
if (partitions[i].ptEntry.Offset.Offset + partitions[i].ptEntry.Length <= previousPartitionEnd) {
|
|
msg(usprintf("%s: CPD partition is located inside another CPD partition, skipped", __FUNCTION__),
|
|
partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
else {
|
|
msg(usprintf("%s: CPD partition intersects with prevous one, skipped", __FUNCTION__),
|
|
partitions[i].index);
|
|
partitions.erase(partitions.begin() + i);
|
|
goto make_partition_table_consistent;
|
|
}
|
|
}
|
|
// Check for padding between current and previous partitions
|
|
else if (partitions[i].ptEntry.Offset.Offset > previousPartitionEnd) {
|
|
padding.ptEntry.Offset.Offset = previousPartitionEnd;
|
|
padding.ptEntry.Length = partitions[i].ptEntry.Offset.Offset - previousPartitionEnd;
|
|
padding.type = Types::Padding;
|
|
std::vector<ME_CPD_PARTITION_INFO>::iterator iter = partitions.begin();
|
|
std::advance(iter, i);
|
|
partitions.insert(iter, padding);
|
|
}
|
|
}
|
|
// Check for padding after the last region
|
|
if ((UINT64)partitions.back().ptEntry.Offset.Offset + (UINT64)partitions.back().ptEntry.Length < (UINT64)directory.size()) {
|
|
padding.ptEntry.Offset.Offset = partitions.back().ptEntry.Offset.Offset + partitions.back().ptEntry.Length;
|
|
padding.ptEntry.Length = (UINT32)directory.size() - padding.ptEntry.Offset.Offset;
|
|
padding.type = Types::Padding;
|
|
partitions.push_back(padding);
|
|
}
|
|
|
|
// Partition map is consistent
|
|
for (size_t i = 0; i < partitions.size(); i++) {
|
|
if (partitions[i].type == Types::CpdPartition) {
|
|
UByteArray partition = directory.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length);
|
|
|
|
// Get info
|
|
name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c",
|
|
partitions[i].ptEntry.EntryName[0], partitions[i].ptEntry.EntryName[1], partitions[i].ptEntry.EntryName[2], partitions[i].ptEntry.EntryName[3],
|
|
partitions[i].ptEntry.EntryName[4], partitions[i].ptEntry.EntryName[5], partitions[i].ptEntry.EntryName[6], partitions[i].ptEntry.EntryName[7],
|
|
partitions[i].ptEntry.EntryName[8], partitions[i].ptEntry.EntryName[9], partitions[i].ptEntry.EntryName[10], partitions[i].ptEntry.EntryName[11]);
|
|
|
|
// It's a manifest
|
|
if (name.contains(".man")) {
|
|
if (!partitions[i].ptEntry.Offset.HuffmanCompressed
|
|
&& partitions[i].ptEntry.Length >= sizeof(ME_CPD_MANIFEST_HEADER)) {
|
|
const ME_CPD_MANIFEST_HEADER* manifestHeader = (const ME_CPD_MANIFEST_HEADER*) partition.constData();
|
|
if (manifestHeader->HeaderId == ME_MANIFEST_HEADER_ID) {
|
|
UByteArray header = partition.left(manifestHeader->HeaderLength * sizeof(UINT32));
|
|
UByteArray body = partition.mid(header.size());
|
|
|
|
info += usprintf(
|
|
"\nHeader type: %u\nHeader length: %Xh (%u)\nHeader version: %Xh\nFlags: %08Xh\nVendor: %Xh\n"
|
|
"Date: %Xh\nSize: %Xh (%u)\nVersion: %u.%u.%u.%u\nSecurity version number: %u\nModulus size: %Xh (%u)\nExponent size: %Xh (%u)",
|
|
manifestHeader->HeaderType,
|
|
manifestHeader->HeaderLength * sizeof(UINT32), manifestHeader->HeaderLength * sizeof(UINT32),
|
|
manifestHeader->HeaderVersion,
|
|
manifestHeader->Flags,
|
|
manifestHeader->Vendor,
|
|
manifestHeader->Date,
|
|
manifestHeader->Size * sizeof(UINT32), manifestHeader->Size * sizeof(UINT32),
|
|
manifestHeader->VersionMajor, manifestHeader->VersionMinor, manifestHeader->VersionBugfix, manifestHeader->VersionBuild,
|
|
manifestHeader->SecurityVersion,
|
|
manifestHeader->ModulusSize * sizeof(UINT32), manifestHeader->ModulusSize * sizeof(UINT32),
|
|
manifestHeader->ExponentSize * sizeof(UINT32), manifestHeader->ExponentSize * sizeof(UINT32));
|
|
|
|
// Add tree item
|
|
UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::ManifestCpdPartition, name, UString(), info, header, body, UByteArray(), Fixed, parent);
|
|
|
|
// Parse data as extensions area
|
|
parseExtensionsArea(partitionIndex);
|
|
}
|
|
}
|
|
}
|
|
// It's a metadata
|
|
else if (name.contains(".met")) {
|
|
info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ",
|
|
partition.size(), partition.size(),
|
|
partitions[i].ptEntry.Offset.Offset,
|
|
partitions[i].ptEntry.Length)
|
|
+ (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No");
|
|
|
|
// Calculate SHA256 hash over the metadata and add it to it's info
|
|
UByteArray hash(SHA256_DIGEST_SIZE, '\x00');
|
|
sha256(partition.constData(), partition.size(), hash.data());
|
|
info += UString("\nMetadata hash: ") + UString(hash.toHex().constData());
|
|
|
|
// Add three item
|
|
UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::MetadataCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
|
|
// Parse data as extensions area
|
|
parseExtensionsArea(partitionIndex);
|
|
}
|
|
// It's a key
|
|
else if (name.contains(".key")) {
|
|
info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ",
|
|
partition.size(), partition.size(),
|
|
partitions[i].ptEntry.Offset.Offset,
|
|
partitions[i].ptEntry.Length)
|
|
+ (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No");
|
|
|
|
// Calculate SHA256 hash over the key and add it to it's info
|
|
UByteArray hash(SHA256_DIGEST_SIZE, '\x00');
|
|
sha256(partition.constData(), partition.size(), hash.data());
|
|
info += UString("\nHash: ") + UString(hash.toHex().constData());
|
|
|
|
// Add three item
|
|
UModelIndex partitionIndex = model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::KeyCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
|
|
// Parse data as extensions area
|
|
parseExtensionsArea(partitionIndex);
|
|
}
|
|
// It's a code
|
|
else {
|
|
info = usprintf("Full size: %Xh (%u)\nEntry offset: %Xh\nEntry length: %Xh\nHuffman compressed: ",
|
|
partition.size(), partition.size(),
|
|
partitions[i].ptEntry.Offset.Offset,
|
|
partitions[i].ptEntry.Length)
|
|
+ (partitions[i].ptEntry.Offset.HuffmanCompressed ? "Yes" : "No");
|
|
|
|
// Calculate SHA256 hash over the code and add it to it's info
|
|
UByteArray hash(SHA256_DIGEST_SIZE, '\x00');
|
|
sha256(partition.constData(), partition.size(), hash.data());
|
|
info += UString("\nHash: ") + UString(hash.toHex().constData());
|
|
|
|
model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::CpdPartition, Subtypes::CodeCpdPartition, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
}
|
|
}
|
|
else if (partitions[i].type == Types::Padding) {
|
|
UByteArray partition = directory.mid(partitions[i].ptEntry.Offset.Offset, partitions[i].ptEntry.Length);
|
|
|
|
// Get info
|
|
name = UString("Padding");
|
|
info = usprintf("Full size: %Xh (%u)", partition.size(), partition.size());
|
|
|
|
// Add tree item
|
|
model->addItem(localOffset + partitions[i].ptEntry.Offset.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
|
|
}
|
|
else {
|
|
msg(usprintf("%s: CPD partition of unknown type found", __FUNCTION__), parent);
|
|
return U_INVALID_ME_PARTITION_TABLE;
|
|
}
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS MeParser::parseExtensionsArea(const UModelIndex & index)
|
|
{
|
|
if (!index.isValid()) {
|
|
return U_INVALID_PARAMETER;
|
|
}
|
|
|
|
UByteArray body = model->body(index);
|
|
UINT32 offset = 0;
|
|
while (offset < (UINT32)body.size()) {
|
|
const ME_CPD_EXTENTION_HEADER* extHeader = (const ME_CPD_EXTENTION_HEADER*) (body.constData() + offset);
|
|
if (extHeader->Length <= ((UINT32)body.size() - offset)) {
|
|
UByteArray partition = body.mid(offset, extHeader->Length);
|
|
|
|
UString name = meExtensionTypeToUstring(extHeader->Type);
|
|
UString info = usprintf("Full size: %Xh (%u)\nType: %Xh", partition.size(), partition.size(), extHeader->Type);
|
|
|
|
// Parse Signed Package Info a bit further
|
|
bool parsed = false;
|
|
if (extHeader->Type == 15) {
|
|
UByteArray header = partition.left(sizeof(ME_CPD_EXT_SIGNED_PACKAGE_INFO));
|
|
UByteArray data = partition.mid(header.size());
|
|
|
|
const ME_CPD_EXT_SIGNED_PACKAGE_INFO* infoHeader = (const ME_CPD_EXT_SIGNED_PACKAGE_INFO*)header.constData();
|
|
|
|
info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %Xh\n"
|
|
"Package name: %c%c%c%c\nVersion control number: %Xh\nSecurity version number: %Xh\n"
|
|
"Usage bitmap: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
|
partition.size(), partition.size(),
|
|
header.size(), header.size(),
|
|
body.size(), body.size(),
|
|
infoHeader->ExtensionType,
|
|
infoHeader->PackageName[0], infoHeader->PackageName[1], infoHeader->PackageName[2], infoHeader->PackageName[3],
|
|
infoHeader->Vcn,
|
|
infoHeader->Svn,
|
|
infoHeader->UsageBitmap[0], infoHeader->UsageBitmap[1], infoHeader->UsageBitmap[2], infoHeader->UsageBitmap[3],
|
|
infoHeader->UsageBitmap[4], infoHeader->UsageBitmap[5], infoHeader->UsageBitmap[6], infoHeader->UsageBitmap[7],
|
|
infoHeader->UsageBitmap[8], infoHeader->UsageBitmap[9], infoHeader->UsageBitmap[10], infoHeader->UsageBitmap[11],
|
|
infoHeader->UsageBitmap[12], infoHeader->UsageBitmap[13], infoHeader->UsageBitmap[14], infoHeader->UsageBitmap[15]);
|
|
|
|
// Add tree item
|
|
UModelIndex infoIndex = model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, header, data, UByteArray(), Fixed, index);
|
|
parseSignedPackageInfoData(infoIndex);
|
|
parsed = true;
|
|
}
|
|
|
|
// Parse IFWI Partition Manifest a bit further
|
|
else if (extHeader->Type == 22) {
|
|
const ME_CPD_EXT_IFWI_PARTITION_MANIFEST* attrHeader = (const ME_CPD_EXT_IFWI_PARTITION_MANIFEST*)partition.constData();
|
|
|
|
// This hash is stored reversed, because why the hell not
|
|
// Need to reverse it back to normal
|
|
UByteArray hash((const char*)&attrHeader->CompletePartitionHash, sizeof(attrHeader->CompletePartitionHash));
|
|
std::reverse(hash.begin(), hash.end());
|
|
|
|
info = usprintf("Full size: %Xh (%u)\nType: %Xh\n"
|
|
"Partition name: %c%c%c%c\nPartition length: %Xh\nPartition version major: %Xh\nPartition version minor: %Xh\n"
|
|
"Data format version: %Xh\nInstance ID: %Xh\nHash algorithm: %Xh\nHash size: %Xh\nAction on update: %Xh",
|
|
partition.size(), partition.size(),
|
|
attrHeader->ExtensionType,
|
|
attrHeader->PartitionName[0], attrHeader->PartitionName[1], attrHeader->PartitionName[2], attrHeader->PartitionName[3],
|
|
attrHeader->CompletePartitionLength,
|
|
attrHeader->PartitionVersionMajor, attrHeader->PartitionVersionMinor,
|
|
attrHeader->DataFormatVersion,
|
|
attrHeader->InstanceId,
|
|
attrHeader->HashAlgorithm,
|
|
attrHeader->HashSize,
|
|
attrHeader->ActionOnUpdate)
|
|
+ UString("\nSupport multiple instances: ") + (attrHeader->SupportMultipleInstances ? "Yes" : "No")
|
|
+ UString("\nSupport API version based update: ") + (attrHeader->SupportApiVersionBasedUpdate ? "Yes" : "No")
|
|
+ UString("\nObey full update rules: ") + (attrHeader->ObeyFullUpdateRules ? "Yes" : "No")
|
|
+ UString("\nIFR enable only: ") + (attrHeader->IfrEnableOnly ? "Yes" : "No")
|
|
+ UString("\nAllow cross point update: ") + (attrHeader->AllowCrossPointUpdate ? "Yes" : "No")
|
|
+ UString("\nAllow cross hotfix update: ") + (attrHeader->AllowCrossHotfixUpdate ? "Yes" : "No")
|
|
+ UString("\nPartial update only: ") + (attrHeader->PartialUpdateOnly ? "Yes" : "No")
|
|
+ UString("\nPartition hash: ") + UString(hash.toHex().constData());
|
|
|
|
// Add tree item
|
|
model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index);
|
|
parsed = true;
|
|
}
|
|
|
|
// Parse Module Attributes a bit further
|
|
else if (extHeader->Type == 10) {
|
|
const ME_CPD_EXT_MODULE_ATTRIBUTES* attrHeader = (const ME_CPD_EXT_MODULE_ATTRIBUTES*)partition.constData();
|
|
|
|
// This hash is stored reversed, because why the hell not
|
|
// Need to reverse it back to normal
|
|
UByteArray hash((const char*)&attrHeader->ImageHash, sizeof(attrHeader->ImageHash));
|
|
std::reverse(hash.begin(), hash.end());
|
|
|
|
info = usprintf("Full size: %Xh (%u)\nType: %Xh\n"
|
|
"Compression type: %Xh\nUncompressed size: %Xh (%u)\nCompressed size: %Xh (%u)\nGlobal module ID: %Xh\nImage hash: ",
|
|
partition.size(), partition.size(),
|
|
attrHeader->ExtensionType,
|
|
attrHeader->CompressionType,
|
|
attrHeader->UncompressedSize, attrHeader->UncompressedSize,
|
|
attrHeader->CompressedSize, attrHeader->CompressedSize,
|
|
attrHeader->GlobalModuleId) + UString(hash.toHex().constData());
|
|
|
|
// Add tree item
|
|
model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index);
|
|
parsed = true;
|
|
}
|
|
|
|
if (!parsed) {
|
|
// Add tree item, if needed
|
|
model->addItem(offset, Types::CpdExtension, 0, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, index);
|
|
}
|
|
|
|
offset += extHeader->Length;
|
|
}
|
|
else break;
|
|
// TODO: add padding at the end
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
USTATUS MeParser::parseSignedPackageInfoData(const UModelIndex & index)
|
|
{
|
|
if (!index.isValid()) {
|
|
return U_INVALID_PARAMETER;
|
|
}
|
|
|
|
UByteArray body = model->body(index);
|
|
UINT32 offset = 0;
|
|
while (offset < (UINT32)body.size()) {
|
|
const ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES* moduleHeader = (const ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES*)(body.constData() + offset);
|
|
if (sizeof(ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES) <= ((UINT32)body.size() - offset)) {
|
|
UByteArray module((const char*)moduleHeader,sizeof(ME_CPD_EXT_SIGNED_PACKAGE_INFO_MODULES));
|
|
|
|
UString name = usprintf("%c%c%c%c%c%c%c%c%c%c%c%c",
|
|
moduleHeader->Name[0], moduleHeader->Name[1], moduleHeader->Name[2], moduleHeader->Name[3],
|
|
moduleHeader->Name[4], moduleHeader->Name[5], moduleHeader->Name[6], moduleHeader->Name[7],
|
|
moduleHeader->Name[8], moduleHeader->Name[9], moduleHeader->Name[10],moduleHeader->Name[11]);
|
|
|
|
// This hash is stored reversed, because why the hell not
|
|
// Need to reverse it back to normal
|
|
UByteArray hash((const char*)&moduleHeader->MetadataHash, sizeof(moduleHeader->MetadataHash));
|
|
std::reverse(hash.begin(), hash.end());
|
|
|
|
UString info = usprintf("Full size: %X (%u)\nType: %Xh\nHash algorithm: %Xh\nHash size: %Xh (%u)\nMetadata size: %Xh (%u)\nMetadata hash: ",
|
|
module.size(), module.size(),
|
|
moduleHeader->Type,
|
|
moduleHeader->HashAlgorithm,
|
|
moduleHeader->HashSize, moduleHeader->HashSize,
|
|
moduleHeader->MetadataSize, moduleHeader->MetadataSize) + UString(hash.toHex().constData());
|
|
// Add tree otem
|
|
model->addItem(offset, Types::CpdSpiEntry, 0, name, UString(), info, UByteArray(), module, UByteArray(), Fixed, index);
|
|
|
|
offset += module.size();
|
|
}
|
|
else break;
|
|
// TODO: add padding at the end
|
|
}
|
|
|
|
return U_SUCCESS;
|
|
}
|
|
|
|
#endif // U_ENABLE_ME_PARSING_SUPPORT
|
|
|