Step to UEFI (143)Windows 下BootOrder研究

之前介绍了 Shell 下 BootOrder 的一些事情,这次介绍一下 Windows中如何取得和更改这个设定。Windows中相关API是SetFirmwareEnvironmentVariable 和GetFirmwareEnvironmentVariable (还有~Ex版)【参考1】【参考2】。需要注意,只有在 UEFI Based 系统上才可以使用上述API。
上面两个 API 的原型:

DWORD WINAPI GetFirmwareEnvironmentVariable( //取得Variable
_In_ LPCTSTR lpName, //变量名称
_In_ LPCTSTR lpGuid, //变量 GUID
_Out_ PVOID pBuffer, //读取结果放在pBuffer指向的内存中
_In_ DWORD nSize //给出pBuffer内存大小
);

返回值如果为0 ,表示失败,可以用GetLastError获取原因;如果返回值不为0,则是成功,该值应该是成功写入的字节数。

BOOL WINAPI SetFirmwareEnvironmentVariable( //设置 Variable
_In_ LPCTSTR lpName, //变量名称
_In_ LPCTSTR lpGuid, //变量 GUID
_In_ PVOID pBuffer, //要设置的内容放在pBuffer指向的内存中
_In_ DWORD nSize //给出pBuffer 指向内存的大小
);

返回值如果为0 ,表示失败,可以用GetLastError获取原因;如果返回值不为0,则是成功,该值应该是成功写入的字节数。

从上面的原型可以看出来,并没有给出Variable 大小的方法。实验如果给一个很小的Buffer会收到 7A 错误。因此在调用之前最好开一个足够大的空间以便使用(相比Windows可以分配的内存,Variable 小的可怜)。还有特别需要注意的地方是:使用这个 API 除了使用 管理员权限运行,还需要提升权限才可以。具体的代码来自【参考3】。
下面的代码首先用GetFirmwareEnvironmentVariable取得BootOrder的Variable,然后修改顺序,再使用SetFirmwareEnvironmentVariable把修改后的Variable写入,最后再读取显示一次:

// getfwenv.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"

#define VariableGuidStr      "{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}"
#define BootOrderStr         "BootOrder"

DWORD    dwRet = 0;

BOOL adjusttoken()
{
	HANDLE htoken;


	if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &htoken))
	{
		size_t           s = sizeof(TOKEN_PRIVILEGES) + 2 * sizeof(LUID_AND_ATTRIBUTES);
		TOKEN_PRIVILEGES *p = (PTOKEN_PRIVILEGES)malloc(s);


		if (!LookupPrivilegeValue(NULL, SE_SYSTEM_ENVIRONMENT_NAME, &(p->Privileges[0].Luid)) ||
			!LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &(p->Privileges[1].Luid)) ||
			!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &(p->Privileges[2].Luid)))
		{
			printf("failed to LookupPrivilegeValue error code : %d \r\n", GetLastError());
			free(p);
			return FALSE;
		}
		p->PrivilegeCount = 3;


		for (int i = 0; i < 3; ++i)
		{
			p->Privileges[i].Attributes = SE_PRIVILEGE_ENABLED;
		}

		 
		if (!AdjustTokenPrivileges(htoken, FALSE, p, (DWORD) s, NULL, NULL) || GetLastError() != ERROR_SUCCESS)
		{
			printf("AdjustTokenPrivileges failed! error code : %d \r\n", GetLastError());
			free(p);
			return FALSE;
		}
		//do something here...
		free(p);
	}
	else
	{
		printf("Open process token failed! error code : %d \r\n", GetLastError());
		return FALSE;
	}


	return TRUE;
}

int main()
{
	byte Buffer[2000];
	byte *pBuffer;
	DWORD  iBufferSize=sizeof(Buffer);
	DWORD dwRet;

	adjusttoken();

	pBuffer = Buffer;

	//Get BootOrder
	dwRet = GetFirmwareEnvironmentVariable(
		_T(BootOrderStr),
		_T(VariableGuidStr),
		pBuffer,
		iBufferSize);
	printf("GetFirmwareEnvironmentVariable return value:%x\n", dwRet);
	for (DWORD i = 0; i < dwRet;i++)
		printf("%X ",pBuffer[i]);
	printf("\n");

	//Set boot from shell
	pBuffer[0] = 0;	pBuffer[2] = 4;

	//Set new Bootorder
	dwRet = SetFirmwareEnvironmentVariable(
		_T(BootOrderStr),
		_T(VariableGuidStr),
		pBuffer,
		dwRet);
	printf("SetFirmwareEnvironmentVariable return value:%x\n", dwRet);

	//Check Bootorder again
	dwRet = GetFirmwareEnvironmentVariable(
		_T(BootOrderStr),
		_T(VariableGuidStr),
		pBuffer,
		iBufferSize);
	printf("GetFirmwareEnvironmentVariable again, return value:%x\n", dwRet);

	for (DWORD i = 0; i < dwRet; i++)
		printf("%X ", pBuffer[i]);

	getchar();

    return 0;
}

 

最终运行结果:

winorder

在我的KBL-R HDK上重启之后就会直接进入 Shell (原因可以在前一篇看到)

编译好的X64 Application和Sourcecode
getfwenv

从实验上来看,所有的变量都是可以读取到,但是不知道为什么很多变量在 Windows下无法写(写保护错误)。如果能够做到写入,那么就可以自动的改变Setup的设定。另外,我无法判断这个写保护错误是Windows本身造成的还是BIOS造成的。如果未来有机会会继续研究下去,有兴趣的朋友也可以在下面给我留言说出你的想法。

参考:
1. https://msdn.microsoft.com/en-us/library/windows/desktop/ms724934(v=vs.85).aspx
2. https://msdn.microsoft.com/en-us/library/windows/desktop/ms724325(v=vs.85).aspx

Step to UEFI (143)Windows 下BootOrder研究》上有 6 条评论

  1. krishna

    Hi,Z.t,我也用过这个API,用来判断是不是UEFI启动的系统,后来发现有另一个API有相似的功能:GetFirmwareType。
    据我所知有些变量是基于SMM模式下access的,叫做Authenticated Variable,所以用户模式不能修改,可能需要driver接口或中断接口。在uefi-shell下的交流接口和方式,类似之前我做过的SMI实验,之前发给你的。

    回复
    1. ziv2013 文章作者

      之前研究过一下,比如保存 Setup 选项设置的 Variable, 在 Shell 下面是可以读取和修改的,但是到了 Windows下面只能读取无法修改,你知道什么原因吗?

      回复
      1. krishna

        我这里类似的工具,只是读取,文档描述都要以管理员方式运行,可能与权限有关,
        可否给个具体的变量名,我也试试。

        回复
        1. ziv2013 文章作者

          我当时是随便选取了一个 setup 使用的变量,好像是 pch_setup 如果能做到修改,那么就可以实现在 Windows 对 Setup Item值的修改了。

          回复
          1. krishna

            最近还发现,有些变量在某些情况,可能是exit-boot后锁定了,会用到这个protocol,例如:
            Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
            if (!EFI_ERROR (Status)) {
            Status = VariableLock->RequestToLock (VariableLock, OptionName, &gEfiGlobalVariableGuid);
            这就纠结了。。。

          2. ziv2013 文章作者

            呃,如果确实是这样的 lock 引起的,那么也不应该 Shell 底下可以修改,但是 Windows下面无法修改的。

发表评论

电子邮件地址不会被公开。 必填项已用*标注