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
C++:
bool DetectFirmwareType()
{
FIRMWARE_TYPE firmwareType;
if (GetFirmwareType(&firmwareType))
{
if (firmwareType != FIRMWARE_TYPE::FirmwareTypeUefi)
{
return false;
}
}
return true;
}
C#:
[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
C++:
// 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;
}
C#:
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 变量值(需要使用管理员权限运行):
C++:
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;
}
C#:
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 变量值(需要使用管理员权限运行):
C++:
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;
}
C#:
[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,调用:
C++:
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");
}
C#:
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:原创不易,转载请注明出处。