之前介绍了 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; }
最终运行结果:
在我的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
Hi,Z.t,我也用过这个API,用来判断是不是UEFI启动的系统,后来发现有另一个API有相似的功能:GetFirmwareType。
据我所知有些变量是基于SMM模式下access的,叫做Authenticated Variable,所以用户模式不能修改,可能需要driver接口或中断接口。在uefi-shell下的交流接口和方式,类似之前我做过的SMI实验,之前发给你的。
之前研究过一下,比如保存 Setup 选项设置的 Variable, 在 Shell 下面是可以读取和修改的,但是到了 Windows下面只能读取无法修改,你知道什么原因吗?
我这里类似的工具,只是读取,文档描述都要以管理员方式运行,可能与权限有关,
可否给个具体的变量名,我也试试。
我当时是随便选取了一个 setup 使用的变量,好像是 pch_setup 如果能做到修改,那么就可以实现在 Windows 对 Setup Item值的修改了。
最近还发现,有些变量在某些情况,可能是exit-boot后锁定了,会用到这个protocol,例如:
Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
if (!EFI_ERROR (Status)) {
Status = VariableLock->RequestToLock (VariableLock, OptionName, &gEfiGlobalVariableGuid);
这就纠结了。。。
呃,如果确实是这样的 lock 引起的,那么也不应该 Shell 底下可以修改,但是 Windows下面无法修改的。
基本上SHELL不會驅動 exit-boot
请问您测试了内存读写的变量了吗?
UEFI 变量吗?