diff --git a/common/generated/phoenix_vss2.cpp b/common/generated/phoenix_vss2.cpp index f9b03bd..45ec1b1 100644 --- a/common/generated/phoenix_vss2.cpp +++ b/common/generated/phoenix_vss2.cpp @@ -8,7 +8,7 @@ phoenix_vss2_t::phoenix_vss2_t(kaitai::kstream* p__io, kaitai::kstruct* p__paren m__root = this; (void)p__root; m_body = nullptr; m__io__raw_body = nullptr; - f_header_size = false; + f_len_vss2_store_header = false; _read(); } @@ -39,7 +39,7 @@ void phoenix_vss2_t::_read() { m_vss2_size = m__io->read_u4le(); { uint32_t _ = vss2_size(); - if (!( ((_ > header_size()) && (_ < 4294967295UL)) )) { + if (!( ((_ > len_vss2_store_header()) && (_ < 4294967295UL)) )) { throw kaitai::validation_expr_error(vss2_size(), _io(), std::string("/seq/3")); } } @@ -53,7 +53,7 @@ void phoenix_vss2_t::_read() { m_state = m__io->read_u1(); m_reserved = m__io->read_u2le(); m_reserved1 = m__io->read_u4le(); - m__raw_body = m__io->read_bytes((vss2_size() - header_size())); + m__raw_body = m__io->read_bytes((vss2_size() - len_vss2_store_header())); m__io__raw_body = std::unique_ptr(new kaitai::kstream(m__raw_body)); m_body = std::unique_ptr(new vss2_store_body_t(m__io__raw_body.get(), this, m__root)); } @@ -125,10 +125,13 @@ phoenix_vss2_t::vss2_variable_t::vss2_variable_t(kaitai::kstream* p__io, phoenix m__root = p__root; m_attributes = nullptr; f_is_auth = false; + f_len_standard_header = false; f_end_offset_auth = false; f_len_alignment_padding = false; + f_len_auth_header = false; f_end_offset = false; f_len_alignment_padding_auth = false; + f_is_valid = false; f_offset = false; _read(); } @@ -298,10 +301,18 @@ bool phoenix_vss2_t::vss2_variable_t::is_auth() { return m_is_auth; } +int8_t phoenix_vss2_t::vss2_variable_t::len_standard_header() { + if (f_len_standard_header) + return m_len_standard_header; + m_len_standard_header = 32; + f_len_standard_header = true; + return m_len_standard_header; +} + int32_t phoenix_vss2_t::vss2_variable_t::end_offset_auth() { if (f_end_offset_auth) return m_end_offset_auth; - m_end_offset_auth = _io()->pos(); + m_end_offset_auth = (int32_t)_io()->pos(); f_end_offset_auth = true; return m_end_offset_auth; } @@ -314,10 +325,18 @@ int32_t phoenix_vss2_t::vss2_variable_t::len_alignment_padding() { return m_len_alignment_padding; } +int8_t phoenix_vss2_t::vss2_variable_t::len_auth_header() { + if (f_len_auth_header) + return m_len_auth_header; + m_len_auth_header = 60; + f_len_auth_header = true; + return m_len_auth_header; +} + int32_t phoenix_vss2_t::vss2_variable_t::end_offset() { if (f_end_offset) return m_end_offset; - m_end_offset = _io()->pos(); + m_end_offset = (int32_t)_io()->pos(); f_end_offset = true; return m_end_offset; } @@ -330,18 +349,26 @@ int32_t phoenix_vss2_t::vss2_variable_t::len_alignment_padding_auth() { return m_len_alignment_padding_auth; } +bool phoenix_vss2_t::vss2_variable_t::is_valid() { + if (f_is_valid) + return m_is_valid; + m_is_valid = ((state() == 127) || (state() == 63)) ; + f_is_valid = true; + return m_is_valid; +} + int32_t phoenix_vss2_t::vss2_variable_t::offset() { if (f_offset) return m_offset; - m_offset = _io()->pos(); + m_offset = (int32_t)_io()->pos(); f_offset = true; return m_offset; } -int32_t phoenix_vss2_t::header_size() { - if (f_header_size) - return m_header_size; - m_header_size = (7 * 4); - f_header_size = true; - return m_header_size; +int32_t phoenix_vss2_t::len_vss2_store_header() { + if (f_len_vss2_store_header) + return m_len_vss2_store_header; + m_len_vss2_store_header = (7 * 4); + f_len_vss2_store_header = true; + return m_len_vss2_store_header; } diff --git a/common/generated/phoenix_vss2.h b/common/generated/phoenix_vss2.h index 98667ad..89c5026 100644 --- a/common/generated/phoenix_vss2.h +++ b/common/generated/phoenix_vss2.h @@ -109,6 +109,13 @@ public: public: bool is_auth(); + private: + bool f_len_standard_header; + int8_t m_len_standard_header; + + public: + int8_t len_standard_header(); + private: bool f_end_offset_auth; int32_t m_end_offset_auth; @@ -123,6 +130,13 @@ public: public: int32_t len_alignment_padding(); + private: + bool f_len_auth_header; + int8_t m_len_auth_header; + + public: + int8_t len_auth_header(); + private: bool f_end_offset; int32_t m_end_offset; @@ -137,6 +151,13 @@ public: public: int32_t len_alignment_padding_auth(); + private: + bool f_is_valid; + bool m_is_valid; + + public: + bool is_valid(); + private: bool f_offset; int32_t m_offset; @@ -316,11 +337,11 @@ public: }; private: - bool f_header_size; - int32_t m_header_size; + bool f_len_vss2_store_header; + int32_t m_len_vss2_store_header; public: - int32_t header_size(); + int32_t len_vss2_store_header(); private: uint32_t m_signature; diff --git a/common/ksy/phoenix_vss2.ksy b/common/ksy/phoenix_vss2.ksy index 6d0f63f..f421ba0 100644 --- a/common/ksy/phoenix_vss2.ksy +++ b/common/ksy/phoenix_vss2.ksy @@ -23,7 +23,7 @@ seq: - id: vss2_size type: u4 valid: - expr: _ > header_size and _ < 0xFFFFFFFF + expr: _ > len_vss2_store_header and _ < 0xFFFFFFFF - id: format type: u1 valid: @@ -36,9 +36,9 @@ seq: type: u4 - id: body type: vss2_store_body - size: vss2_size - header_size + size: vss2_size - len_vss2_store_header instances: - header_size: + len_vss2_store_header: value: 7 * sizeof types: @@ -150,5 +150,11 @@ types: value: (((end_offset - offset)+3) & ~3) - (end_offset - offset) len_alignment_padding_auth: value: (((end_offset_auth - offset)+3) & ~3) - (end_offset - offset) + is_valid: + value: state == 0x7F or state == 0x3F is_auth: value: (attributes.auth_write or attributes.time_based_auth or attributes.append_write) or (len_name == 0 or len_data == 0) + len_auth_header: + value: 60 + len_standard_header: + value: 32 diff --git a/common/nvramparser.cpp b/common/nvramparser.cpp index 8c157c3..5ad586a 100644 --- a/common/nvramparser.cpp +++ b/common/nvramparser.cpp @@ -361,7 +361,7 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index) UString text; info.clear(); name.clear(); - + // This is thew terminating entry, needs special processing if (variable->_is_null_signature_last()) { // Add free space or padding after all variables, if needed @@ -385,7 +385,7 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index) // This is a normal entry UINT32 variableSize; - if (variable->is_intel_legacy()) { + if (variable->is_intel_legacy()) { // Intel legacy subtype = Subtypes::IntelVssEntry; // Needs some additional parsing of variable->intel_legacy_data to separate the name from the value text = uFromUcs2(variable->intel_legacy_data().c_str()); @@ -397,7 +397,7 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index) name = guidToUString(variableGuid); info += UString("Variable GUID: ") + guidToUString(variableGuid, false) + "\n"; } - else if (variable->is_auth()) { + else if (variable->is_auth()) { // Authenticated subtype = Subtypes::AuthVssEntry; header = vss.mid(vssVariableOffset, variable->len_auth_header() + variable->len_name_auth()); body = vss.mid(vssVariableOffset + header.size(), variable->len_data_auth()); @@ -407,7 +407,7 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index) text = uFromUcs2(variable->name_auth().c_str()); info += UString("Variable GUID: ") + guidToUString(variableGuid, false) + "\n"; } - else if (!variable->_is_null_apple_data_crc32()) { + else if (!variable->_is_null_apple_data_crc32()) { // Apple CRC32 subtype = Subtypes::AppleVssEntry; header = vss.mid(vssVariableOffset, variable->len_apple_header() + variable->len_name()); body = vss.mid(vssVariableOffset + header.size(), variable->len_data()); @@ -417,7 +417,7 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index) text = uFromUcs2(variable->name().c_str()); info += UString("Variable GUID: ") + guidToUString(variableGuid, false) + "\n"; } - else { + else { // Standard subtype = Subtypes::StandardVssEntry; header = vss.mid(vssVariableOffset, variable->len_standard_header() + variable->len_name()); body = vss.mid(vssVariableOffset + header.size(), variable->len_data()); @@ -445,7 +445,7 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index) + (variable->attributes()->apple_data_checksum() << 31); // Add generic info - info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nReserved: %02X\nAttributes: %08Xh (", + info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nReserved: %02Xh\nAttributes: %08Xh (", variableSize, variableSize, (UINT32)header.size(), (UINT32)header.size(), (UINT32)body.size(), (UINT32)body.size(), @@ -481,7 +481,141 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index) } // VSS2 + try { + UByteArray vss2 = volumeBody.mid(storeOffset); + umemstream is(vss2.constData(), vss2.size()); + kaitai::kstream ks(&is); + phoenix_vss2_t parsed(&ks); + // VSS2 store at current offset parsed correctly + // Check if we need to add a padding before it + if (!outerPadding.isEmpty()) { + UString info = usprintf("Full size: %Xh (%u)", (UINT32)outerPadding.size(), (UINT32)outerPadding.size()); + model->addItem(previousStoreEndOffset, Types::Padding, getPaddingType(outerPadding), UString("Padding"), UString(), info, UByteArray(), outerPadding, UByteArray(), Fixed, index); + outerPadding.clear(); + } + + // Construct header and body + UByteArray header = vss2.left(parsed.len_vss2_store_header()); + UByteArray body = vss2.mid(header.size(), parsed.vss2_size() - header.size()); + + // Add info + UString name = UString("VSS2 store"); + UString info; + if (parsed.signature() == NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID_PART1) { + info = UString("Signature: AAF32C78-947B-439A-A180-2E144EC37792\n"); + } + else { + info = UString("Signature: DDCF3617-3275-4164-98B6-FE85707FFE7D\n"); + } + + info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nFormat: %02Xh\nState: %02Xh\nReserved: %02Xh\nReserved1: %04Xh", + parsed.vss2_size() , parsed.vss2_size(), + (UINT32)header.size(), (UINT32)header.size(), + (UINT32)body.size(), (UINT32)body.size(), + parsed.format(), + parsed.state(), + parsed.reserved(), + parsed.reserved1()); + + // Add header tree item + UModelIndex headerIndex = model->addItem(localOffset + storeOffset, Types::VssStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, index); + + UINT32 vss2VariableOffset = storeOffset + parsed.len_vss2_store_header(); + for (const auto & variable : *parsed.body()->variables()) { + UINT8 subtype; + UString text; + info.clear(); + name.clear(); + + // This is thew terminating entry, needs special processing + if (variable->_is_null_signature_last()) { + // Add free space or padding after all variables, if needed + if (vss2VariableOffset < parsed.vss2_size()) { + UByteArray freeSpace = vss2.mid(vss2VariableOffset, parsed.vss2_size() - vss2VariableOffset); + // Add info + info = usprintf("Full size: %Xh (%u)", (UINT32)freeSpace.size(), (UINT32)freeSpace.size()); + + // Check that remaining unparsed bytes are actually empty + if (freeSpace.count(emptyByte) == freeSpace.size()) { // Free space + // Add tree item + model->addItem(vss2VariableOffset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + } + else { + // Add tree item + model->addItem(vss2VariableOffset, Types::Padding, getPaddingType(freeSpace), UString("Padding"), UString(), info, UByteArray(), freeSpace, UByteArray(), Fixed, headerIndex); + } + } + break; + } + + // This is a normal entry + UINT32 variableSize; + if (variable->is_auth()) { // Authenticated + subtype = Subtypes::AuthVssEntry; + header = vss2.mid(vss2VariableOffset, variable->len_auth_header() + variable->len_name_auth()); + body = vss2.mid(vss2VariableOffset + header.size(), variable->len_data_auth()); + variableSize = (UINT32)(header.size() + body.size()); + const EFI_GUID variableGuid = readUnaligned((const EFI_GUID*)(variable->vendor_guid().c_str())); + name = guidToUString(variableGuid); + text = uFromUcs2(variable->name_auth().c_str()); + info += UString("Variable GUID: ") + guidToUString(variableGuid, false) + "\n"; + } + else { // Standard + subtype = Subtypes::StandardVssEntry; + header = vss2.mid(vss2VariableOffset, variable->len_standard_header() + variable->len_name()); + body = vss2.mid(vss2VariableOffset + header.size(), variable->len_data()); + variableSize = (UINT32)(header.size() + body.size()); + const EFI_GUID variableGuid = readUnaligned((const EFI_GUID*)(variable->vendor_guid().c_str())); + name = guidToUString(variableGuid); + text = uFromUcs2(variable->name().c_str()); + info += UString("Variable GUID: ") + guidToUString(variableGuid, false) + "\n"; + } + + // Override variable type to Invalid if needed + if (!variable->is_valid()) { + subtype = Subtypes::InvalidVssEntry; + name = UString("Invalid"); + text.clear(); + } + + const UINT32 variableAttributes = variable->attributes()->non_volatile() + + (variable->attributes()->boot_service() << 1) + + (variable->attributes()->runtime() << 2) + + (variable->attributes()->hw_error_record() << 3) + + (variable->attributes()->auth_write() << 4) + + (variable->attributes()->time_based_auth() << 5) + + (variable->attributes()->append_write() << 6); + + // Add generic info + info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nReserved: %02Xh\nAttributes: %08Xh (", + variableSize, variableSize, + (UINT32)header.size(), (UINT32)header.size(), + (UINT32)body.size(), (UINT32)body.size(), + variable->state(), + variable->reserved(), + variableAttributes) + vssAttributesToUString(variableAttributes) + UString(")"); + + // Add specific info + if (variable->is_auth()) { + UINT64 monotonicCounter = (UINT64)variable->len_name() + ((UINT64)variable->len_data() << 32); + info += usprintf("\nMonotonic counter: %" PRIX64 "h\nTimestamp: ", monotonicCounter) + efiTimeToUString(*(const EFI_TIME*)variable->timestamp().c_str()) + + usprintf("\nPubKey index: %u", variable->pubkey_index()); + } + + // Add tree item + model->addItem(vss2VariableOffset, Types::VssEntry, subtype, name, text, info, header, body, UByteArray(), Fixed, headerIndex); + + vss2VariableOffset += variableSize; + } + + storeFound = true; + storeOffset += parsed.vss2_size(); + previousStoreEndOffset = storeOffset; + } catch (...) { + // Parsing failed, try something else + } + // FDC // EVSA @@ -496,7 +630,7 @@ USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index) // Intel uCode // Padding - outerPadding += volumeBody.at(storeOffset); + outerPadding.append(volumeBody.at(storeOffset)); } // Add padding at the very end diff --git a/kaitai_regenerate.sh b/kaitai_regenerate.sh index 92a60b0..a343810 100755 --- a/kaitai_regenerate.sh +++ b/kaitai_regenerate.sh @@ -50,6 +50,14 @@ ${UFIND} common/generated ${UFINDOPT} \ -name 'ami_nvar.cpp' \ -exec sed -i.bak 's/_offset = _io()->pos();/_offset = (int32_t)_io()->pos();/g' {} + || exit 1 +# Suppress type downcast warning in phoenix_vss2.cpp +${UFIND} common/generated ${UFINDOPT} \ + -name 'phoenix_vss2.cpp' \ + -exec sed -i.bak 's/_offset = _io()->pos();/_offset = (int32_t)_io()->pos();/g' {} + || exit 1 +${UFIND} common/generated ${UFINDOPT} \ + -name 'phoenix_vss2.cpp' \ + -exec sed -i.bak 's/_offset_auth = _io()->pos();/_offset_auth = (int32_t)_io()->pos();/g' {} + || exit 1 + # Remove backup files ${UFIND} common/generated ${UFINDOPT} \ -regex '.*\.(bak)' \