如何在调用UEFI RT服务时阻止其他UEFI Runtime Services任务运行?
阻止其他UEFI Runtime服务调用的实现方案
以下从Linux侧和UEFI侧分别提供可落地的C语言实现方案,核心思路是通过钩子拦截+权限过滤,确保仅你的任务A能调用目标UEFI RT服务。
Linux侧实现
方案1:内核模块钩子(推荐,权限足够)
Linux内核维护着UEFI Runtime服务的函数表,通过内核模块可以直接替换目标服务的函数指针为自定义钩子,实现调用过滤。
示例代码(以拦截SetVariable服务为例):
#include <linux/module.h> #include <linux/efi.h> #include <linux/spinlock.h> // 保存原始服务函数指针 static EFI_STATUS (*orig_set_var)(const efi_char16_t *, const efi_guid_t *, u32, unsigned long, const void *); static DEFINE_SPINLOCK(efi_service_lock); // 任务A的唯一标识(自定义GUID) static const efi_guid_t my_task_a_guid = {0x12345678, 0x1234, 0x1234, {0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}}; // 自定义钩子函数 static EFI_STATUS hook_set_var(const efi_char16_t *var_name, const efi_guid_t *vendor_guid, u32 attrs, unsigned long data_size, const void *data) { unsigned long flags; spin_lock_irqsave(&efi_service_lock, flags); // 仅允许任务A的调用通过(通过GUID判断) if (efi_guidcmp(vendor_guid, &my_task_a_guid) == 0) { EFI_STATUS ret = orig_set_var(var_name, vendor_guid, attrs, data_size, data); spin_unlock_irqrestore(&efi_service_lock, flags); return ret; } else { // 拒绝其他调用,返回权限错误 spin_unlock_irqrestore(&efi_service_lock, flags); return EFI_ACCESS_DENIED; } } static int __init efi_hook_init(void) { // 获取原始服务指针 orig_set_var = efi.runtime->set_variable; // 替换为钩子函数 efi.runtime->set_variable = hook_set_var; pr_info("UEFI SetVariable hooked successfully\n"); return 0; } static void __exit efi_hook_exit(void) { // 恢复原始服务指针 efi.runtime->set_variable = orig_set_var; pr_info("UEFI SetVariable hook removed\n"); } module_init(efi_hook_init); module_exit(efi_hook_exit); MODULE_LICENSE("GPL");
方案2:用户空间包装(需内核配合)
如果无法编写内核模块,可在用户空间通过包装efi_call_runtime_service宏,加入互斥锁和调用过滤,但需确保内核未限制对EFI服务表的修改(部分内核版本会保护该区域)。
示例代码:
#include <pthread.h> #include <efi.h> #include <efilib.h> pthread_mutex_t efi_lock = PTHREAD_MUTEX_INITIALIZER; // 任务A的GUID static const EFI_GUID my_task_a_guid = {0x12345678, 0x1234, 0x1234, {0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}}; // 包装SetVariable调用 EFI_STATUS my_set_variable(CHAR16 *var_name, EFI_GUID *vendor_guid, UINT32 attrs, UINTN data_size, VOID *data) { pthread_mutex_lock(&efi_lock); if (!CompareGuid(vendor_guid, &my_task_a_guid)) { pthread_mutex_unlock(&efi_lock); return EFI_ACCESS_DENIED; } EFI_STATUS ret = efi_call_runtime_service(SetVariable, var_name, vendor_guid, attrs, data_size, data); pthread_mutex_unlock(&efi_lock); return ret; } // 任务A代码 void task_a() { CHAR16 var_name[] = L"MyTaskAVar"; UINT8 data = 0xAA; my_set_variable(var_name, (EFI_GUID*)&my_task_a_guid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 1, &data); }
UEFI侧实现
从固件层面修改RT服务,是最彻底的拦截方案,需编写UEFI Runtime Driver并加载到固件中。
示例代码(拦截SetVariable服务):
#include <Uefi.h> #include <Library/UefiRuntimeServicesTableLib.h> #include <Library/UefiLib.h> // 保存原始服务指针 static EFI_SET_VARIABLE OriginalSetVariable; // EFI Runtime可用的锁(必须用EFI提供的锁,不能用普通互斥锁) static EFI_LOCK ServiceLock = EFI_INITIALIZE_LOCK_VARIABLE; // 任务A的唯一GUID static CONST EFI_GUID MyTaskAGuid = {0x12345678, 0x1234, 0x1234, {0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}}; // 自定义包装服务函数 EFI_STATUS EFIAPI WrappedSetVariable( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data ) { EFI_STATUS Status; EfiAcquireLock(&ServiceLock); // 仅允许任务A的调用 if (!CompareGuid(VendorGuid, &MyTaskAGuid)) { EfiReleaseLock(&ServiceLock); return EFI_ACCESS_DENIED; } // 调用原始服务 Status = OriginalSetVariable(VariableName, VendorGuid, Attributes, DataSize, Data); EfiReleaseLock(&ServiceLock); return Status; } // Runtime Driver入口 EFI_STATUS EFIAPI MyRuntimeDriverEntry( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { // 保存原始服务指针 OriginalSetVariable = gRT->SetVariable; // 替换为包装函数 gRT->SetVariable = WrappedSetVariable; return EFI_SUCCESS; }
方案优缺点对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| Linux内核模块 | 无需修改固件,权限足够 | 内核版本兼容性需注意 |
| Linux用户空间 | 实现简单,无内核编译依赖 | 可能受内核内存保护限制 |
| UEFI侧修改 | 拦截最彻底,无Linux依赖 | 需要固件开发/修改权限,适配不同固件难度高 |
内容的提问来源于stack exchange,提问作者user8292736
相关产品推荐
相关产品推荐

