@@ -9456,176 +9456,155 @@ struct VM {
94569456 * @implements VM::NVRAM
94579457 */
94589458 [[nodiscard]] static bool nvram_vars () {
9459- struct VARIABLE_NAME {
9460- ULONG NextEntryOffset;
9461- GUID VendorGuid;
9462- WCHAR Name[1 ];
9463- };
9459+ struct VARIABLE_NAME { ULONG NextEntryOffset; GUID VendorGuid; WCHAR Name[1 ]; };
94649460 using PVARIABLE_NAME = VARIABLE_NAME*;
9461+ using NtEnumerateSystemEnvironmentValuesEx_t = NTSTATUS (__stdcall*)(ULONG, PVOID, PULONG);
94659462
9466- using NtEnumerateSystemEnvironmentValuesEx_t = NTSTATUS (__stdcall*)(
9467- ULONG InformationClass,
9468- PVOID Buffer,
9469- PULONG BufferLength);
9470-
9471- // secure boot
9472- bool found_dbDefault = false ;
9473- bool found_dbxDefault = false ;
9474- bool found_KEKDefault = false ;
9475- bool found_PKDefault = false ;
9476-
9477- // extra vars
9463+ bool found_dbDefault = false , found_dbxDefault = false , found_KEKDefault = false , found_PKDefault = false ;
94789464 bool found_MORCL = false ;
94799465
9480- if (!util::is_admin ()) {
9481- return false ;
9482- }
9466+ if (!util::is_admin ()) return false ;
94839467
94849468 HANDLE hToken = nullptr ;
9485- if (!OpenProcessToken (GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
9486- return false ;
9487- }
9469+ if (!OpenProcessToken (GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return false ;
94889470
9489- auto token_closer = [&](HANDLE token) {
9490- if (token) {
9491- TOKEN_PRIVILEGES tp{};
9492- tp.PrivilegeCount = 1 ;
9493- LookupPrivilegeValue (nullptr , SE_SYSTEM_ENVIRONMENT_NAME, &tp.Privileges [0 ].Luid );
9494- tp.Privileges [0 ].Attributes = 0 ;
9495- AdjustTokenPrivileges (token, FALSE , &tp, sizeof (TOKEN_PRIVILEGES), nullptr , nullptr );
9496- CloseHandle (token);
9471+ LUID luid{};
9472+ bool priv_enabled = false ;
9473+ auto cleanup = [&]() {
9474+ if (priv_enabled && hToken) {
9475+ TOKEN_PRIVILEGES tpDisable{};
9476+ tpDisable.PrivilegeCount = 1 ;
9477+ tpDisable.Privileges [0 ].Luid = luid;
9478+ tpDisable.Privileges [0 ].Attributes = 0 ;
9479+ AdjustTokenPrivileges (hToken, FALSE , &tpDisable, sizeof (TOKEN_PRIVILEGES), nullptr , nullptr );
9480+ }
9481+ if (hToken) {
9482+ CloseHandle (hToken);
9483+ hToken = nullptr ;
94979484 }
94989485 };
9499- std::unique_ptr<void , decltype (token_closer)> token_guard (hToken, token_closer);
95009486
9501- LUID luid{};
9502- if (!LookupPrivilegeValue (nullptr , SE_SYSTEM_ENVIRONMENT_NAME, &luid)) {
9503- return false ;
9504- }
9505-
9506- TOKEN_PRIVILEGES tp{};
9507- tp.PrivilegeCount = 1 ;
9508- tp.Privileges [0 ].Luid = luid;
9509- tp.Privileges [0 ].Attributes = SE_PRIVILEGE_ENABLED;
9510-
9511- if (!AdjustTokenPrivileges (hToken, FALSE , &tp, sizeof (TOKEN_PRIVILEGES), nullptr , nullptr ) || GetLastError () != ERROR_SUCCESS) {
9512- return false ;
9487+ if (!LookupPrivilegeValue (nullptr , SE_SYSTEM_ENVIRONMENT_NAME, &luid)) { cleanup (); return false ; }
9488+
9489+ TOKEN_PRIVILEGES tpEnable{};
9490+ tpEnable.PrivilegeCount = 1 ;
9491+ tpEnable.Privileges [0 ].Luid = luid;
9492+ tpEnable.Privileges [0 ].Attributes = SE_PRIVILEGE_ENABLED;
9493+ AdjustTokenPrivileges (hToken, FALSE , &tpEnable, sizeof (TOKEN_PRIVILEGES), nullptr , nullptr );
9494+ if (GetLastError () != ERROR_SUCCESS) { cleanup (); return false ; }
9495+ priv_enabled = true ;
9496+
9497+ bool hasFunction = false ;
9498+ bool success = false ;
9499+ std::vector<BYTE> resBuffer;
9500+ ULONG bufferLength = 0 ;
9501+ HMODULE ntdll = util::get_ntdll ();
9502+ if (ntdll) {
9503+ const char * names[] = { " NtEnumerateSystemEnvironmentValuesEx" };
9504+ void * functions[1 ] = { nullptr };
9505+ util::get_function_address (ntdll, names, functions, 1 );
9506+ auto NtEnum = reinterpret_cast <NtEnumerateSystemEnvironmentValuesEx_t>(functions[0 ]);
9507+ if (NtEnum) {
9508+ hasFunction = true ;
9509+ NTSTATUS status = NtEnum (1 , nullptr , &bufferLength);
9510+ if (bufferLength != 0 ) {
9511+ try { resBuffer.resize (bufferLength); }
9512+ catch (...) { resBuffer.clear (); bufferLength = 0 ; }
9513+ if (!resBuffer.empty ()) {
9514+ status = NtEnum (1 , resBuffer.data (), &bufferLength);
9515+ if (status == 0 ) { success = true ; resBuffer.resize (bufferLength); }
9516+ else resBuffer.clear ();
9517+ }
9518+ }
9519+ }
95139520 }
95149521
9515- util::EnumerateFirmwareResult res = util::enumerate_firmware_variables ();
9516-
9517- if (!res.hasFunction ) {
9522+ if (!hasFunction) {
95189523 debug (" NVRAM: Handle to ntdll.dll could not be obtained, possibly tampered" );
9519- return true ; // returning true on purpose
9524+ cleanup ();
9525+ return true ;
95209526 }
9521-
9522- if (!res.success ) {
9527+ if (!success) {
95239528 debug (" NVRAM: System is not UEFI" );
9524- return false ; // NOT UEFI
9529+ cleanup ();
9530+ return false ;
95259531 }
95269532
9527- auto contains_redhat_ascii_ci = [](const std::vector<BYTE>& buf) -> bool {
9533+ auto contains_redhat_ascii_ci = [](const std::vector<BYTE>& buf)-> bool {
95289534 if (buf.empty ()) return false ;
95299535 std::string s (reinterpret_cast <const char *>(buf.data ()), buf.size ());
9530- for (auto & c : s) c = static_cast <char >(::tolower (static_cast <u8 >(c)));
9536+ for (auto & c : s) c = static_cast <char >(::tolower (static_cast <unsigned char >(c)));
95319537 return s.find (" red hat secure boot" ) != std::string::npos;
95329538 };
9533- auto contains_redhat_utf16le_ci = [](const std::vector<BYTE>& buf) -> bool {
9534- if (buf.size () < 2 ) return false ;
9535- if (buf.size () % 2 != 0 ) return false ;
9539+ auto contains_redhat_utf16le_ci = [](const std::vector<BYTE>& buf)->bool {
9540+ if (buf.size () < 2 || (buf.size () % 2 ) != 0 ) return false ;
95369541 const WCHAR* wptr = reinterpret_cast <const WCHAR*>(buf.data ());
95379542 size_t wlen = buf.size () / sizeof (WCHAR);
95389543 try {
95399544 std::wstring ws (wptr, wlen);
95409545 for (auto & wc : ws) wc = static_cast <wchar_t >(::towlower (wc));
9541- std::wstring needle = L" red hat secure boot" ;
9542- return ws.find (needle) != std::wstring::npos;
9543- }
9544- catch (...) {
9545- return false ;
9546+ return ws.find (L" red hat secure boot" ) != std::wstring::npos;
95469547 }
9548+ catch (...) { return false ; }
95479549 };
95489550
9549- PVARIABLE_NAME varName = reinterpret_cast <PVARIABLE_NAME>(res.buffer .data ());
9550- const size_t bufSize = res.buffer .size ();
9551-
9551+ PVARIABLE_NAME varName = reinterpret_cast <PVARIABLE_NAME>(resBuffer.data ());
9552+ const size_t bufSize = resBuffer.size ();
95529553 constexpr size_t MAX_NAME_BYTES = 4096 ;
95539554
95549555 while (true ) {
9555- const uintptr_t basePtr = reinterpret_cast <uintptr_t >(res. buffer .data ());
9556+ const uintptr_t basePtr = reinterpret_cast <uintptr_t >(resBuffer .data ());
95569557 const uintptr_t curPtr = reinterpret_cast <uintptr_t >(varName);
9557-
95589558 if (curPtr < basePtr) break ;
95599559 const size_t offset = static_cast <size_t >(curPtr - basePtr);
95609560 if (offset >= bufSize) break ;
95619561
95629562 const size_t nameOffset = offsetof (VARIABLE_NAME, Name);
95639563 if (bufSize - offset < nameOffset) break ;
95649564
9565+ // read name
95659566 std::wstring nameStr;
9566- {
9567- size_t nameMaxBytes = 0 ;
9568- if (varName->NextEntryOffset != 0 ) {
9569- const SIZE_T ne = static_cast <SIZE_T>(varName->NextEntryOffset );
9570- if (ne <= nameOffset) {
9571- return false ;
9572- }
9573- if (ne > bufSize - offset) {
9574- break ;
9575- }
9576- nameMaxBytes = ne - nameOffset;
9577- }
9578- else {
9579- if (offset + nameOffset >= bufSize) {
9580- return false ;
9581- }
9582- nameMaxBytes = bufSize - (offset + nameOffset);
9583- }
9584-
9585- if (nameMaxBytes > MAX_NAME_BYTES) nameMaxBytes = MAX_NAME_BYTES;
9586-
9587- if (nameMaxBytes >= sizeof (WCHAR)) {
9588- const WCHAR* namePtr = reinterpret_cast <const WCHAR*>(reinterpret_cast <const BYTE*>(varName) + nameOffset);
9589- const size_t maxChars = nameMaxBytes / sizeof (WCHAR);
9590- size_t realChars = 0 ;
9591- while (realChars < maxChars && namePtr[realChars] != L' \0 ' ) ++realChars;
9592- if (realChars == maxChars) return false ;
9593- nameStr.assign (namePtr, realChars);
9594- }
9595- else {
9596- nameStr.clear ();
9597- }
9567+ size_t nameMaxBytes = 0 ;
9568+ if (varName->NextEntryOffset != 0 ) {
9569+ const SIZE_T ne = static_cast <SIZE_T>(varName->NextEntryOffset );
9570+ if (ne <= nameOffset) { cleanup (); return false ; }
9571+ if (ne > bufSize - offset) break ;
9572+ nameMaxBytes = ne - nameOffset;
95989573 }
9599-
9600- std::wstring guidStr;
9601- {
9602- auto guid_to_wstring = [](const GUID& g) -> std::wstring {
9603- wchar_t buf[40 ] = {};
9604- int written = _snwprintf_s (
9605- buf, _countof (buf), _TRUNCATE,
9606- L" {%08lX-%04hX-%04hX-%02X%02X-%02X%02X%02X%02X%02X%02X}" ,
9607- static_cast <unsigned long >(g.Data1 ),
9608- static_cast <u16 >(g.Data2 ),
9609- static_cast <u16 >(g.Data3 ),
9610- static_cast <u32 >(g.Data4 [0 ]),
9611- static_cast <u32 >(g.Data4 [1 ]),
9612- static_cast <u32 >(g.Data4 [2 ]),
9613- static_cast <u32 >(g.Data4 [3 ]),
9614- static_cast <u32 >(g.Data4 [4 ]),
9615- static_cast <u32 >(g.Data4 [5 ]),
9616- static_cast <u32 >(g.Data4 [6 ]),
9617- static_cast <u32 >(g.Data4 [7 ])
9618- );
9619- if (written <= 0 ) return std::wstring ();
9620- return std::wstring (buf);
9621- };
9622-
9623- guidStr = guid_to_wstring (varName->VendorGuid );
9624- if (guidStr.empty ()) return true ;
9574+ else {
9575+ if (offset + nameOffset >= bufSize) { cleanup (); return false ; }
9576+ nameMaxBytes = bufSize - (offset + nameOffset);
96259577 }
9578+ if (nameMaxBytes > MAX_NAME_BYTES) nameMaxBytes = MAX_NAME_BYTES;
9579+ if (nameMaxBytes >= sizeof (WCHAR)) {
9580+ const WCHAR* namePtr = reinterpret_cast <const WCHAR*>(reinterpret_cast <const BYTE*>(varName) + nameOffset);
9581+ const size_t maxChars = nameMaxBytes / sizeof (WCHAR);
9582+ size_t realChars = 0 ;
9583+ while (realChars < maxChars && namePtr[realChars] != L' \0 ' ) ++realChars;
9584+ if (realChars == maxChars) { cleanup (); return false ; }
9585+ nameStr.assign (namePtr, realChars);
9586+ }
9587+
9588+ auto guid_to_wstring = [](const GUID& g)->std ::wstring {
9589+ wchar_t buf[40 ] = {};
9590+ int written = _snwprintf_s (buf, _countof (buf), _TRUNCATE,
9591+ L" {%08lX-%04hX-%04hX-%02X%02X-%02X%02X%02X%02X%02X%02X}" ,
9592+ static_cast <unsigned long >(g.Data1 ),
9593+ static_cast <u16 >(g.Data2 ),
9594+ static_cast <u16 >(g.Data3 ),
9595+ static_cast <u32 >(g.Data4 [0 ]), static_cast <u32 >(g.Data4 [1 ]),
9596+ static_cast <u32 >(g.Data4 [2 ]), static_cast <u32 >(g.Data4 [3 ]),
9597+ static_cast <u32 >(g.Data4 [4 ]), static_cast <u32 >(g.Data4 [5 ]),
9598+ static_cast <u32 >(g.Data4 [6 ]), static_cast <u32 >(g.Data4 [7 ]));
9599+ if (written <= 0 ) return std::wstring ();
9600+ return std::wstring (buf);
9601+ };
9602+ std::wstring guidStr = guid_to_wstring (varName->VendorGuid );
9603+ if (guidStr.empty ()) { cleanup (); return true ; }
96269604
96279605 if (!nameStr.empty () && nameStr.rfind (L" VMM" , 0 ) == 0 ) {
96289606 debug (" NVRAM: Detected hypervisor signature" );
9607+ cleanup ();
96299608 return true ;
96309609 }
96319610
@@ -9635,79 +9614,47 @@ struct VM {
96359614 else if (nameStr == L" dbxDefault" ) found_dbxDefault = true ;
96369615
96379616 std::vector<BYTE> valueBuf;
9638- DWORD readLen = 0 ;
9639- DWORD required = 0 ;
9640- required = GetFirmwareEnvironmentVariableW (nameStr.c_str (), guidStr.c_str (), nullptr , 0 );
9617+ DWORD required = GetFirmwareEnvironmentVariableW (nameStr.c_str (), guidStr.c_str (), nullptr , 0 );
96419618 if (required > 0 ) {
96429619 valueBuf.resize (required);
9643- readLen = GetFirmwareEnvironmentVariableW (nameStr.c_str (), guidStr.c_str (), valueBuf.data (), required);
9644- if (readLen == 0 ) {
9645- valueBuf.clear ();
9646- }
9647- else {
9648- valueBuf.resize (readLen);
9649- }
9620+ DWORD readLen = GetFirmwareEnvironmentVariableW (nameStr.c_str (), guidStr.c_str (), valueBuf.data (), required);
9621+ if (readLen == 0 ) valueBuf.clear (); else valueBuf.resize (readLen);
96509622 }
96519623 else {
96529624 const DWORD fallbackSize = 8192 ;
96539625 valueBuf.resize (fallbackSize);
9654- readLen = GetFirmwareEnvironmentVariableW (nameStr.c_str (), guidStr.c_str (), valueBuf.data (), fallbackSize);
9655- if (readLen > 0 ) {
9656- valueBuf.resize (readLen);
9657- }
9658- else {
9659- valueBuf.clear ();
9660- }
9626+ DWORD readLen = GetFirmwareEnvironmentVariableW (nameStr.c_str (), guidStr.c_str (), valueBuf.data (), fallbackSize);
9627+ if (readLen > 0 ) valueBuf.resize (readLen); else valueBuf.clear ();
96619628 }
96629629
9663- if (nameStr == L" MemoryOverwriteRequestControlLock" ) {
9664- found_MORCL = true ;
9665- }
9630+ if (nameStr == L" MemoryOverwriteRequestControlLock" ) found_MORCL = true ;
96669631
96679632 if (nameStr == L" PKDefault" ) {
96689633 bool pk_has_redhat = false ;
9669- if (!valueBuf.empty ()) {
9670- if (contains_redhat_utf16le_ci (valueBuf) || contains_redhat_ascii_ci (valueBuf)) {
9671- pk_has_redhat = true ;
9672- }
9673- }
9634+ if (!valueBuf.empty () && (contains_redhat_utf16le_ci (valueBuf) || contains_redhat_ascii_ci (valueBuf)))
9635+ pk_has_redhat = true ;
96749636 if (pk_has_redhat) {
96759637 debug (" NVRAM: QEMU detected" );
9676- return core::add (brands::QEMU);
9638+ bool added = core::add (brands::QEMU);
9639+ cleanup ();
9640+ return added;
96779641 }
96789642 }
96799643
96809644 if (varName->NextEntryOffset == 0 ) break ;
9681-
96829645 const SIZE_T ne = static_cast <SIZE_T>(varName->NextEntryOffset );
96839646 const size_t nextOffset = offset + ne;
96849647 if (nextOffset <= offset || nextOffset > bufSize) break ;
9685-
9686- varName = reinterpret_cast <PVARIABLE_NAME>(reinterpret_cast <PBYTE>(res.buffer .data ()) + nextOffset);
9648+ varName = reinterpret_cast <PVARIABLE_NAME>(reinterpret_cast <PBYTE>(resBuffer.data ()) + nextOffset);
96879649 }
96889650
9689- if (!found_MORCL) {
9690- debug (" NVRAM: Missing MemoryOverwriteRequestControlLock" );
9691- return true ;
9692- }
9693-
9694- if (!found_dbDefault) {
9695- debug (" NVRAM: Missing dbDefault" );
9696- return true ;
9697- }
9698- if (!found_dbxDefault) {
9699- debug (" NVRAM: Missing dbxDefault" );
9700- return true ;
9701- }
9702- if (!found_KEKDefault) {
9703- debug (" NVRAM: Missing KEKDefault" );
9704- return true ;
9705- }
9706- if (!found_PKDefault) {
9707- debug (" NVRAM: Missing PKDefault" );
9708- return true ;
9709- }
9651+ if (!found_MORCL) { debug (" NVRAM: Missing MemoryOverwriteRequestControlLock" ); cleanup (); return true ; }
9652+ if (!found_dbDefault) { debug (" NVRAM: Missing dbDefault" ); cleanup (); return true ; }
9653+ if (!found_dbxDefault) { debug (" NVRAM: Missing dbxDefault" ); cleanup (); return true ; }
9654+ if (!found_KEKDefault) { debug (" NVRAM: Missing KEKDefault" ); cleanup (); return true ; }
9655+ if (!found_PKDefault) { debug (" NVRAM: Missing PKDefault" ); cleanup (); return true ; }
97109656
9657+ cleanup ();
97119658 return false ;
97129659 }
97139660
0 commit comments