diff --git a/src/boot.cpp b/src/boot.cpp index a447022..ace46a0 100644 --- a/src/boot.cpp +++ b/src/boot.cpp @@ -775,7 +775,16 @@ std::vector scan_boot(const std::string& path, std::span { static const uint8_t kFvh[4] = {'_', 'F', 'V', 'H'}; std::span head = data.subspan(0, std::min(data.size(), 64u << 20)); - if (find_bytes(head, kFvh, 4, 0) != std::string::npos) { + // A full BIOS image carries the FV header signature "_FVH" somewhere. A + // variable store extracted on its own (the usual chipsec/UEFITool artifact) + // has no FV wrapper: an EDK2 authenticated/variable store begins with its + // store GUID, an AMI store begins with an "NVAR" entry. Trigger on either + // so an extracted store is analyzed too; analyze_uefi self-filters (emits + // nothing without a real recovered variable), so the loose trigger is safe. + bool is_varstore = guid_at(data, 0, kAuthVarStore) || guid_at(data, 0, kVarStore) || + (data.size() >= 4 && data[0] == 'N' && data[1] == 'V' && + data[2] == 'A' && data[3] == 'R'); + if (is_varstore || find_bytes(head, kFvh, 4, 0) != std::string::npos) { analyze_uefi(data, out); if (!out.empty()) return out; } diff --git a/tests/test_boot.py b/tests/test_boot.py index d4a9bd6..4a21f55 100644 --- a/tests/test_boot.py +++ b/tests/test_boot.py @@ -457,6 +457,24 @@ def check(cond, msg): check("uefi-platform-key" in types(ph) and "uefi-secureboot-on" in types(ph), "nvar: FV at a nonzero offset (full-flash dump) still analyzed") + # A variable store extracted on its own (a chipsec/UEFITool artifact) has no FV + # wrapper, so no "_FVH" signature. It must still be analyzed -- the raw AMI store + # begins with an "NVAR" entry. A large defaults-container entry ("StdDefaults") + # sits up front, as on a real BIOS; the top-level PK/dbx after it are still + # recovered. Regression: without the store-start trigger this was silently + # skipped, so an extracted NVAR store reported nothing. + raw = b"".join(_nvar_entry(n, d) for (n, d) in + [("StdDefaults", b"\x00" * 200), ("PK", b"PKCERT" * 40), + ("KEK", b"KEK" * 30), ("db", b"DB" * 300), ("dbx", sha256_siglist(5)), + ("SecureBoot", b"\x01")]) + check(raw[:4] == b"NVAR", "nvar: standalone store begins with an NVAR entry (no FV)") + rh = run("varstore.bin", raw) + check("uefi-platform-key" in types(rh) and "uefi-secureboot-on" in types(rh), + "nvar: standalone extracted store (no FV wrapper) is analyzed") + rdbx = [x for x in rh if x["type"] == "uefi-dbx"] + check(bool(rdbx) and "5 revocation" in rdbx[0]["label"], + "nvar: standalone store dbx enumerated past a leading container entry") + # PKFAIL through the NVAR store: PK is the AMI "DO NOT TRUST" test key. nvpk = nvar_store([("PK", efi_sig_list(ami_cert)), ("SecureBoot", b"\x01")]) check("uefi-test-platform-key" in types(run("bios.bin", nvpk)),