From 5505621dcd0efbabd96826b662ed3248a42303f8 Mon Sep 17 00:00:00 2001 From: Alon Livne <2005alonlivne@gmail.com> Date: Tue, 30 Sep 2025 21:17:46 +0300 Subject: [PATCH 01/10] fix: remove ifdef for module --- shared/include/core/header.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/shared/include/core/header.h b/shared/include/core/header.h index 8cde0b5..98d4b4b 100644 --- a/shared/include/core/header.h +++ b/shared/include/core/header.h @@ -1,15 +1,10 @@ #ifndef _INCLUDE_CORE_HEADER #define _INCLUDE_CORE_HEADER -#include "core/consts.h" - -#if defined(CORE) || defined(UEFI) #include #include -#elif defined(MODULE) -#include -#endif +#include "core/consts.h" #include "utils.h" struct mem_area { From 0e33833a78386600aa0684e88f86bc96bad4e840 Mon Sep 17 00:00:00 2001 From: Alon Livne <2005alonlivne@gmail.com> Date: Tue, 30 Sep 2025 21:31:08 +0300 Subject: [PATCH 02/10] feature: uefi add g_core_header global --- uefi/app.c | 8 ++++-- uefi/core_header_utils.c | 60 +++++++++++++++++++++------------------- uefi/core_header_utils.h | 3 +- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/uefi/app.c b/uefi/app.c index ce23c21..fd93956 100644 --- a/uefi/app.c +++ b/uefi/app.c @@ -5,10 +5,13 @@ #include "acpi/hook_pts.h" #include "acpi/tables.h" +#include "core/header.h" #include "core_header_utils.h" #include "core_loader.h" #include "services/hooks_loader.h" +struct core_header* g_core_header = NULL; + EFI_STATUS efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable) { err_t err = SUCCESS; @@ -16,8 +19,7 @@ efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable) { InitializeLib(ImageHandle, SystemTable); TRACE("Loading core...\n"); - struct core_header* core_header = NULL; - CHECK_RETHROW(load_core(&core_header)); + CHECK_RETHROW(load_core(&g_core_header)); TRACE("Locating ACPI tables...\n"); CHECK_RETHROW(find_acpi_tables()); @@ -29,7 +31,7 @@ efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable) { CHECK_RETHROW(create_or_hook_pts()); TRACE("Filling core header...\n"); - CHECK_RETHROW(fill_core_header(core_header)); + CHECK_RETHROW(fill_core_header()); cleanup: if (IS_ERROR(err)) { diff --git a/uefi/core_header_utils.c b/uefi/core_header_utils.c index 3659ff0..f460811 100644 --- a/uefi/core_header_utils.c +++ b/uefi/core_header_utils.c @@ -2,8 +2,10 @@ #include #include +#include #include "acpi/tables.h" +#include "core/header.h" #include "pci.h" // TODO: This is currently hard coded to a virtio blk device. Make this @@ -13,40 +15,42 @@ static const struct pci_dev_id g_disk_pci_id = {.vendor_id = DISK_PCI_VENDOR_ID, .device_id = DISK_PCI_DEVICE_ID}; +extern struct core_header* g_core_header; + /** - * Fills `core_header.rsdp` with `g_rsdp`. + * Fills `g_core_header.rsdp` with `g_rsdp`. */ -static void fill_rsdp(struct core_header* core_header) { - core_header->rsdp = (uint64_t)(uintptr_t)g_rsdp; +static void fill_rsdp(void) { + g_core_header->rsdp = (uint64_t)(uintptr_t)g_rsdp; } /** - * Fills `core_header.facs` with `g_facs`. + * Fills `g_core_header.facs` with `g_facs`. */ -static void fill_facs(struct core_header* core_header) { - core_header->facs = (uint64_t)(uintptr_t)g_facs; +static void fill_facs(void) { + g_core_header->facs = (uint64_t)(uintptr_t)g_facs; } /** - * Locates the disk by enumerating the PCI bus, and fills `core_header->disk_pci` + * Locates the disk by enumerating the PCI bus, and fills `g_core_header->disk_pci` * with the disk's PCI location, and the BIOS-initialized BARs. */ -static err_t fill_disk_pci(struct core_header* core_header) { +static err_t fill_disk_pci(void) { err_t err = SUCCESS; struct pci_dev disk_pci_dev = {0}; CHECK_RETHROW(lookup_pci_dev(&disk_pci_dev, &g_disk_pci_id)); // Fill the disk's pci bus information. - core_header->disk_pci.addr.bus = disk_pci_dev.addr.bus; - core_header->disk_pci.addr.device = disk_pci_dev.addr.device; - core_header->disk_pci.addr.function = disk_pci_dev.addr.function; + g_core_header->disk_pci.addr.bus = disk_pci_dev.addr.bus; + g_core_header->disk_pci.addr.device = disk_pci_dev.addr.device; + g_core_header->disk_pci.addr.function = disk_pci_dev.addr.function; // Fill the disk's bars. struct pci_bar pci_bar = {0}; for (size_t i = 0; i < 6; i++) { CHECK_RETHROW(pci_get_bar(&disk_pci_dev, i, &pci_bar)); - core_header->disk_pci.bars[i] = pci_bar.addr; + g_core_header->disk_pci.bars[i] = pci_bar.addr; } cleanup: @@ -75,22 +79,22 @@ static bool is_memory_desc_usable(const EFI_MEMORY_DESCRIPTOR* desc) { } /** - * Inserts a memory descriptor into `core_header->ram_areas`. + * Inserts a memory descriptor into `g_core_header->ram_areas`. * * This also defragments consecutive descriptors so the table is as small as possible. */ -static err_t insert_desc(struct core_header* core_header, const EFI_MEMORY_DESCRIPTOR* desc) { +static err_t insert_desc(const EFI_MEMORY_DESCRIPTOR* desc) { err_t err = SUCCESS; - CHECK_TRACE(core_header->ram_areas_size < ARRAY_SIZE(core_header->ram_areas), + CHECK_TRACE(g_core_header->ram_areas_size < ARRAY_SIZE(g_core_header->ram_areas), "No space left for additional RAM areas!\n"); size_t desc_size = desc->NumberOfPages * EFI_PAGE_SIZE; size_t desc_end = desc->PhysicalStart + desc_size; // Try to defragment the new descriptor into an existing consecutive descriptor. - for (size_t i = 0; i < core_header->ram_areas_size; i++) { - struct mem_area* area = &core_header->ram_areas[i]; + for (size_t i = 0; i < g_core_header->ram_areas_size; i++) { + struct mem_area* area = &g_core_header->ram_areas[i]; if (area->start == desc_end) { // An existing descriptor begins where the new descriptor ends. @@ -103,19 +107,19 @@ static err_t insert_desc(struct core_header* core_header, const EFI_MEMORY_DESCR } } - core_header->ram_areas[core_header->ram_areas_size].start = desc->PhysicalStart; - core_header->ram_areas[core_header->ram_areas_size].size = desc_size; + g_core_header->ram_areas[g_core_header->ram_areas_size].start = desc->PhysicalStart; + g_core_header->ram_areas[g_core_header->ram_areas_size].size = desc_size; - core_header->ram_areas_size++; + g_core_header->ram_areas_size++; cleanup: return err; } /** - * Locates all the usable memory RAM areas using the `GetMemoryMap` boot service, and fills `core_header->ram_areas`. + * Locates all the usable memory RAM areas using the `GetMemoryMap` boot service, and fills `g_core_header->ram_areas`. */ -static err_t fill_ram_areas(struct core_header* core_header) { +static err_t fill_ram_areas(void) { err_t err = SUCCESS; EFI_MEMORY_DESCRIPTOR* memory_map = NULL; @@ -145,7 +149,7 @@ static err_t fill_ram_areas(struct core_header* core_header) { continue; } - CHECK_RETHROW(insert_desc(core_header, desc)); + CHECK_RETHROW(insert_desc(desc)); } cleanup: @@ -155,13 +159,13 @@ static err_t fill_ram_areas(struct core_header* core_header) { return err; } -err_t fill_core_header(struct core_header* core_header) { +err_t fill_core_header() { err_t err = SUCCESS; - fill_rsdp(core_header); - fill_facs(core_header); - CHECK_RETHROW(fill_disk_pci(core_header)); - CHECK_RETHROW(fill_ram_areas(core_header)); + fill_rsdp(); + fill_facs(); + CHECK_RETHROW(fill_disk_pci()); + CHECK_RETHROW(fill_ram_areas()); cleanup: return err; diff --git a/uefi/core_header_utils.h b/uefi/core_header_utils.h index e27b67b..51cd3d9 100644 --- a/uefi/core_header_utils.h +++ b/uefi/core_header_utils.h @@ -3,10 +3,9 @@ #include #include -#include "core/header.h" #include "error.h" /** * Fills the core header with all the required information. */ -err_t fill_core_header(struct core_header* core_header); +err_t fill_core_header(); From 74bbf045c0510721bd962f9bd0dee3715239d3d1 Mon Sep 17 00:00:00 2001 From: Alon Livne <2005alonlivne@gmail.com> Date: Tue, 30 Sep 2025 21:34:06 +0300 Subject: [PATCH 03/10] feature: add mem_area.h --- shared/include/core/header.h | 6 +----- shared/include/mem_area.h | 11 +++++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 shared/include/mem_area.h diff --git a/shared/include/core/header.h b/shared/include/core/header.h index 98d4b4b..16e98e8 100644 --- a/shared/include/core/header.h +++ b/shared/include/core/header.h @@ -5,13 +5,9 @@ #include #include "core/consts.h" +#include "mem_area.h" #include "utils.h" -struct mem_area { - uint64_t start; - uint64_t size; -}; - enum core_action { CORE_ACTION_INVALID, CORE_ACTION_STORE, diff --git a/shared/include/mem_area.h b/shared/include/mem_area.h new file mode 100644 index 0000000..45eb7e5 --- /dev/null +++ b/shared/include/mem_area.h @@ -0,0 +1,11 @@ +#ifndef _INCLUDE_MEM_AREA_H +#define _INCLUDE_MEM_AREA_H + +#include + +struct mem_area { + uint64_t start; + uint64_t size; +}; + +#endif From ae79d14a8572d6814bd3f6257a904ede2798607d Mon Sep 17 00:00:00 2001 From: Alon Livne <2005alonlivne@gmail.com> Date: Tue, 30 Sep 2025 21:44:55 +0300 Subject: [PATCH 04/10] feature: add is_mem_area_contained_in_range utility --- shared/include/mem_area.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shared/include/mem_area.h b/shared/include/mem_area.h index 45eb7e5..bda4fb5 100644 --- a/shared/include/mem_area.h +++ b/shared/include/mem_area.h @@ -1,6 +1,7 @@ #ifndef _INCLUDE_MEM_AREA_H #define _INCLUDE_MEM_AREA_H +#include #include struct mem_area { @@ -8,4 +9,8 @@ struct mem_area { uint64_t size; }; +static inline bool is_mem_area_contained_in_range(const struct mem_area* area, uint64_t start, uint64_t end) { + return start <= area->start && area->start + area->size <= end; +} + #endif From 909699e5fc1fbb7b722cf4729fe1aca23e32c847 Mon Sep 17 00:00:00 2001 From: Alon Livne <2005alonlivne@gmail.com> Date: Tue, 30 Sep 2025 21:45:43 +0300 Subject: [PATCH 05/10] feature: get_memory_map mark multiple areas as runtime --- uefi/services/headers.h | 4 +- .../hooks/get_memory_map/get_memory_map.c | 37 ++++++++++--------- uefi/services/loaders/get_memory_map.c | 9 ++++- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/uefi/services/headers.h b/uefi/services/headers.h index b9cde50..ede3e5d 100644 --- a/uefi/services/headers.h +++ b/uefi/services/headers.h @@ -3,6 +3,8 @@ #include +#include "mem_area.h" + struct set_variable_hook_header { EFI_SET_VARIABLE original_set_variable; uint32_t* waking_vector_addr; @@ -11,8 +13,8 @@ struct set_variable_hook_header { struct get_memory_map_hook_header { EFI_GET_MEMORY_MAP original_get_memory_map; - uintptr_t waking_vector_phys_addr; EFI_INSTALL_CONFIGURATION_TABLE install_configuration_table; + struct mem_area runtime_areas[32]; }; struct virtual_address_map_event_header { diff --git a/uefi/services/hooks/get_memory_map/get_memory_map.c b/uefi/services/hooks/get_memory_map/get_memory_map.c index a8baa48..492414e 100644 --- a/uefi/services/hooks/get_memory_map/get_memory_map.c +++ b/uefi/services/hooks/get_memory_map/get_memory_map.c @@ -3,7 +3,9 @@ #include "../headers.h" #include "error.h" +#include "mem_area.h" #include "trace.h" +#include "utils.h" #define EFI_MEMORY_ATTRIBUTES_TABLE_GUID {0xdcfa911d, 0x26eb, 0x469f, 0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20}; @@ -11,35 +13,37 @@ __attribute__((section(".header"))) struct get_memory_map_hook_header g_hook_hea EFI_STATUS EFIAPI _start(IN OUT UINTN* MemoryMapSize, IN OUT EFI_MEMORY_DESCRIPTOR* MemoryMap, OUT UINTN* MapKey, OUT UINTN* DescriptorSize, OUT UINT32* DescriptorVersion) { - err_t err = SUCCESS; - // Call the original GetMemoryMap. Its returned value is returned from the hook regardless. EFI_STATUS res = g_hook_header.original_get_memory_map(MemoryMapSize, MemoryMap, MapKey, DescriptorSize, DescriptorVersion); if (res == EFI_SUCCESS) { - // Locate a descriptor that contains the waking vector, and mark it as `EFI_MEMORY_RUNTIME`. - // This requires the kernel to map this descriptor when calling `VirtualAddressMap`, which lets us access the waking - // vector in our `SetVariable` hook. - bool is_desc_found = false; + // Locate all descriptors that contain any of the runtime areas, and mark them as `EFI_MEMORY_RUNTIME`. + // This enforces the kernel to map these descriptors when calling `VirtualAddressMap`, which lets us access them + // from our `SetVariable` hook. for (size_t i = 0; i < *MemoryMapSize / *DescriptorSize; i++) { EFI_MEMORY_DESCRIPTOR* desc = NextMemoryDescriptor(MemoryMap, i * *DescriptorSize); uintptr_t desc_end = desc->PhysicalStart + (desc->NumberOfPages * EFI_PAGE_SIZE); - // Check if the descriptor contains the waking vector inside. - if (desc->PhysicalStart <= g_hook_header.waking_vector_phys_addr && - g_hook_header.waking_vector_phys_addr < desc_end) { - TRACE("Found a descriptor that contains the waking vector (%lx - %lx, type: %x)\n", desc->PhysicalStart, - desc_end, desc->Type); + // Check if the descriptor contains any of the runtime areas inside it, and if so mark it as runtime. + for (size_t i = 0; i < ARRAY_SIZE(g_hook_header.runtime_areas); i++) { + struct mem_area* runtime_area = &g_hook_header.runtime_areas[i]; + + if (runtime_area->size == 0) { + // The last entry in the `runtime_areas` array has size 0. + break; + } - CHECK_TRACE(!is_desc_found, "A descriptor containing the waking vector was found twice\n"); - is_desc_found = true; + if (is_mem_area_contained_in_range(runtime_area, desc->PhysicalStart, desc_end)) { + TRACE("Found a descriptor (%lx - %lx, type: %x) that contains runtime area (%lx - %lx)\n", + desc->PhysicalStart, desc_end, desc->Type, runtime_area->start, + runtime_area->start + runtime_area->size); - desc->Attribute |= EFI_MEMORY_RUNTIME; + desc->Attribute |= EFI_MEMORY_RUNTIME; + break; + } } } - - CHECK_TRACE(is_desc_found, "A descriptor containing the waking_vector was not found\n"); } // The Memory Attributes Table (see section 4.6.4 of the UEFI specs) allows fine-tuning page permissions for runtime @@ -61,6 +65,5 @@ EFI_STATUS EFIAPI _start(IN OUT UINTN* MemoryMapSize, IN OUT EFI_MEMORY_DESCRIPT EFI_GUID memory_attributes_table_guid = EFI_MEMORY_ATTRIBUTES_TABLE_GUID; g_hook_header.install_configuration_table(&memory_attributes_table_guid, NULL); -cleanup: return res; } diff --git a/uefi/services/loaders/get_memory_map.c b/uefi/services/loaders/get_memory_map.c index 901dd18..950c26a 100644 --- a/uefi/services/loaders/get_memory_map.c +++ b/uefi/services/loaders/get_memory_map.c @@ -5,6 +5,7 @@ #include #include "acpi/tables.h" +#include "core/header.h" #include "services/headers.h" #include "services/hooks_loader.h" @@ -14,6 +15,8 @@ struct loaded_hook g_loaded_get_memory_map; static EFI_GET_MEMORY_MAP g_original_get_memory_map = NULL; +extern struct core_header* g_core_header; + err_t hook_get_memory_map(void) { err_t err = SUCCESS; @@ -21,8 +24,12 @@ err_t hook_get_memory_map(void) { *(struct get_memory_map_hook_header*)g_loaded_get_memory_map.header = (struct get_memory_map_hook_header){ .original_get_memory_map = gBS->GetMemoryMap, - .waking_vector_phys_addr = (uintptr_t)&g_facs->firmware_waking_vector, .install_configuration_table = gBS->InstallConfigurationTable, + .runtime_areas = + { + {.start = (uintptr_t)&g_facs->firmware_waking_vector, .size = sizeof(g_facs->firmware_waking_vector)}, + {.start = (uintptr_t)g_core_header, .size = sizeof(*g_core_header)}, + }, }; g_original_get_memory_map = gBS->GetMemoryMap; From cc216dc33ab7fba4dd70cb8f55b070786dc0a6a2 Mon Sep 17 00:00:00 2001 From: Alon Livne <2005alonlivne@gmail.com> Date: Tue, 30 Sep 2025 21:48:53 +0300 Subject: [PATCH 06/10] feature: use CORE_WAKEUP_PHYS_ADDR instead of CORE_RM_PHYS_ADDR when necessary --- uefi/services/hooks/set_variable/set_variable.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uefi/services/hooks/set_variable/set_variable.c b/uefi/services/hooks/set_variable/set_variable.c index 9c893de..1b429ec 100644 --- a/uefi/services/hooks/set_variable/set_variable.c +++ b/uefi/services/hooks/set_variable/set_variable.c @@ -15,11 +15,11 @@ EFI_STATUS EFIAPI _start(IN CHAR16* VariableName, IN EFI_GUID* VendorGuid, IN UI // by updating a UEFI variable every few steps in its sleep process. // Some of the writes of this variable occur after updating the waking vector, so we can override it right after. if (wstrcmp(VariableName, WINDOWS_SLEEP_CHECKPOINT_VARIABLE_NAME) == 0 && - *g_hook_header.waking_vector_addr != CORE_RM_PHYS_ADDR) { + *g_hook_header.waking_vector_addr != CORE_WAKEUP_PHYS_ADDR) { TRACE("Updating the waking vector from the set_variable hook\n"); *g_hook_header.original_waking_vector_addr = *g_hook_header.waking_vector_addr; - *g_hook_header.waking_vector_addr = CORE_RM_PHYS_ADDR; + *g_hook_header.waking_vector_addr = CORE_WAKEUP_PHYS_ADDR; } // Call the original SetVariable function. From d311f11170456d5b6d1ff12684265afeda4b927c Mon Sep 17 00:00:00 2001 From: Alon Livne <2005alonlivne@gmail.com> Date: Tue, 30 Sep 2025 23:47:47 +0300 Subject: [PATCH 07/10] feature: modify is_core_header_XXX to not accept the entire struct --- shared/include/core/header.h | 33 +++++++++++++++++---------------- uefi/core_header_utils.c | 1 + uefi/core_loader.c | 4 +++- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/shared/include/core/header.h b/shared/include/core/header.h index 16e98e8..72d0c26 100644 --- a/shared/include/core/header.h +++ b/shared/include/core/header.h @@ -6,12 +6,13 @@ #include "core/consts.h" #include "mem_area.h" -#include "utils.h" enum core_action { CORE_ACTION_INVALID, CORE_ACTION_STORE, CORE_ACTION_SWITCH, + + CORE_ACTION_MAX, }; #define MAX_RAM_AREAS 64 @@ -55,29 +56,29 @@ struct core_header { struct mem_area ram_areas[MAX_RAM_AREAS]; }; -static inline bool is_core_header_magic_valid(const struct core_header* core_header) { - return core_header->magic == CORE_HEADER_MAGIC; +static inline bool is_core_header_magic_valid(uint32_t magic) { + return magic == CORE_HEADER_MAGIC; } -static inline bool is_core_header_action_valid(const struct core_header* core_header) { - return core_header->action != CORE_ACTION_INVALID; +static inline bool is_core_header_action_valid(enum core_action action) { + return action != CORE_ACTION_INVALID && action < CORE_ACTION_MAX; } -static inline bool is_core_header_facs_valid(const struct core_header* core_header) { - return core_header->facs != 0; +static inline bool is_core_header_facs_valid(uint64_t facs) { + return facs != 0; } -static inline bool is_core_header_rsdp_valid(const struct core_header* core_header) { - return core_header->rsdp != 0; +static inline bool is_core_header_rsdp_valid(uint64_t rsdp) { + return rsdp != 0; } -static inline bool is_core_header_ram_areas_valid(const struct core_header* core_header) { - if (core_header->ram_areas_size > ARRAY_SIZE(core_header->ram_areas)) { +static inline bool is_core_header_ram_areas_valid(const struct mem_area* ram_areas, uint32_t ram_areas_size) { + if (ram_areas_size > MAX_RAM_AREAS) { return false; } - for (uint32_t i = 0; i < core_header->ram_areas_size; i++) { - if (core_header->ram_areas[i].size == 0) { + for (uint32_t i = 0; i < ram_areas_size; i++) { + if (ram_areas[i].size == 0) { return false; } } @@ -86,9 +87,9 @@ static inline bool is_core_header_ram_areas_valid(const struct core_header* core } static inline bool is_core_header_valid(const struct core_header* core_header) { - return is_core_header_magic_valid(core_header) && is_core_header_action_valid(core_header) && - is_core_header_facs_valid(core_header) && is_core_header_rsdp_valid(core_header) && - is_core_header_ram_areas_valid(core_header); + return is_core_header_magic_valid(core_header->magic) && is_core_header_action_valid(core_header->action) && + is_core_header_facs_valid(core_header->facs) && is_core_header_rsdp_valid(core_header->rsdp) && + is_core_header_ram_areas_valid(core_header->ram_areas, core_header->ram_areas_size); } #endif diff --git a/uefi/core_header_utils.c b/uefi/core_header_utils.c index f460811..b1a034a 100644 --- a/uefi/core_header_utils.c +++ b/uefi/core_header_utils.c @@ -7,6 +7,7 @@ #include "acpi/tables.h" #include "core/header.h" #include "pci.h" +#include "utils.h" // TODO: This is currently hard coded to a virtio blk device. Make this // configurable. diff --git a/uefi/core_loader.c b/uefi/core_loader.c index b4fdc98..07afeff 100644 --- a/uefi/core_loader.c +++ b/uefi/core_loader.c @@ -9,6 +9,8 @@ #include #include +#include "utils.h" + extern char _binary_build_core_raw_bin_start[]; extern char _binary_build_core_raw_bin_end[]; @@ -69,7 +71,7 @@ err_t load_core(struct core_header** core_header_out) { CHECK_RETHROW(load_memory(CORE_PM_PHYS_ADDR, CORE_PM_START, CORE_PM_SIZE)); struct core_header* core_header = (struct core_header*)CORE_PHYS_ADDR; - CHECK(is_core_header_magic_valid(core_header)); + CHECK(is_core_header_magic_valid(core_header->magic)); *core_header_out = core_header; From 8ae2084dc6bdd289c1d686b356f47b739df1aecf Mon Sep 17 00:00:00 2001 From: Alon Livne <2005alonlivne@gmail.com> Date: Tue, 30 Sep 2025 23:48:03 +0300 Subject: [PATCH 08/10] feature: qemu_print %ls wide strings --- shared/src/qemu_print/qemu_print.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/shared/src/qemu_print/qemu_print.c b/shared/src/qemu_print/qemu_print.c index 6756080..1c23eb1 100644 --- a/shared/src/qemu_print/qemu_print.c +++ b/shared/src/qemu_print/qemu_print.c @@ -1,6 +1,7 @@ #include #include #include +#include static void qemu_print_char(char c) { // Use QEMU's debugcon device @@ -59,6 +60,14 @@ unsigned long qemu_print(const char* fmt, ...) { qemu_print_unsigned_num(arg, 16); break; } + case 's': { + wchar_t* arg = va_arg(args, wchar_t*); + for (const wchar_t* arg_c = arg; *arg_c != '\0'; arg_c++) { + // NOTE: This treats wide strings as regular strings + qemu_print_char((char)*arg_c); + } + break; + } } break; } From bec9a731e8e35eabbdb8ea1a12d7be7a72a810b6 Mon Sep 17 00:00:00 2001 From: Alon Livne <2005alonlivne@gmail.com> Date: Tue, 30 Sep 2025 23:58:29 +0300 Subject: [PATCH 09/10] feature: pass commands via SetVariable hook Usermode applications can now pass commands to the SetVariable hook by passing the magic guid `EFI_COMMANDS_GUID`. When passing commands, their corresponding handler is called, which is located and executed in the `handle_command` function. --- uefi/services/headers.h | 2 ++ uefi/services/hooks/Makefile | 1 + uefi/services/hooks/set_variable/Makefile | 1 + uefi/services/hooks/set_variable/commands.c | 30 +++++++++++++++++++ uefi/services/hooks/set_variable/commands.h | 28 +++++++++++++++++ .../hooks/set_variable/set_variable.c | 23 ++++++++++++-- uefi/services/loaders/set_variable.c | 3 ++ .../loaders/virtual_address_map_event.c | 1 + 8 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 uefi/services/hooks/set_variable/commands.c create mode 100644 uefi/services/hooks/set_variable/commands.h diff --git a/uefi/services/headers.h b/uefi/services/headers.h index ede3e5d..9d733f5 100644 --- a/uefi/services/headers.h +++ b/uefi/services/headers.h @@ -3,12 +3,14 @@ #include +#include "core/header.h" #include "mem_area.h" struct set_variable_hook_header { EFI_SET_VARIABLE original_set_variable; uint32_t* waking_vector_addr; uint32_t* original_waking_vector_addr; + struct core_header* core_header; }; struct get_memory_map_hook_header { diff --git a/uefi/services/hooks/Makefile b/uefi/services/hooks/Makefile index d06f739..ffe2e37 100644 --- a/uefi/services/hooks/Makefile +++ b/uefi/services/hooks/Makefile @@ -6,6 +6,7 @@ CFLAGS := -I$(GNU_EFI)/inc \ -ffunction-sections \ -D GNU_EFI_USE_MS_ABI \ -I . \ + -I $(ROOT_DIR)/uefi \ -I $(ROOT_DIR)/shared/include/ LDFLAGS := -ffreestanding -nostdlib -nostartfiles diff --git a/uefi/services/hooks/set_variable/Makefile b/uefi/services/hooks/set_variable/Makefile index 0e91e70..12554c9 100644 --- a/uefi/services/hooks/set_variable/Makefile +++ b/uefi/services/hooks/set_variable/Makefile @@ -1,4 +1,5 @@ SET_VARIABLE_HOOK_OBJS += set_variable/set_variable.o +SET_VARIABLE_HOOK_OBJS += set_variable/commands.o SET_VARIABLE_HOOK_OBJS += $(addprefix shared/,$(SHARED_OBJS)) SET_VARIABLE_HOOK_OBJS := $(addprefix obj/,$(SET_VARIABLE_HOOK_OBJS)) diff --git a/uefi/services/hooks/set_variable/commands.c b/uefi/services/hooks/set_variable/commands.c new file mode 100644 index 0000000..f6d0d88 --- /dev/null +++ b/uefi/services/hooks/set_variable/commands.c @@ -0,0 +1,30 @@ +#include "commands.h" + +#include + +#include "error.h" +#include "handlers/set_core_action.h" +#include "mem.h" + +static const EFI_GUID g_efi_commands_guid = EFI_COMMANDS_GUID; + +bool is_commands_guid(const EFI_GUID* guid) { + return memcmp(guid, &g_efi_commands_guid, sizeof(EFI_GUID)) == 0; +} + +err_t handle_command(const wchar_t* cmd_name, void* data, size_t data_size) { + err_t err = SUCCESS; + + // This macro creates an if clause for every supported command. + // + // The idea approach here would be to create an array of pairs of commands and their handles, and search for the + // command through it. However, this would require a relocation (since we're passing a function pointer), which is not + // supported for our services hooks. Instead, this method uses rip-relative code which does not require relocations. +#define HANDLE_COMMAND(name, handler) \ + if (wstrcmp(cmd_name, name) == 0) CHECK_RETHROW(handler(data, data_size)) + + HANDLE_COMMAND(L"SET_CORE_ACTION", set_core_action_handler); + +cleanup: + return err; +} diff --git a/uefi/services/hooks/set_variable/commands.h b/uefi/services/hooks/set_variable/commands.h new file mode 100644 index 0000000..1305b0c --- /dev/null +++ b/uefi/services/hooks/set_variable/commands.h @@ -0,0 +1,28 @@ +#ifndef _SET_VARIABLE_HOOK_COMMANDS_H +#define _SET_VARIABLE_HOOK_COMMANDS_H + +#include +#include + +#include "error.h" + +// Arbitrary GUID that the running kernel needs to use in order to communicate with us and send commands. +#define EFI_COMMANDS_GUID {0xe21c66ed, 0xbc2e, 0x4d30, 0xac, 0x5e, 0x1b, 0x96, 0x80, 0x02, 0xc5, 0x41} + +/** + * Returns whether a given guid is equal to `EFI_COMMANDS_GUID`. + * + * This function should be called by the `SetVariable` hook to detect whether a command should be executed. + */ +bool is_commands_guid(const EFI_GUID* guid); + +/** + * Receives the command name, and executes its corresponding command handler. + * Note that `data` may change due by the handler. + * + * This function should be called by the `SetVariable` hook with the variable name as the command name, after validating + * the passed guid matches the special commands guid `EFI_COMMANDS_GUID`, using `is_commands_guid`. + */ +err_t handle_command(const wchar_t* cmd_name, void* data, size_t data_size); + +#endif diff --git a/uefi/services/hooks/set_variable/set_variable.c b/uefi/services/hooks/set_variable/set_variable.c index 1b429ec..abb7ef4 100644 --- a/uefi/services/hooks/set_variable/set_variable.c +++ b/uefi/services/hooks/set_variable/set_variable.c @@ -1,8 +1,9 @@ #include -#include "../headers.h" #include "core/consts.h" #include "mem.h" +#include "services/headers.h" +#include "set_variable/commands.h" #include "trace.h" #define WINDOWS_SLEEP_CHECKPOINT_VARIABLE_NAME (L"SystemSleepCheckpoint") @@ -11,12 +12,30 @@ __attribute__((section(".header"))) struct set_variable_hook_header g_hook_heade EFI_STATUS EFIAPI _start(IN CHAR16* VariableName, IN EFI_GUID* VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID* Data) { + if (is_commands_guid(VendorGuid)) { + if (DataSize == 0) { + // Some implementations of kernel `SetVariable` wrappers delete the variable prior to updating it, which is done + // by passing 0 size. + return EFI_SUCCESS; + } + + // The running kernel is trying to communicate with us and send us commands. + // Our command handler may change the value behind the `Data` pointer even though it's `IN`. + // We shouldn't call the original SetVariable, since this isn't a real call to this function. + err_t cmd_res = handle_command(VariableName, Data, DataSize); + if (IS_SUCCESS(cmd_res)) { + return EFI_SUCCESS; + } else { + return EFIERR(cmd_res); + } + } + // The Windows kernel implements a mechanism called "Sleep Checkpoints", which traces the progress of entering sleep // by updating a UEFI variable every few steps in its sleep process. // Some of the writes of this variable occur after updating the waking vector, so we can override it right after. if (wstrcmp(VariableName, WINDOWS_SLEEP_CHECKPOINT_VARIABLE_NAME) == 0 && *g_hook_header.waking_vector_addr != CORE_WAKEUP_PHYS_ADDR) { - TRACE("Updating the waking vector from the set_variable hook\n"); + TRACE("Sleep checkpoint caught. Updating the waking vector from the set_variable hook\n"); *g_hook_header.original_waking_vector_addr = *g_hook_header.waking_vector_addr; *g_hook_header.waking_vector_addr = CORE_WAKEUP_PHYS_ADDR; diff --git a/uefi/services/loaders/set_variable.c b/uefi/services/loaders/set_variable.c index 68f1de6..c0c5fee 100644 --- a/uefi/services/loaders/set_variable.c +++ b/uefi/services/loaders/set_variable.c @@ -14,6 +14,8 @@ struct loaded_hook g_loaded_set_variable; static EFI_SET_VARIABLE g_original_set_variable = NULL; +extern struct core_header* g_core_header; + err_t hook_set_variable(void) { err_t err = SUCCESS; @@ -23,6 +25,7 @@ err_t hook_set_variable(void) { .original_set_variable = gRT->SetVariable, .waking_vector_addr = &g_facs->firmware_waking_vector, .original_waking_vector_addr = (uint32_t*)&g_facs->rsvd1, + .core_header = g_core_header, }; g_original_set_variable = gRT->SetVariable; diff --git a/uefi/services/loaders/virtual_address_map_event.c b/uefi/services/loaders/virtual_address_map_event.c index 5c43ac5..28a195f 100644 --- a/uefi/services/loaders/virtual_address_map_event.c +++ b/uefi/services/loaders/virtual_address_map_event.c @@ -39,6 +39,7 @@ err_t create_virtual_address_map_event(void) { (void**)&((struct set_variable_hook_header*)g_loaded_set_variable.header)->original_set_variable, (void**)&((struct set_variable_hook_header*)g_loaded_set_variable.header)->waking_vector_addr, (void**)&((struct set_variable_hook_header*)g_loaded_set_variable.header)->original_waking_vector_addr, + (void**)&((struct set_variable_hook_header*)g_loaded_set_variable.header)->core_header, }, }; From 505fb68a5eec4b21a8857a59c0b4625f5abd02cf Mon Sep 17 00:00:00 2001 From: Alon Livne <2005alonlivne@gmail.com> Date: Wed, 1 Oct 2025 00:00:06 +0300 Subject: [PATCH 10/10] feature: SET_CORE_ACTION command --- uefi/services/hooks/set_variable/Makefile | 1 + .../set_variable/handlers/set_core_action.c | 20 +++++++++++++++++++ .../set_variable/handlers/set_core_action.h | 10 ++++++++++ 3 files changed, 31 insertions(+) create mode 100644 uefi/services/hooks/set_variable/handlers/set_core_action.c create mode 100644 uefi/services/hooks/set_variable/handlers/set_core_action.h diff --git a/uefi/services/hooks/set_variable/Makefile b/uefi/services/hooks/set_variable/Makefile index 12554c9..b947d55 100644 --- a/uefi/services/hooks/set_variable/Makefile +++ b/uefi/services/hooks/set_variable/Makefile @@ -1,5 +1,6 @@ SET_VARIABLE_HOOK_OBJS += set_variable/set_variable.o SET_VARIABLE_HOOK_OBJS += set_variable/commands.o +SET_VARIABLE_HOOK_OBJS += set_variable/handlers/set_core_action.o SET_VARIABLE_HOOK_OBJS += $(addprefix shared/,$(SHARED_OBJS)) SET_VARIABLE_HOOK_OBJS := $(addprefix obj/,$(SET_VARIABLE_HOOK_OBJS)) diff --git a/uefi/services/hooks/set_variable/handlers/set_core_action.c b/uefi/services/hooks/set_variable/handlers/set_core_action.c new file mode 100644 index 0000000..8c97de6 --- /dev/null +++ b/uefi/services/hooks/set_variable/handlers/set_core_action.c @@ -0,0 +1,20 @@ +#include "set_core_action.h" + +#include "core/header.h" +#include "services/headers.h" + +extern struct set_variable_hook_header g_hook_header; + +err_t set_core_action_handler(void* data, size_t data_size) { + err_t err = SUCCESS; + + CHECK_TRACE(data_size == sizeof(enum core_action), "Invalid data size! Expecting data of type `core_action`\n"); + + enum core_action action = *(enum core_action*)data; + CHECK_TRACE(is_core_header_action_valid(action), "Invalid action value\n"); + + g_hook_header.core_header->action = action; + +cleanup: + return err; +} diff --git a/uefi/services/hooks/set_variable/handlers/set_core_action.h b/uefi/services/hooks/set_variable/handlers/set_core_action.h new file mode 100644 index 0000000..065fdd4 --- /dev/null +++ b/uefi/services/hooks/set_variable/handlers/set_core_action.h @@ -0,0 +1,10 @@ +#ifndef _SET_ACTION_HANDLER_H +#define _SET_ACTION_HANDLER_H + +#include + +#include "error.h" + +err_t set_core_action_handler(void* data, size_t data_size); + +#endif