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

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:原创不易,转载请注明出处。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
BIOS(Basic Input/Output System)和UEFI(Unified Extensible Firmware Interface)是计算机系统中用于启动和初始化硬件设备的软件接口。HII(Human Interface Infrastructure)是UEFI中的一个基本框架,它提供了一种标准化的方式来创建和管理图形用户界面(GUI)。下面是对BIOS/UEFI和HII的基本概述: 1. BIOS:BIOS是一种早期的固件接口,在过去的计算机系统中广泛使用。它负责在计算机启动时初始化硬件设备,并加载操作系统。BIOS通常使用基于文本的用户界面(TUI),通过键盘输入来进行配置和设置。 2. UEFIUEFI是BIOS的后继者,它提供了更先进的系统启动和硬件初始化功能。UEFI支持更大的硬盘容量、更快的启动速度和更丰富的扩展性。与BIOS相比,UEFI使用图形用户界面(GUI)来提供更直观和易用的界面。 3. HII:HII是UEFI规范中定义的一个基本框架,它提供了一套标准化的API和工具,用于创建、管理和显示UEFI图形用户界面。HII使得开发人员可以轻松地设计和定制UEFI界面,以满足用户的需求。HII还支持多语言和可扩展的用户界面,提供了更好的用户体验。 总而言之,BIOS/UEFI是计算机系统中用于启动和初始化硬件设备的软件接口,而HII是UEFI中的一个基本框架,用于创建和管理图形用户界面。它们共同为计算机提供了更快、更稳定和更易用的启动和配置功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值