Windows下访问/修改UEFI变量(C++/C#)
UEFI,全称 Unified Extensible Firmware Interface,译为“统一可扩展固件接口”。
UEFI 相比传统的 BIOS(Legacy) 启动方式,除了启动方便,还有一个最大的特点就是 UEFI 支持图形化操作,使用户操作更简单便捷,尤其是对于外行的朋友,可视化界面总比一堆看不懂的“代码”操作简便。
简单来讲 UEFI 启动是新一代的 BIOS,功能更加强大,被看作是 Legacy BIOS 的继任者。
Windows 下访问 UEFI 变量,MSDN 提供了几个接口:
GetFirmwareEnvironmentVariableExA function
GetFirmwareEnvironmentVariableExW function
SetFirmwareEnvironmentVariableExA function
SetFirmwareEnvironmentVariableExW function
1,在调用这些接口之前,首先需要判断当前计算机是否为 UEFI 的固件类型:GetFirmwareType function

bool DetectFirmwareType() { FIRMWARE_TYPE firmwareType; if (GetFirmwareType(&firmwareType)) { if (firmwareType != FIRMWARE_TYPE::FirmwareTypeUefi) { return false; } } return true; }

[DllImport("kernel32.dll")] private static extern bool GetFirmwareType(ref FirmwareType FirmwareType); internal enum FirmwareType { FirmwareTypeUnknown = 0, FirmwareTypeBios = 1, FirmwareTypeUefi = 2, FirmwareTypeMax = 3 }; private static FirmwareType GetFirmwareType() { FirmwareType type = FirmwareType.FirmwareTypeUnknown; if (GetFirmwareType(ref type)) { return type; } return FirmwareType.FirmwareTypeUnknown; }
2,根据 MSDN 的描述:
To read a UEFI firmware environment variable, the user account that the app is running under must have the SE_SYSTEM_ENVIRONMENT_NAME privilege.
因此需要提升进程权限:AdjustTokenPrivileges function

// newPrivileges -> 提升权限值 // enable -> 提升/降低(恢复) bool promoteProcessPrivileges(const TCHAR* newPrivileges, const bool enable) { HANDLE tokenHandle; //获得当前进程的access token句柄 if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &tokenHandle) == FALSE) return false; TOKEN_PRIVILEGES structTkp; //查找newPrivileges参数对应的Luid,并将结果写入structTkp.Privileges[0]的Luid域中 if (::LookupPrivilegeValue(NULL, newPrivileges, &structTkp.Privileges[0].Luid) == FALSE) { CloseHandle(tokenHandle); return false; } //设置structTkp结构 structTkp.PrivilegeCount = 1; structTkp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; //通知操作系统更改权限 if (::AdjustTokenPrivileges(tokenHandle, FALSE, &structTkp, sizeof(structTkp), NULL, NULL) == FALSE) { CloseHandle(tokenHandle); return false; } CloseHandle(tokenHandle); return true; }

internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; internal const int TOKEN_QUERY = 0x00000008; internal const int SE_PRIVILEGE_ENABLED = 0x00000002; private const string SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege"; [DllImport("kernel32.dll", ExactSpelling = true)] internal static extern IntPtr GetCurrentProcess(); [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok); [DllImport("advapi32.dll", SetLastError = true)] internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokenPrivelege newst, int len, IntPtr prev, IntPtr relen); [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct TokenPrivelege { public int Count; public long Luid; public int Attr; } /// <summary> /// 提升进程权限 /// </summary> /// <param name="privilege">权限</param> /// <param name="enable">提升/恢复</param> /// <returns></returns> private static bool PromoteProcessPrivileges(string privilege, bool enable) { IntPtr hToken = IntPtr.Zero; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref hToken)) { Console.WriteLine("OpenProcessToken failed!"); return false; } TokenPrivelege tp; tp.Count = 1; tp.Luid = 0; tp.Attr = enable ? SE_PRIVILEGE_ENABLED : 0; if (!LookupPrivilegeValue(null, privilege, ref tp.Luid)) { Console.WriteLine("LookupPrivilegeValue failed!"); return false; } if (!AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero)) { Console.WriteLine("AdjustTokenPrivileges failed!"); return false; } return true; }
Refer: Enabling and Disabling Privileges in C++
3.1,获取 UEFI 变量值(需要使用管理员权限运行):

typedef struct { UINT8 Variable; UINT8 Reserved[3]; } VARIABLE; enum Status { Disable, Enable, UnknownStatus }; LPCTSTR lpName = _T(""); LPCTSTR lpGuid = _T(""); DWORD dwAttribute = 0; VARIABLE lenStru = { 0 }; Status GetUEFIVariable() { Status status = Status::UnknownStatus; DWORD isSuss = GetFirmwareEnvironmentVariableEx(lpName, lpGuid, &lenStru, sizeof(lenStru), &dwAttribute); uint32_t u32LastError = GetLastError(); if (u32LastError != ERROR_SUCCESS) cout << "GetError: " << u32LastError << endl; if (isSuss != 0) { UINT8 Value = lenStru.Variable; status = (Value == 1) ? Status::Enable : Status::Disable; } return status; }

private const string Name = ""; private const string EFI_VARIABLE = ""; [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] private static extern uint GetFirmwareEnvironmentVariableExW( [MarshalAs(UnmanagedType.LPWStr)] string lpName, [MarshalAs(UnmanagedType.LPWStr)] string lpGuid, IntPtr pBuffer, int nSize, IntPtr intPtr); [StructLayout(LayoutKind.Sequential, Pack = 1)] private struct VARIABLE_INTERFACE { [MarshalAs(UnmanagedType.U1)] public byte VariableEn; //public byte[] Reserved; [MarshalAs(UnmanagedType.U1)] public byte Reserved1; [MarshalAs(UnmanagedType.U1)] public byte Reserved2; [MarshalAs(UnmanagedType.U1)] public byte Reserved3; }; private static int GetDataFromUEFI() { int flag = -1; try { VARIABLE_INTERFACE shape = new VARIABLE_INTERFACE { VariableEn = 0, Reserved1 = 0, Reserved2 = 0, Reserved3 = 0 }; IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf<VARIABLE_INTERFACE>()); Marshal.StructureToPtr(shape, pointer, false); var length = GetFirmwareEnvironmentVariableExW(Name, EFI_VARIABLE.ToUpper(), pointer, Marshal.SizeOf<VARIABLE_INTERFACE>(), IntPtr.Zero); if (length != 0) { VARIABLE_INTERFACE data = (VARIABLE_INTERFACE)Marshal.PtrToStructure(pointer, typeof(VARIABLE_INTERFACE)); flag = (data.VariableEn == 1) ? 1 : 0; } else { int ec = Marshal.GetLastWin32Error(); flag = ec * -1; Console.WriteLine("Error code: " + ec.ToString()); } Marshal.FreeHGlobal(pointer); } catch (Exception ex) { Console.WriteLine($"GetDataFromUEFI error {ex.Message}"); } return flag; }
GetFirmwareEnvironmentVariableEx(lpName, lpGuid, &lenStru, sizeof(lenStru), &dwAttribute) 的参数 “lpName”,“lpGuid”,“lenStru” 均需要 BIOS 开发者提供(或者MS对外公开的值)。
Get失败时可以参考 MSDN 提供的 System Error: System Error Codes
3.2,修改 UEFI 变量值(需要使用管理员权限运行):

bool SetFlipToBootVariable(bool status) { lenStru.Variable = status ? 0x01 : 0x00; bool isSucc = SetFirmwareEnvironmentVariableEx(lpName, lpGuid, &lenStru, sizeof(lenStru), 0x00000001 | 0x00000002 | 0x00000004); uint32_t u32LastError = GetLastError(); if (u32LastError != ERROR_SUCCESS) cout << "SetError: " << u32LastError << endl; return isSucc; }

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] private static extern bool SetFirmwareEnvironmentVariableExW( [MarshalAs(UnmanagedType.LPWStr)] string lpName, [MarshalAs(UnmanagedType.LPWStr)] string lpGuid, IntPtr pValue, int nSize, int attributes); [DllImport("kernel32.dll")] private static extern uint GetLastError(); private static VARIABLE_INTERFACE len_Struct = new VARIABLE_INTERFACE { VariableEn = 0x00, //Reserved = new byte[3], Reserved1 = 0, Reserved2 = 0, Reserved3 = 0 }; private static IntPtr pointer = IntPtr.Zero; private static bool SetDataFromUEFI(bool status) { bool isSucc = false; try { if (status == true) { len_Struct.VariableEn = 0x01; //len_Struct.Reserved = new byte[3]; len_Struct.Reserved1 = 0; len_Struct.Reserved2 = 0; len_Struct.Reserved3 = 0; } pointer = Marshal.AllocHGlobal(Marshal.SizeOf<VARIABLE_INTERFACE>()); Marshal.StructureToPtr(len_Struct, pointer, false); uint u32LastError = 0; isSucc = SetFirmwareEnvironmentVariableExW(Name, EFI_VARIABLE, pointer, Marshal.SizeOf<VARIABLE_INTERFACE>(), 0x00000001 | 0x00000002 | 0x00000004); u32LastError = GetLastError(); if (u32LastError != 0) Console.WriteLine("SetError: " + u32LastError); Marshal.FreeHGlobal(pointer); } catch (Exception ex) { isSucc = false; } return isSucc; }
4,调用:

int main() { // 是否为UEFI if (!DetectFirmwareType()) { cout << "Error! Not UEFI Environment." << endl; return -1; } //提权是否成功 if (!promoteProcessPrivileges(SE_SYSTEM_ENVIRONMENT_NAME, true)) { cout << "Can not promote current process SE_SYSTEM_ENVIRONMENT_NAME privilege." << endl; return -1; } Status status = GetUEFIVariable(); cout << "UEFI Variable status: " << status << endl; if (status != Status::UnknownStatus) { if (SetFlipToBootVariable(true)) { cout << "Set UEFI Variable Succeed." << endl; } } promoteProcessPrivileges(SE_SYSTEM_ENVIRONMENT_NAME, false); system("pause"); }

static void Main(string[] args) { if (GetFirmwareType() != FirmwareType.FirmwareTypeUefi) { Console.WriteLine("NOT UEFI."); return; } if (!PromoteProcessPrivileges(SE_SYSTEM_ENVIRONMENT_NAME, true)) { Console.WriteLine("Can not promote current process SE_SYSTEM_ENVIRONMENT_NAME privilege."); return; } int status = GetDataFromUEFI(); Console.WriteLine("UEFI status:" + status); if (status == 1 || status == 0) { if (SetDataFromUEFI(true)) { Console.WriteLine("Set UEFI Variable Succeed."); } } Console.ReadKey(); }
That's All, thx~
PS:原创不易,转载请注明出处。