Skip to content

Add UEFI Firmare Volume Variable Store pattern #421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,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 |
| 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 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) |
| Valve VPK | | [`patterns/valve_vpk.hexpat`](valve_vpk.hexpat) | Valve Package File |
| VBMeta | | [`patterns/vbmeta.hexpat`](patterns/vbmeta.hexpat) | Android VBMeta image |
Expand Down
212 changes: 212 additions & 0 deletions patterns/uefi_fv_varstore.hexpat
Original file line number Diff line number Diff line change
@@ -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 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...
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure how to define the struct based on the attribute inside the struct. Any advice would be helpful.

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}") {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the best way to check a GUID?

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.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure how to change the color attribute on the contents of a struct entry. I'd like to grey out "disabled" entries.

Binary file not shown.