patterns: Add UEFI Firmare Volume Variable Store pattern (#421)

* Add UEFI Firmare Volume Variable Store pattern

Add a pattern for UEFI Firmare Volume Variable store.
This file type is commonly used with virtual machine UEFI variable files,
like OVMF.fd used with QEMU. You could also extract a UEFI firmware binary
from a flash device, search for the FV Variable Store, and set this pattern
to the FV address.

Signed-off-by: Marc Jones <marcj303@gmail.com>

* Fixed description pragma

---------

Signed-off-by: Marc Jones <marcj303@gmail.com>
Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
Marc Jones
2025-08-31 03:38:32 -06:00
committed by GitHub
parent 6630180276
commit 284ca8d325
3 changed files with 213 additions and 0 deletions

View File

@@ -184,6 +184,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
| UPK | | [`patterns/upk-ue3.hexpat`](patterns/upk-ue3.hexpat) | Unreal Engine 3 UPK file | | UPK | | [`patterns/upk-ue3.hexpat`](patterns/upk-ue3.hexpat) | Unreal Engine 3 UPK file |
| UEFI | | [`patterns/uefi.hexpat`](patterns/uefi.hexpat)` | UEFI structs for parsing efivars | | UEFI | | [`patterns/uefi.hexpat`](patterns/uefi.hexpat)` | UEFI structs for parsing efivars |
| UEFI Boot Entry | | [`patterns/uefi_boot_entry.hexpat`](patterns/uefi_boot_entry.hexpat) | UEFI Boot Entry (Load option) | | UEFI Boot Entry | | [`patterns/uefi_boot_entry.hexpat`](patterns/uefi_boot_entry.hexpat) | UEFI Boot Entry (Load option) |
| UEFI Variable Store | | [`patterns/uefi_fv_varstore.hexpat`](patterns/uefi_fv_varstore.hexpat) | UEFI Firmware Volume Variable Store |
| UF2 | | [`patterns/uf2.hexpat`](patterns/uf2.hexpat) | [USB Flashing Format](https://github.com/microsoft/uf2) | | UF2 | | [`patterns/uf2.hexpat`](patterns/uf2.hexpat) | [USB Flashing Format](https://github.com/microsoft/uf2) |
| Valve VPK | | [`patterns/valve_vpk.hexpat`](valve_vpk.hexpat) | Valve Package File | | Valve VPK | | [`patterns/valve_vpk.hexpat`](valve_vpk.hexpat) | Valve Package File |
| VBMeta | | [`patterns/vbmeta.hexpat`](patterns/vbmeta.hexpat) | Android VBMeta image | | VBMeta | | [`patterns/vbmeta.hexpat`](patterns/vbmeta.hexpat) | Android VBMeta image |

View File

@@ -0,0 +1,212 @@
/*
* ImHex Pattern for UEFI Firmware Volume Variable Store
*
* This file type is commonly used with virtual machine UEFI variable files, like OVMF.fd
* used with QEMU. You could also extract a UEFI firmware binary from a flash device,
* search for the FV Variable Store, and set this pattern to the FV address.
*
* A 'custom_vars.fd' can be generated with these tools:
*
* https://gitlab.com/kraxel/virt-firmware
* https://github.com/rhuefi/qemu-ovmf-secureboot/tree/master
* https://github.com/LongSoft/UEFITool
*
* 1. Generate a blank .fd file with ovmfvartool.
*
* $ ovmfvartool generate-blank blank.fd
*
* 2. Enroll the Redhat and Microsoft keys with virt-fw-vars in custom_vars.fd.
*
* $ virt-fw-vars -i blank.fd -o custom_vars.fd --enroll-redhat --secure-boot
*
* 3. Dump custom_vars.fd contents
*
* $virt-fw-vars -i custom_vars.fd -pvx
*
* or
*
* $ uefitool custom_vars.fd
*
* or use this pattern with ImHex!
*/
#pragma author Marc Jones
#pragma description UEFI Firmware Volume Variable Store
// #pragma debug
import std.core;
import std.mem;
import type.guid;
// --- GUIDs ---
#define NVRAM_FV "{FFF12B8D-7696-4C8B-A985-2747075B4F50}"
#define NVRAM_VARSTORE "{AAF32C78-947B-439A-A180-2E144EC37792}"
// --- Enumerations and Bitfields ---
// Describes the type of a file within the Firmware File System.
enum FfsFileType : u8 {
RAW = 0x01,
FREEFORM = 0x02,
SECURITY_CORE = 0x03,
PEI_CORE = 0x04,
DXE_CORE = 0x05,
PEIM = 0x06,
DRIVER = 0x07,
COMBINED_PEIM_DRIVER = 0x08,
APPLICATION = 0x09,
SMM = 0x0A,
FIRMWARE_VOLUME_IMAGE = 0x0B,
COMBINED_SMM_DXE = 0x0C,
SMM_CORE = 0x0D,
FFS_PAD = 0xF0,
};
// Attributes for a UEFI variable, indicating its properties and accessibility.
bitfield VariableAttributes{
NON_VOLATILE : 1;
BOOTSERVICE_ACCESS : 1;
RUNTIME_ACCESS : 1;
HARDWARE_ERROR_RECORD : 1;
AUTHENTICATED_WRITE_ACCESS : 1;
TIME_BASED_AUTHENTICATED_WRITE_ACCESS : 1;
APPEND_WRITE : 1;
RSVD: 25;
};
//
// Variable Store Header Format & State flags
//
enum VariableStoreFormat : u8 {
VARIABLE_STORE_FORMATTED = 0x5a,
};
enum VariableStoreState : u8 {
VARIABLE_STORE_HEALTHY = 0xfe,
};
//
// Variable State flags. See https://countchu.blogspot.com/2014/09/the-life-cycle-of-uefi-variable-in.html
//
enum VariableState : u8 {
VAR_IN_DELETED_TRANSITION = 0xfe,
VAR_DELETED = 0xfd,
VAR_HEADER_VALID_ONLY = 0x7f,
VAR_ADDED = 0x3f,
VAR_ADDED__VAR_IN_DELETED_TRANSITION__VAR_DELETED = 0x3c,
VAR_ADDED__VAR_IN_DELETED_TRANSITION = 0x3e,
VAR_ADDED__VAR_DELETED = 0x3d,
};
// --- Other Structures ---
struct EFI_TIME {
u16 Year;
u8 Month;
u8 Day;
u8 Hour;
u8 Minute;
u8 Second;
u8 Pad1;
u32 Nanosecond;
u16 TimeZone;
u8 Daylight;
u8 Pad2;
};
// --- Firmware Volume Structures ---
// Header for a block in the firmware volume map.
struct EFI_FV_BLOCK_MAP_ENTRY {
u32 NumBlocks;
u32 Length;
};
// The main header for a Firmware Volume.
struct EFI_FIRMWARE_VOLUME_HEADER {
u128 ZeroVector;
type::GUID FileSystemGuid;
u64 FvLength;
u32 Signature;
u32 Attributes;
u16 HeaderLength;
u16 Checksum;
u16 ExtHeaderOffset;
u8 Reserved;
u8 Revision;
EFI_FV_BLOCK_MAP_ENTRY BlockMap[while(std::mem::read_unsigned($, 4) != 0 || std::mem::read_unsigned($ + 4, 4) != 0)];
EFI_FV_BLOCK_MAP_ENTRY BlockMapTerminator; // After the loop, explicitly parse the (0,0) terminator entry
}[[single_color]];
// --- UEFI Variable Structures ---
struct VARIABLE_STORE_HEADER {
type::GUID Signature;
u32 Size;
VariableStoreFormat Format;
VariableStoreState State;
u16 Reserved;
u32 Reserved1;
}[[single_color]];
#define VAR_START_ID 0x55AA
struct VARIABLE_HEADER {
u16 StartId;
VariableState State;
u8 Reserved;
VariableAttributes Attributes;
u32 NameSize;
u32 DataSize;
type::GUID VendorGuid;
};
struct AUTHENTICATED_VARIABLE_HEADER {
u16 StartId;
VariableState State;
u8 Reserved;
VariableAttributes Attributes;
u64 MonotonicCount;
EFI_TIME TimeStamp;
u32 PubKeyIndex;
u32 NameSize;
u32 DataSize;
type::GUID VendorGuid;
};
struct UEFI_VARIABLE {
AUTHENTICATED_VARIABLE_HEADER Header; // TODO: Check authenticated vs normal variable...
char16 Name[Header.NameSize / 2]; // Name is a UTF-16 string
u8 Data[Header.DataSize];
// Align to the next 4-byte boundary for the next variable.
u8 pad[std::mem::align_to(4, sizeof(this)) - sizeof(this)];
} [[name(this.Name), single_color]];
// --- Main Pattern Entry Point ---
EFI_FIRMWARE_VOLUME_HEADER FV_Header @ 0;
if (std::core::formatted_value(FV_Header.FileSystemGuid) != "{FFF12B8D-7696-4C8B-A985-2747075B4F50}") {
std::error(std::format("Unknown FV_Header.FileSystemGuid {}", std::core::formatted_value(FV_Header.FileSystemGuid)));
}
// The next structure should be the Variable Store Header
VARIABLE_STORE_HEADER VarStore @ $;
if (std::core::formatted_value(VarStore.Signature) != NVRAM_VARSTORE) {
std::error(std::format("Unknown VarStore.Signature {}", std::core::formatted_value(VarStore.Signature)));
}
// Index through the Uefi variables until we don't find a Variable Signature 0x55AA
UEFI_VARIABLE UefiVars[while(std::mem::read_unsigned($, 2) == VAR_START_ID)] @ $;
// TODO: grey out the Uefi variables that are in the non-active state, != VAR_ADDED.

Binary file not shown.