之前介绍了 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