Windows: verify EFI loader restoration

Verify restored EFI Microsoft and fallback boot loader paths after system decryption.

Show clearer recovery guidance when EFI file restoration or NVRAM cleanup remains incomplete.

Add a GPT-only EFI boot loader repair menu action for already decrypted systems.
This commit is contained in:
Mounir IDRASSI
2026-04-26 09:49:09 +09:00
parent 4fedeb461e
commit df4e755112
49 changed files with 656 additions and 14 deletions

View File

@@ -2660,8 +2660,11 @@ namespace VeraCrypt
return (BootOrderLen != 0) || (GetLastError() != ERROR_INVALID_FUNCTION);
}
void EfiBoot::DeleteStartExec(uint16 statrtOrderNum, wchar_t* type) {
static const unsigned __int64 TC_MAX_EFI_BOOT_LOADER_FILE_SIZE = 16ULL * 1024 * 1024;
bool EfiBoot::DeleteStartExec(uint16 statrtOrderNum, wchar_t* type) {
DWORD dwLastError;
bool bRet = true;
BOOL bPrivilegesSet = IsPrivilegeEnabled (SE_SYSTEM_ENVIRONMENT_NAME);
if (!bPrivilegesSet && !SetPrivilege(SE_SYSTEM_ENVIRONMENT_NAME, TRUE))
{
@@ -2678,11 +2681,22 @@ namespace VeraCrypt
}
wchar_t varName[256];
StringCchPrintfW(varName, ARRAYSIZE (varName), L"%s%04X", type == NULL ? L"Boot" : type, statrtOrderNum);
SetFirmwareEnvironmentVariable(varName, EfiVarGuid, NULL, 0);
if (!SetFirmwareEnvironmentVariable(varName, EfiVarGuid, NULL, 0))
{
dwLastError = GetLastError();
if (dwLastError != ERROR_ENVVAR_NOT_FOUND)
bRet = false;
}
wstring order = L"Order";
order.insert(0, type == NULL ? L"Boot" : type);
uint32 startOrderLen = GetFirmwareEnvironmentVariable(order.c_str(), EfiVarGuid, tempBuf, sizeof(tempBuf));
if (startOrderLen == 0)
{
dwLastError = GetLastError();
if (dwLastError != ERROR_ENVVAR_NOT_FOUND)
bRet = false;
}
uint32 startOrderNumPos = UINT_MAX;
bool startOrderUpdate = false;
uint16* startOrder = (uint16*)tempBuf;
@@ -2703,7 +2717,8 @@ namespace VeraCrypt
}
if (startOrderUpdate) {
SetFirmwareEnvironmentVariable(order.c_str(), EfiVarGuid, startOrder, startOrderLen);
if (!SetFirmwareEnvironmentVariable(order.c_str(), EfiVarGuid, startOrder, startOrderLen))
bRet = false;
// remove ourselves from BootNext value
uint16 bootNextValue = 0;
@@ -2714,12 +2729,19 @@ namespace VeraCrypt
&& (bootNextValue == statrtOrderNum)
)
{
SetFirmwareEnvironmentVariable(next.c_str(), EfiVarGuid, startOrder, 0);
if (!SetFirmwareEnvironmentVariable(next.c_str(), EfiVarGuid, NULL, 0))
{
dwLastError = GetLastError();
if (dwLastError != ERROR_ENVVAR_NOT_FOUND)
bRet = false;
}
}
}
if (!bPrivilegesSet)
SetPrivilege(SE_SYSTEM_ENVIRONMENT_NAME, FALSE);
return bRet;
}
void EfiBoot::SetStartExec(wstring description, wstring execPath, bool setBootEntry, bool forceFirstBootEntry, bool setBootNext, uint16 statrtOrderNum , wchar_t* type, uint32 attr) {
@@ -3019,6 +3041,32 @@ namespace VeraCrypt
return bRet;
}
bool EfiBoot::FileHasPattern (const wchar_t* name, const void* pattern, size_t patternLen)
{
std::vector<uint8> fileContent;
if (!ReadFileToBuffer (name, fileContent))
return false;
return BufferHasPattern (fileContent.data (), fileContent.size (), pattern, patternLen);
}
bool EfiBoot::IsVeraCryptBootLoader (const wchar_t* name)
{
std::vector<uint8> fileContent;
if (!ReadFileToBuffer (name, fileContent))
return false;
const wchar_t* appName = _T(TC_APP_NAME);
return BufferHasPattern (fileContent.data (), fileContent.size (), appName, wcslen (appName) * sizeof (wchar_t))
|| BufferHasPattern (fileContent.data (), fileContent.size (), TC_APP_NAME, strlen (TC_APP_NAME));
}
bool EfiBoot::IsWindowsBootLoader (const wchar_t* name)
{
const char* g_szMsBootString = "bootmgfw.pdb";
return FileHasPattern (name, g_szMsBootString, strlen (g_szMsBootString));
}
void EfiBoot::SaveFile(const wchar_t* name, uint8* data, DWORD size) {
wstring path = EfiBootPartPath;
path += name;
@@ -3056,6 +3104,47 @@ namespace VeraCrypt
f.Close();
}
bool EfiBoot::ReadFileToBuffer (const wchar_t* name, std::vector<uint8>& fileContent)
{
fileContent.clear ();
wstring path = EfiBootPartPath;
path += name;
File f(path, true);
if (!f.IsOpened ())
{
f.Close ();
return false;
}
unsigned __int64 fileSize = 0;
f.GetFileSize (fileSize);
if (fileSize == 0)
{
f.Close ();
return false;
}
if (fileSize > TC_MAX_EFI_BOOT_LOADER_FILE_SIZE || fileSize > UINT_MAX)
{
f.Close ();
throw ErrorException (wstring (GetString ("EFI_BOOT_LOADER_FILE_TOO_LARGE"))
+ L"\n" + name, SRC_POS);
}
DWORD fileSize32 = (DWORD) fileSize;
fileContent.resize ((size_t) fileSize32);
if (f.Read (fileContent.data (), fileSize32) != fileSize32)
{
f.Close ();
throw ErrorException (wstring (GetString ("EFI_BOOT_LOADER_FILE_READ_FAILED"))
+ L"\n" + name, SRC_POS);
}
f.Close ();
return true;
}
void EfiBoot::CopyFile(const wchar_t* name, const wchar_t* targetName) {
wstring path = EfiBootPartPath;
path += name;
@@ -4502,11 +4591,15 @@ namespace VeraCrypt
EfiBootInst.PrepareBootPartition();
EfiBootInst.DeleteStartExec();
EfiBootInst.DeleteStartExec(0xDC5B, L"Driver"); // remove DcsBml boot driver it was installed
EfiBootInst.RenameFile(L"\\EFI\\Boot\\original_bootx64.vc_backup", L"\\EFI\\Boot\\bootx64.efi", TRUE);
const wchar_t * szStdMsBootloader = L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi";
const wchar_t * szBackupMsBootloader = L"\\EFI\\Microsoft\\Boot\\bootmgfw_ms.vc";
// EFI system encryption currently ships x64 EFI loaders and the UEFI fallback path is bootx64.efi
const wchar_t * szStdEfiBootloader = L"\\EFI\\Boot\\bootx64.efi";
const wchar_t * szBackupEfiBootloader = L"\\EFI\\Boot\\original_bootx64.vc_backup";
if (!EfiBootInst.RenameFile(L"\\EFI\\Microsoft\\Boot\\bootmgfw_ms.vc", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi", TRUE))
EfiBootInst.RenameFile(szBackupEfiBootloader, szStdEfiBootloader, TRUE);
if (!EfiBootInst.RenameFile(szBackupMsBootloader, szStdMsBootloader, TRUE))
{
EfiBootConf conf;
if (EfiBootInst.ReadConfig (L"\\EFI\\VeraCrypt\\DcsProp", conf) && strlen (conf.actionSuccessValue.c_str()))
@@ -4515,8 +4608,9 @@ namespace VeraCrypt
if (EfiBootConf::IsPostExecFileField (conf.actionSuccessValue, loaderPath))
{
// check that it is not bootmgfw_ms.vc or bootmgfw.efi
if ( (0 != _wcsicmp (loaderPath.c_str(), L"\\EFI\\Microsoft\\Boot\\bootmgfw_ms.vc"))
&& (0 != _wcsicmp (loaderPath.c_str(), L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi"))
if ( (0 != _wcsicmp (loaderPath.c_str(), szBackupMsBootloader))
&& (0 != _wcsicmp (loaderPath.c_str(), szStdMsBootloader))
&& EfiBootInst.FileExists (loaderPath.c_str())
)
{
const char* g_szMsBootString = "bootmgfw.pdb";
@@ -4529,13 +4623,48 @@ namespace VeraCrypt
// look for bootmgfw.efi identifiant string
if (BufferHasPattern (bootLoaderBuf.data (), (size_t) loaderSize, g_szMsBootString, strlen (g_szMsBootString)))
{
EfiBootInst.RenameFile(loaderPath.c_str(), L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi", TRUE);
EfiBootInst.RenameFile(loaderPath.c_str(), szStdMsBootloader, TRUE);
}
}
}
}
}
bool bMsBootloaderRestored = EfiBootInst.FileExists (szStdMsBootloader) && EfiBootInst.IsWindowsBootLoader (szStdMsBootloader);
if (!bMsBootloaderRestored
&& EfiBootInst.FileExists (szStdEfiBootloader)
&& EfiBootInst.IsWindowsBootLoader (szStdEfiBootloader))
{
EfiBootInst.CopyFile (szStdEfiBootloader, szStdMsBootloader);
bMsBootloaderRestored = EfiBootInst.FileExists (szStdMsBootloader) && EfiBootInst.IsWindowsBootLoader (szStdMsBootloader);
}
if (!bMsBootloaderRestored)
{
throw ErrorException (wstring (GetString ("SYS_LOADER_RESTORE_FAILED"))
+ L"\n\n" + GetString ("EFI_MS_BOOT_LOADER_RESTORE_FAILED") + L"\n"
+ szStdMsBootloader, SRC_POS);
}
if (EfiBootInst.FileExists (szStdEfiBootloader) && EfiBootInst.IsVeraCryptBootLoader (szStdEfiBootloader))
{
EfiBootInst.CopyFile (szStdMsBootloader, szStdEfiBootloader);
if (EfiBootInst.IsVeraCryptBootLoader (szStdEfiBootloader) || !EfiBootInst.IsWindowsBootLoader (szStdEfiBootloader))
{
throw ErrorException (wstring (GetString ("SYS_LOADER_RESTORE_FAILED"))
+ L"\n\n" + GetString ("EFI_FALLBACK_BOOT_LOADER_STILL_VERACRYPT") + L"\n"
+ szStdEfiBootloader, SRC_POS);
}
}
bool bBootEntryRemoved = EfiBootInst.DeleteStartExec();
bool bBmlDriverEntryRemoved = EfiBootInst.DeleteStartExec(0xDC5B, L"Driver"); // remove DcsBml boot driver if it was installed
if (!bBootEntryRemoved || !bBmlDriverEntryRemoved)
{
// Keep VeraCrypt EFI files in place if firmware entries may still point to them.
throw ErrorException (wstring (GetString ("SYS_LOADER_RESTORE_FAILED"))
+ L"\n\n" + GetString ("EFI_BOOT_LOADER_NVRAM_CLEANUP_FAILED"), SRC_POS);
}
EfiBootInst.DelFile(L"\\DcsBoot.efi");
EfiBootInst.DelFile(L"\\DcsInt.efi");
@@ -5311,6 +5440,14 @@ namespace VeraCrypt
RestoreSystemLoader ();
}
catch (ErrorException &e)
{
if (!e.ErrMsg.empty())
throw;
e.Show (ParentWindow);
throw ErrorException ("SYS_LOADER_RESTORE_FAILED", SRC_POS);
}
catch (Exception &e)
{
e.Show (ParentWindow);

View File

@@ -198,15 +198,19 @@ namespace VeraCrypt
void PrepareBootPartition(bool bDisableException = false);
bool IsEfiBoot();
void DeleteStartExec(uint16 statrtOrderNum = 0xDC5B, wchar_t* type = NULL);
bool DeleteStartExec(uint16 statrtOrderNum = 0xDC5B, wchar_t* type = NULL);
void SetStartExec(wstring description, wstring execPath, bool setBootEntry = true, bool forceFirstBootEntry = true, bool setBootNext = true, uint16 statrtOrderNum = 0xDC5B, wchar_t* type = NULL, uint32 attr = 1);
void SaveFile(const wchar_t* name, uint8* data, DWORD size);
void GetFileSize(const wchar_t* name, unsigned __int64& size);
void ReadFile(const wchar_t* name, uint8* data, DWORD size);
bool ReadFileToBuffer (const wchar_t* name, std::vector<uint8>& fileContent);
void CopyFile(const wchar_t* name, const wchar_t* targetName);
bool FileExists(const wchar_t* name);
static bool CompareFiles (const wchar_t* fileName1, const wchar_t* fileName2);
static bool CompareFileData (const wchar_t* fileName, const uint8* data, DWORD size);
bool FileHasPattern (const wchar_t* name, const void* pattern, size_t patternLen);
bool IsVeraCryptBootLoader (const wchar_t* name);
bool IsWindowsBootLoader (const wchar_t* name);
BOOL RenameFile(const wchar_t* name, const wchar_t* nameNew, BOOL bForce);
BOOL DelFile(const wchar_t* name);

View File

@@ -1658,6 +1658,16 @@
<entry lang="en" key="IDC_SECURE_DESKTOP_ENABLE_IME">Enable Input Method Editor (IME) in Secure Desktop</entry>
<entry lang="en" key="ENABLE_IME_IN_SECURE_DESKTOP_WARNING">WARNING: Enable this option only if you are encountering issues when selecting Keyfiles/Tokens under Secure Desktop.</entry>
<entry lang="en" key="ERR_KEY_DERIVATION_FAILED">Key derivation failed. This may be caused by insufficient memory or an interrupted operation.</entry>
<entry lang="en" key="EFI_MS_BOOT_LOADER_RESTORE_FAILED">The system partition/drive is already decrypted, but the EFI Microsoft boot loader path was not restored to the Windows Boot Manager. Only the EFI boot files need repair. Use the VeraCrypt Rescue Disk repair option, or boot Windows recovery media and run 'bcdboot W:\\Windows /s S: /f UEFI' after replacing W: with the Windows volume drive letter and S: with the EFI System Partition drive letter. Path:</entry>
<entry lang="en" key="EFI_FALLBACK_BOOT_LOADER_STILL_VERACRYPT">The system partition/drive is already decrypted, but the EFI fallback boot loader path still contains the VeraCrypt Boot Loader. Only the EFI boot files need repair. Use the VeraCrypt Rescue Disk repair option, or boot Windows recovery media and run 'bcdboot W:\\Windows /s S: /f UEFI' after replacing W: with the Windows volume drive letter and S: with the EFI System Partition drive letter. Path:</entry>
<entry lang="en" key="IDM_REPAIR_EFI_BOOT_LOADER">Repair EFI Boot Loader...</entry>
<entry lang="en" key="CONFIRM_REPAIR_EFI_BOOT_LOADER">VeraCrypt will restore the Windows EFI boot loader paths and remove VeraCrypt EFI boot entries and files.\n\nUse this only after the system partition/drive is fully decrypted and Windows can boot without system encryption.\n\nDo you want to continue?</entry>
<entry lang="en" key="EFI_BOOT_LOADER_FILE_READ_FAILED">The EFI boot loader file could not be read completely:</entry>
<entry lang="en" key="EFI_BOOT_LOADER_FILE_TOO_LARGE">The EFI boot loader file is unexpectedly large and was not inspected:</entry>
<entry lang="en" key="EFI_BOOT_LOADER_NVRAM_CLEANUP_FAILED">The system partition/drive is already decrypted and the EFI boot loader files were restored, but VeraCrypt could not remove one or more VeraCrypt firmware boot entries. The VeraCrypt EFI files were left in place so any remaining firmware entry still points to an existing loader. Retry as Administrator or remove the VeraCrypt boot entry from firmware setup after confirming Windows Boot Manager starts normally.</entry>
<entry lang="en" key="EFI_BOOT_LOADER_REPAIR_BLOCKED">The EFI boot loader cannot be repaired while system encryption or decryption is active or incomplete. If decryption has just completed, reboot Windows and try again.</entry>
<entry lang="en" key="EFI_BOOT_LOADER_REPAIR_NOT_APPLICABLE">This repair action is available only on systems booting in UEFI mode from a GPT system partition.</entry>
<entry lang="en" key="EFI_BOOT_LOADER_REPAIR_SUCCESS">The EFI boot loader has been repaired successfully.</entry>
</localization>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="VeraCrypt">

View File

@@ -6677,6 +6677,7 @@ BOOL CALLBACK MainDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa
catch (Exception &e)
{
e.Show (hwndDlg);
return 1;
}
ManageStartupSeqWiz (TRUE, L"");
@@ -10782,4 +10783,4 @@ int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpsz
return 0;
}
#endif
#endif

View File

@@ -663,6 +663,10 @@ static void InitMainDialog (HWND hwndDlg)
{
EnableMenuItem (GetMenu (hwndDlg), IDM_CREATE_HIDDEN_OS, MF_GRAYED);
}
else
{
EnableMenuItem (GetMenu (hwndDlg), IDM_REPAIR_EFI_BOOT_LOADER, MF_GRAYED);
}
}
// Disable menu item for changing system header key derivation algorithm until it's implemented
@@ -1464,10 +1468,12 @@ unsigned __int64 GetSysEncDeviceEncryptedPartSize (BOOL bSilent)
static void PopulateSysEncContextMenu (HMENU popup, BOOL bToolsOnly)
{
SystemDriveConfiguration config;
BOOL bRepairEfiBootLoaderApplicable = FALSE;
try
{
BootEncStatus = BootEncObj->GetStatus();
config = BootEncObj->GetSystemDriveConfiguration();
bRepairEfiBootLoaderApplicable = config.SystemPartition.IsGPT;
}
catch (Exception &e)
{
@@ -1501,6 +1507,8 @@ static void PopulateSysEncContextMenu (HMENU popup, BOOL bToolsOnly)
AppendMenuW (popup, MF_STRING, IDM_CREATE_RESCUE_DISK, GetString ("IDM_CREATE_RESCUE_DISK"));
AppendMenuW (popup, MF_STRING, IDM_VERIFY_RESCUE_DISK, GetString ("IDM_VERIFY_RESCUE_DISK"));
AppendMenuW (popup, MF_STRING, IDM_VERIFY_RESCUE_DISK_ISO, GetString ("IDM_VERIFY_RESCUE_DISK_ISO"));
if (bRepairEfiBootLoaderApplicable)
AppendMenuW (popup, MF_STRING, IDM_REPAIR_EFI_BOOT_LOADER, GetString ("IDM_REPAIR_EFI_BOOT_LOADER"));
}
if (!bToolsOnly)
@@ -6454,6 +6462,63 @@ static void DecryptSystemDevice (HWND hwndDlg)
Warning ("SYSTEM_ENCRYPTION_IN_PROGRESS_ELSEWHERE", hwndDlg);
}
static void RepairEfiBootLoader (HWND hwndDlg)
{
SystemDriveConfiguration config;
try
{
BootEncStatus = BootEncObj->GetStatus();
config = BootEncObj->GetSystemDriveConfiguration ();
}
catch (Exception &e)
{
e.Show (hwndDlg);
return;
}
if (!config.SystemPartition.IsGPT)
{
Warning ("EFI_BOOT_LOADER_REPAIR_NOT_APPLICABLE", hwndDlg);
return;
}
if (IsHiddenOSRunning()
|| BootEncStatus.SetupInProgress
|| BootEncStatus.DriveEncrypted
|| BootEncStatus.DriveMounted
|| SysEncryptionOrDecryptionRequired ())
{
Warning ("EFI_BOOT_LOADER_REPAIR_BLOCKED", hwndDlg);
return;
}
if (AskWarnNoYes ("CONFIRM_REPAIR_EFI_BOOT_LOADER", hwndDlg) == IDNO)
return;
if (!CreateSysEncMutex ())
{
Warning ("SYSTEM_ENCRYPTION_IN_PROGRESS_ELSEWHERE", hwndDlg);
return;
}
WaitCursor ();
try
{
BootEncObj->RestoreSystemLoader ();
}
catch (Exception &e)
{
NormalCursor ();
CloseSysEncMutex ();
e.Show (hwndDlg);
return;
}
NormalCursor ();
CloseSysEncMutex ();
Info ("EFI_BOOT_LOADER_REPAIR_SUCCESS", hwndDlg);
}
// Initiates the process of creation of a hidden operating system
static void CreateHiddenOS (HWND hwndDlg)
{
@@ -8519,6 +8584,9 @@ BOOL CALLBACK MainDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa
case IDM_VERIFY_RESCUE_DISK_ISO:
VerifyRescueDisk (hwndDlg, true);
break;
case IDM_REPAIR_EFI_BOOT_LOADER:
RepairEfiBootLoader (hwndDlg);
break;
case IDM_MOUNT_SYSENC_PART_WITHOUT_PBA:
if (CheckSysEncMountWithoutPBA (hwndDlg, L"", FALSE))

View File

@@ -699,6 +699,7 @@ BEGIN
MENUITEM "Create Rescue Disk...", IDM_CREATE_RESCUE_DISK
MENUITEM "Verify Rescue Disk", IDM_VERIFY_RESCUE_DISK
MENUITEM "Verify Rescue Disk Image", IDM_VERIFY_RESCUE_DISK_ISO
MENUITEM "Repair EFI Boot Loader...", IDM_REPAIR_EFI_BOOT_LOADER
MENUITEM SEPARATOR
MENUITEM "Mount Without Pre-Boot &Authentication...", IDM_MOUNT_SYSENC_PART_WITHOUT_PBA
MENUITEM SEPARATOR

View File

@@ -275,6 +275,7 @@
#define IDM_DECRYPT_NONSYS_VOL 40067
#define IDM_VERIFY_RESCUE_DISK_ISO 40068
#define IDM_MOUNIT_NO_CACHE 40069
#define IDM_REPAIR_EFI_BOOT_LOADER 40070
// Next default values for new objects
//
@@ -282,7 +283,7 @@
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 123
#define _APS_NEXT_COMMAND_VALUE 40070
#define _APS_NEXT_COMMAND_VALUE 40071
#define _APS_NEXT_CONTROL_VALUE 1184
#define _APS_NEXT_SYMED_VALUE 101
#endif