From 0d2b75ef0a09efa0a6190115979ca092dc8a52fa Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Wed, 20 May 2026 18:55:49 +0900 Subject: [PATCH] Windows: add read-only recovery mount for interrupted non-system in-place volumes --- .../en/Command Line Usage for Windows.html | 3 +- doc/html/ru/Command Line Usage.html | 3 +- doc/html/zh-cn/Command Line Usage.html | 5 +- src/Common/Apidrvr.h | 1 + src/Common/Common.h | 1 + src/Common/Common.rc | 40 ++-- src/Common/Dlgcode.c | 27 ++- src/Common/Language.xml | 3 + src/Common/Resource.h | 3 +- src/Common/Tcdefs.h | 3 +- src/Common/Volumes.h | 3 + src/Driver/EncryptedIoQueue.c | 113 ++++++++++ src/Driver/EncryptedIoQueue.h | 4 + src/Driver/Ntdriver.c | 197 +++++++++++++----- src/Driver/Ntvol.c | 97 ++++++++- src/Driver/Ntvol.h | 1 + src/Format/InPlace.c | 2 - src/Mount/Mount.c | 71 ++++++- 18 files changed, 480 insertions(+), 97 deletions(-) diff --git a/doc/html/en/Command Line Usage for Windows.html b/doc/html/en/Command Line Usage for Windows.html index b490ec8e..10435172 100644 --- a/doc/html/en/Command Line Usage for Windows.html +++ b/doc/html/en/Command Line Usage for Windows.html @@ -173,6 +173,7 @@ Note that turning the password cache off will not clear it (use /w to clear the fact that the password needs to be typed in the pre-boot environment (before Windows starts) where non-US Windows keyboard layouts are not available.

bk or headerbak: Mount volume using embedded backup header. Note: All volumes created by VeraCrypt contain an embedded backup header (located at the end of the volume).

recovery: Do not verify any checksums stored in the volume header. This option should be used only when the volume header is damaged and the volume cannot be mounted even with the mount option headerbak. Example: /m ro

+

recoveryro, inplacerecovery, or nonsysinplacerecovery: Recovery-mount an interrupted non-system in-place encryption/decryption device-hosted volume as read-only using the embedded backup header. Use this option only to copy data from a sector-by-sector clone or image.

label=LabelValue: Use the given string value LabelValue as a label of the mounted volume in Windows Explorer. The maximum length for LabelValue  is 32 characters for NTFS volumes and 11 characters for FAT volumes. For example, /m label=MyDrive will set the label of the drive in Explorer to MyDrive.

@@ -320,7 +321,7 @@ If it is followed by n or no: the password dia

Syntax

-

VeraCrypt.exe [/tc] [/hash {sha256|sha-256|sha512|sha-512|whirlpool |blake2s|blake2s-256}][/a [devices|favorites]] [/b] [/c [y|n|f]] [/d [drive letter]] [/e] [/f] [/h [y|n]] [/k keyfile or search path] [tryemptypass [y|n]] [/l drive letter] [/m {bk|rm|recovery|ro|sm|ts|noattach}] +

VeraCrypt.exe [/tc] [/hash {sha256|sha-256|sha512|sha-512|whirlpool |blake2s|blake2s-256}][/a [devices|favorites]] [/b] [/c [y|n|f]] [/d [drive letter]] [/e] [/f] [/h [y|n]] [/k keyfile or search path] [tryemptypass [y|n]] [/l drive letter] [/m {bk|rm|recovery|recoveryro|ro|sm|ts|noattach}] [/p password] [/pim pimvalue] [/q [background|preferences]] [/s] [/tokenlib path] [/v volume] [/w]

"VeraCrypt Format.exe" [/n] [/create] [/size number[{K|M|G|T}]] [/p password]  [/encryption {AES | Serpent | Twofish | Camellia | Kuznyechik | AES(Twofish) | AES(Twofish(Serpent)) | Serpent(AES) | Serpent(Twofish(AES)) | Twofish(Serpent) | Camellia(Kuznyechik) | Kuznyechik(Twofish) | Camellia(Serpent) | Kuznyechik(AES) | Kuznyechik(Serpent(Camellia)))}] [/hash {sha256|sha-256|sha512|sha-512|whirlpool|blake2s|blake2s-256}] [/filesystem {None|FAT|NTFS|ExFAT|ReFS}] [/dynamic] [/force] [/silent] [/noisocheck] [FastCreateFile] [/quick]

diff --git a/doc/html/ru/Command Line Usage.html b/doc/html/ru/Command Line Usage.html index 0f47f20b..5e3e040a 100644 --- a/doc/html/ru/Command Line Usage.html +++ b/doc/html/ru/Command Line Usage.html @@ -168,6 +168,7 @@ что пароль требуется вводить на этапе до загрузки операционной системы (до запуска Windows), когда раскладки клавиатуры, отличные от американской, ещё недоступны.

bk или headerbak: смонтировать том, используя встроенную резервную копию заголовка. Примечание: встроенная резервная копия заголовка содержится во всех томах, созданных VeraCrypt (эта копия располагается в конце тома).

recovery: не проверять контрольные суммы, хранящиеся в заголовке тома. Этот параметр следует использовать только при повреждении заголовка тома и когда такой том невозможно смонтировать даже с параметром headerbak. Пример: /m ro

+

recoveryro, inplacerecovery или nonsysinplacerecovery: восстановительное монтирование прерванного несистемного тома с шифрованием/расшифровкой на месте, размещённого на устройстве, только для чтения с использованием встроенного резервного заголовка. Используйте этот параметр только для копирования данных с посекторного клона или образа.

label=LabelValue: использовать указанную строку LabelValue как метку смонтированного тома в Проводнике Windows. Максимальная длина LabelValue  – 32 символа для томов NTFS и 11 символов для томов FAT. Например, /m label=MyDrive установит для диска в Проводнике метку тома MyDrive.

@@ -305,7 +306,7 @@

Синтаксис

-

VeraCrypt.exe [/tc] [/hash {sha256|sha-256|sha512|sha-512|whirlpool |blake2s|blake2s-256}][/a [devices|favorites]] [/b] [/c [y|n|f]] [/d [буква диска]] [/e] [/f] [/h [y|n]] [/k ключевой файл или путь поиска] [tryemptypass [y|n]] [/l буква диска] [/m {bk|rm|recovery|ro|sm|ts|noattach}] +

VeraCrypt.exe [/tc] [/hash {sha256|sha-256|sha512|sha-512|whirlpool |blake2s|blake2s-256}][/a [devices|favorites]] [/b] [/c [y|n|f]] [/d [буква диска]] [/e] [/f] [/h [y|n]] [/k ключевой файл или путь поиска] [tryemptypass [y|n]] [/l буква диска] [/m {bk|rm|recovery|recoveryro|ro|sm|ts|noattach}] [/p пароль] [/pim значение pim] [/q [background|preferences]] [/s] [/tokenlib путь] [/v том] [/w]

"VeraCrypt Format.exe" [/n] [/create] [/size число[{K|M|G|T}]] [/p пароль]  [/encryption {AES | Serpent | Twofish | Camellia | Kuznyechik | AES(Twofish) | AES(Twofish(Serpent)) | Serpent(AES) | Serpent(Twofish(AES)) | Twofish(Serpent) | Camellia(Kuznyechik) | Kuznyechik(Twofish) | Camellia(Serpent) | Kuznyechik(AES) | Kuznyechik(Serpent(Camellia))}] [/hash {sha256|sha-256|sha512|sha-512|whirlpool|blake2s|blake2s-256}] [/filesystem {пусто|FAT|NTFS|ExFAT|ReFS}] [/dynamic] [/force] [/silent] [/noisocheck] [FastCreateFile] [/quick]

diff --git a/doc/html/zh-cn/Command Line Usage.html b/doc/html/zh-cn/Command Line Usage.html index c5227542..3490a059 100644 --- a/doc/html/zh-cn/Command Line Usage.html +++ b/doc/html/zh-cn/Command Line Usage.html @@ -165,6 +165,7 @@

smsystem:无需预启动验证,挂载位于系统加密密钥范围内的分区(例如,位于未运行的其他操作系统的加密系统驱动器上的分区)。 例如,用于备份或修复操作。注意:如果您将密码作为 /p 的参数提供,请确保使用标准美式键盘布局键入密码(相比之下,GUI 会自动确保这一点)。这是必需的,因为密码需要在预启动环境(Windows 启动之前)中键入,而在该环境中非美式 Windows 键盘布局不可用。

bkheaderbak:使用嵌入式备份头挂载卷。注意:VeraCrypt 创建的所有卷都包含一个嵌入式备份头(位于卷的末尾)。

recovery:不验证存储在卷头中的任何校验和。仅当卷头损坏且即使使用挂载选项 headerbak 也无法挂载卷时,才应使用此选项。示例:/m ro

+

recoveryroinplacerecoverynonsysinplacerecovery:使用嵌入式备份头,以只读方式恢复挂载已中断的非系统就地加密/解密设备托管卷。仅应使用此选项从逐扇区克隆或映像中复制数据。

label=LabelValue:使用给定的字符串值 LabelValue 作为 Windows 资源管理器中挂载卷的标签。对于 NTFS 卷, LabelValue  的最大长度为 32 个字符,对于 FAT 卷,最大长度为 11 个字符。例如, /m label=我的驱动器 将资源管理器中的驱动器标签设置为 我的驱动器

@@ -299,7 +300,7 @@

语法

-

VeraCrypt.exe [/tc] [/hash {sha256|sha-256|sha512|sha-512|whirlpool |blake2s|blake2s-256}][/a [devices|favorites]] [/b] [/c [y|n|f]] [/d [drive letter]] [/e] [/f] [/h [y|n]] [/k keyfile or search path] [tryemptypass [y|n]] [/l drive letter] [/m {bk|rm|recovery|ro|sm|ts|noattach}] +

VeraCrypt.exe [/tc] [/hash {sha256|sha-256|sha512|sha-512|whirlpool |blake2s|blake2s-256}][/a [devices|favorites]] [/b] [/c [y|n|f]] [/d [drive letter]] [/e] [/f] [/h [y|n]] [/k keyfile or search path] [tryemptypass [y|n]] [/l drive letter] [/m {bk|rm|recovery|recoveryro|ro|sm|ts|noattach}] [/p password] [/pim pimvalue] [/q [background|preferences]] [/s] [/tokenlib path] [/v volume] [/w]

"VeraCrypt Format.exe" [/n] [/create] [/size number[{K|M|G|T}]] [/p password]  [/encryption {AES | Serpent | Twofish | Camellia | Kuznyechik | AES(Twofish) | AES(Twofish(Serpent)) | Serpent(AES) | Serpent(Twofish(AES)) | Twofish(Serpent) | Camellia(Kuznyechik) | Kuznyechik(Twofish) | Camellia(Serpent) | Kuznyechik(AES) | Kuznyechik(Serpent(Camellia))}] [/hash {sha256|sha-256|sha512|sha-512|whirlpool|blake2s|blake2s-256}] [/filesystem {None|FAT|NTFS|ExFAT|ReFS}] [/dynamic] [/force] [/silent] [/noisocheck] [FastCreateFile] [/quick]

@@ -315,4 +316,4 @@

使用密码 test 创建一个 10 MB 的文件容器,并使用 FAT 格式化:

"C:\Program Files\VeraCrypt\VeraCrypt Format.exe" /create c:\Data\test.hc /password test /hash sha512 /encryption serpent /filesystem FAT /size 10M /force

-
\ No newline at end of file +
diff --git a/src/Common/Apidrvr.h b/src/Common/Apidrvr.h index 81213a27..aaa0bc8d 100644 --- a/src/Common/Apidrvr.h +++ b/src/Common/Apidrvr.h @@ -164,6 +164,7 @@ typedef struct Password ProtectedHidVolPassword; /* Password to the hidden volume to be protected against overwriting */ BOOL UseBackupHeader; BOOL RecoveryMode; + BOOL NonSysInplaceRecoveryReadOnly; int pkcs5_prf; int ProtectedHidVolPkcs5Prf; BOOL VolumeMountedReadOnlyAfterPartialSysEnc; diff --git a/src/Common/Common.h b/src/Common/Common.h index 00c62798..03c2b20b 100644 --- a/src/Common/Common.h +++ b/src/Common/Common.h @@ -89,6 +89,7 @@ typedef struct Password ProtectedHidVolPassword; /* Password of hidden volume to protect against overwriting */ BOOL UseBackupHeader; BOOL RecoveryMode; + BOOL NonSysInplaceRecoveryReadOnly; int ProtectedHidVolPkcs5Prf; int ProtectedHidVolPim; wchar_t Label[33]; /* maximum label length is 32 for NTFS and 11 for FAT32 */ diff --git a/src/Common/Common.rc b/src/Common/Common.rc index bb9f617d..2f9c05ec 100644 --- a/src/Common/Common.rc +++ b/src/Common/Common.rc @@ -65,7 +65,7 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,248,190,50,14 END -IDD_MOUNT_OPTIONS DIALOGEX 0, 0, 310, 244 +IDD_MOUNT_OPTIONS DIALOGEX 0, 0, 310, 258 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "VeraCrypt - Mount Options" FONT 8, "MS Shell Dlg", 400, 0, 0x1 @@ -75,29 +75,31 @@ BEGIN "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,25,231,10 CONTROL "Use backup header embedded in &volume if available",IDC_USE_EMBEDDED_HEADER_BAK, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,39,295,11 - CONTROL "Mount partition &using system encryption without pre-boot authentication",IDC_MOUNT_SYSENC_PART_WITHOUT_PBA, + CONTROL "Recovery mount interrupted non-system in-place volume as read-only",IDC_NONSYS_INPLACE_RECOVERY_READONLY, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,53,295,11 - EDITTEXT IDC_VOLUME_LABEL,134,82,167,14,ES_AUTOHSCROLL + CONTROL "Mount partition &using system encryption without pre-boot authentication",IDC_MOUNT_SYSENC_PART_WITHOUT_PBA, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,67,295,11 + EDITTEXT IDC_VOLUME_LABEL,134,96,167,14,ES_AUTOHSCROLL CONTROL "&Protect hidden volume against damage caused by writing to outer volume",IDC_PROTECT_HIDDEN_VOL, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,115,283,10 - EDITTEXT IDC_PASSWORD_PROT_HIDVOL,134,133,167,14,ES_PASSWORD | ES_AUTOHSCROLL - COMBOBOX IDC_PKCS5_PRF_ID,134,154,91,90,CBS_DROPDOWNLIST | WS_TABSTOP - EDITTEXT IDC_PIM,134,174,42,14,ES_RIGHT | ES_PASSWORD | ES_AUTOHSCROLL | ES_NUMBER | NOT WS_VISIBLE - CONTROL "Use P&IM",IDC_PIM_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,134,179,97,10 - LTEXT "(Empty or 0 for defaults)",IDC_PIM_HELP,181,177,121,8,NOT WS_VISIBLE - CONTROL "&Display password",IDC_SHOW_PASSWORD_MO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,134,192,90,10 - CONTROL "U&se keyfiles",IDC_KEYFILES_ENABLE_HIDVOL_PROT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,134,205,90,10 - PUSHBUTTON "&Keyfiles...",IDC_KEYFILES_HIDVOL_PROT,239,201,60,14 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,129,283,10 + EDITTEXT IDC_PASSWORD_PROT_HIDVOL,134,147,167,14,ES_PASSWORD | ES_AUTOHSCROLL + COMBOBOX IDC_PKCS5_PRF_ID,134,168,91,90,CBS_DROPDOWNLIST | WS_TABSTOP + EDITTEXT IDC_PIM,134,188,42,14,ES_RIGHT | ES_PASSWORD | ES_AUTOHSCROLL | ES_NUMBER | NOT WS_VISIBLE + CONTROL "Use P&IM",IDC_PIM_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,134,193,97,10 + LTEXT "(Empty or 0 for defaults)",IDC_PIM_HELP,181,191,121,8,NOT WS_VISIBLE + CONTROL "&Display password",IDC_SHOW_PASSWORD_MO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,134,206,90,10 + CONTROL "U&se keyfiles",IDC_KEYFILES_ENABLE_HIDVOL_PROT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,134,219,90,10 + PUSHBUTTON "&Keyfiles...",IDC_KEYFILES_HIDVOL_PROT,239,215,60,14 DEFPUSHBUTTON "OK",IDOK,246,7,60,14 PUSHBUTTON "Cancel",IDCANCEL,246,24,60,14 - LTEXT "What is hidden volume protection?",IDC_LINK_HIDVOL_PROTECTION_INFO,16,220,279,10,SS_NOTIFY - RTEXT "P&assword to hidden volume:\n(if empty, cache is used)",IDT_HIDDEN_PROT_PASSWD,15,132,115,17,0,WS_EX_RIGHT - GROUPBOX "Hidden Volume Protection",IDT_HIDDEN_VOL_PROTECTION,6,101,299,136 - RTEXT "KDF:",IDT_KDF,15,155,115,17 - RTEXT "Volume PIM:",IDT_PIM,15,177,115,17,NOT WS_VISIBLE - LTEXT "Volume Label in Windows:",IDT_VOLUME_LABEL,12,85,115,8 + LTEXT "What is hidden volume protection?",IDC_LINK_HIDVOL_PROTECTION_INFO,16,234,279,10,SS_NOTIFY + RTEXT "P&assword to hidden volume:\n(if empty, cache is used)",IDT_HIDDEN_PROT_PASSWD,15,146,115,17,0,WS_EX_RIGHT + GROUPBOX "Hidden Volume Protection",IDT_HIDDEN_VOL_PROTECTION,6,115,299,136 + RTEXT "KDF:",IDT_KDF,15,169,115,17 + RTEXT "Volume PIM:",IDT_PIM,15,191,115,17,NOT WS_VISIBLE + LTEXT "Volume Label in Windows:",IDT_VOLUME_LABEL,12,99,115,8 CONTROL "Only create virtual device without mounting on selected drive letter",IDC_DISABLE_MOUNT_MANAGER, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,67,297,10 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,81,297,10 END IDD_KEYFILES DIALOGEX 0, 0, 363, 264 diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c index d73cb551..8b5b0660 100644 --- a/src/Common/Dlgcode.c +++ b/src/Common/Dlgcode.c @@ -5827,6 +5827,10 @@ void handleError (HWND hwndDlg, int code, const char* srcPos) Error ("ERR_NONSYS_INPLACE_ENC_INCOMPLETE", hwndDlg); break; + case ERR_NONSYS_INPLACE_RECOVERY_READONLY_UNSUPPORTED: + Error ("ERR_NONSYS_INPLACE_RECOVERY_READONLY_UNSUPPORTED", hwndDlg); + break; + case ERR_SYS_HIDVOL_HEAD_REENC_MODE_WRONG: Error ("ERR_SYS_HIDVOL_HEAD_REENC_MODE_WRONG", hwndDlg); break; @@ -8987,7 +8991,7 @@ int MountVolume (HWND hwndDlg, BYTE volumeID[VOLUME_ID_SIZE] = {0}; #ifdef TCMOUNT - if (mountOptions->PartitionInInactiveSysEncScope) + if (!mountOptions->NonSysInplaceRecoveryReadOnly && mountOptions->PartitionInInactiveSysEncScope) { if (!CheckSysEncMountWithoutPBA (hwndDlg, volumePath, quiet)) return -1; @@ -9016,8 +9020,9 @@ int MountVolume (HWND hwndDlg, ZeroMemory (&mount, sizeof (mount)); mount.bExclusiveAccess = sharedAccess ? FALSE : TRUE; mount.SystemFavorite = MountVolumesAsSystemFavorite; - mount.UseBackupHeader = mountOptions->UseBackupHeader; + mount.UseBackupHeader = mountOptions->UseBackupHeader || mountOptions->NonSysInplaceRecoveryReadOnly; mount.RecoveryMode = mountOptions->RecoveryMode; + mount.NonSysInplaceRecoveryReadOnly = mountOptions->NonSysInplaceRecoveryReadOnly; StringCbCopyW (mount.wszLabel, sizeof (mount.wszLabel), mountOptions->Label); retry: @@ -9032,7 +9037,7 @@ retry: else mount.VolumePassword.Length = 0; - if (!mountOptions->ReadOnly && mountOptions->ProtectHiddenVolume) + if (!mountOptions->ReadOnly && !mountOptions->NonSysInplaceRecoveryReadOnly && mountOptions->ProtectHiddenVolume) { mount.ProtectedHidVolPassword = mountOptions->ProtectedHidVolPassword; mount.bProtectHiddenVolume = TRUE; @@ -9042,7 +9047,7 @@ retry: else mount.bProtectHiddenVolume = FALSE; - mount.bMountReadOnly = mountOptions->ReadOnly; + mount.bMountReadOnly = mountOptions->ReadOnly || mountOptions->NonSysInplaceRecoveryReadOnly; mount.bMountRemovable = mountOptions->Removable; mount.bPreserveTimestamp = mountOptions->PreserveTimestamp; @@ -9164,7 +9169,14 @@ retry: } } - if (mountOptions->PartitionInInactiveSysEncScope) + if (mountOptions->NonSysInplaceRecoveryReadOnly && !bDevice) + { + if (!quiet) + Error ("ERR_NONSYS_INPLACE_RECOVERY_READONLY_UNSUPPORTED", hwndDlg); + return -1; + } + + if (!mountOptions->NonSysInplaceRecoveryReadOnly && mountOptions->PartitionInInactiveSysEncScope) { if (mount.wszVolume == NULL || swscanf_s ((const wchar_t *) mount.wszVolume, WIDE("\\Device\\Harddisk%d\\Partition"), @@ -9299,7 +9311,8 @@ retry: // Mount successful - if (mount.UseBackupHeader != mountOptions->UseBackupHeader + if (!mountOptions->NonSysInplaceRecoveryReadOnly + && mount.UseBackupHeader != mountOptions->UseBackupHeader && mount.UseBackupHeader) { if (bReportWrongPassword && !Silent) @@ -9314,7 +9327,7 @@ retry: Warning ("ERR_XTS_MASTERKEY_VULNERABLE", hwndDlg); } - if (mount.FilesystemDirty) + if (mount.FilesystemDirty && !mountOptions->NonSysInplaceRecoveryReadOnly) { wchar_t msg[1024]; wchar_t mountPoint[] = { (wchar_t) (L'A' + driveNo), L':', 0 }; diff --git a/src/Common/Language.xml b/src/Common/Language.xml index bd893690..416ffa03 100644 --- a/src/Common/Language.xml +++ b/src/Common/Language.xml @@ -151,6 +151,7 @@ &Auto-Mount Devices Mount Opti&ons... Mount volume as read-&only + Recovery mount interrupted non-system in-place volume as read-only Keyfiles... (Empty or 0 for defaults) (Empty or 0 for defaults) @@ -1238,6 +1239,8 @@ The process of encryption or decryption of the system partition/drive has not been completed. Please wait until it is complete before proceeding. Error: The process of encryption of the partition/drive has not been completed. It must be completed first. Error: The process of encryption or decryption of the partition/volume has not been completed. It must be completed first.\n\nNote: To resume the process, select 'Volumes' > 'Resume Interrupted Process' from the menu bar of the main VeraCrypt window. + Error: Recovery read-only mount is supported only for device-hosted volumes with an incomplete non-system in-place encryption/decryption state and a valid embedded backup header layout.\n\nFully encrypted, fully decrypted, hidden, file-hosted, or unsupported-layout volumes cannot be mounted with this option. + This recovery mode is intended only for copying data from a sector-by-sector clone or image of a partition where non-system in-place encryption/decryption was interrupted.\n\nThe volume will be mounted read-only using the embedded backup header. Do not run CHKDSK, formatting, partition repair, or any write-capable recovery tool on the original disk or on the partially transformed volume.\n\nContinue? The password is correct, VeraCrypt has successfully decrypted the volume header and detected that this volume is a hidden system volume. However, you cannot modify the header of a hidden system volume this way.\n\nTo change the password for a hidden system volume, boot the operating system residing in the hidden volume, and then select 'System' > 'Change Password' from the menu bar of the main VeraCrypt window.\n\nTo set the header key derivation algorithm, boot the hidden operating system and then select 'System' > 'Set Header Key Derivation Algorithm'. VeraCrypt does not support in-place decryption of a hidden system partition.\n\nNote: If you want to decrypt the decoy system partition, boot the decoy system, and then select 'System' > 'Permanently Decrypt System Partition/Drive' from the menu bar of the main VeraCrypt window. Error: Incorrect/invalid parameter. diff --git a/src/Common/Resource.h b/src/Common/Resource.h index 93c7ab08..503e6b32 100644 --- a/src/Common/Resource.h +++ b/src/Common/Resource.h @@ -227,6 +227,7 @@ #define IDC_LINK_KEYFILES_EXTENSIONS_WARNING 5144 #define IDC_DISABLE_MEMORY_PROTECTION 5145 #define IDC_DISABLE_MEMORY_PROTECTION_HELP 5146 +#define IDC_NONSYS_INPLACE_RECOVERY_READONLY 5147 // Next default values for new objects // @@ -235,7 +236,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 585 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 5147 +#define _APS_NEXT_CONTROL_VALUE 5148 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/src/Common/Tcdefs.h b/src/Common/Tcdefs.h index 26449388..53e39a10 100644 --- a/src/Common/Tcdefs.h +++ b/src/Common/Tcdefs.h @@ -457,7 +457,8 @@ enum ERR_CAPI_INIT_FAILED = 35, ERR_XTS_MASTERKEY_VULNERABLE = 36, ERR_SYSENC_XTS_MASTERKEY_VULNERABLE = 37, - ERR_KEY_DERIVATION_FAILED = 38 + ERR_KEY_DERIVATION_FAILED = 38, + ERR_NONSYS_INPLACE_RECOVERY_READONLY_UNSUPPORTED = 39 }; #endif // #ifndef TCDEFS_H diff --git a/src/Common/Volumes.h b/src/Common/Volumes.h index 02e3e7f7..7393ffbd 100644 --- a/src/Common/Volumes.h +++ b/src/Common/Volumes.h @@ -76,6 +76,9 @@ extern "C" { #define TC_VOLUME_DATA_OFFSET TC_VOLUME_HEADER_GROUP_SIZE +#define TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE (2 * TC_MAX_VOLUME_SECTOR_SIZE) +#define TC_NTFS_CONCEAL_CONSTANT 0xFF + // The offset, in bytes, of the legacy hidden volume header position from the end of the file (a positive value). #define TC_HIDDEN_VOLUME_HEADER_OFFSET_LEGACY (TC_VOLUME_HEADER_SIZE_LEGACY + TC_SECTOR_SIZE_LEGACY * 2) diff --git a/src/Driver/EncryptedIoQueue.c b/src/Driver/EncryptedIoQueue.c index 51a9600c..1dbfd6cb 100644 --- a/src/Driver/EncryptedIoQueue.c +++ b/src/Driver/EncryptedIoQueue.c @@ -556,6 +556,27 @@ static VOID CompletionThreadProc(PVOID threadArg) DecryptDataUnits(request->Data + request->EncryptedOffset, &dataUnit, request->EncryptedLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo); } + else if (queue->NonSysInplaceRecoveryMode + && request->EncryptedLength == 0 + && queue->NonSysInplaceConcealedPlaintextLength > 0 + && NT_SUCCESS(request->Item->Status)) + { + uint64 concealIntersectStart; + uint32 concealIntersectLength; + uint32 concealBufferOffset; + uint32 i; + + GetIntersection ((uint64) request->Offset.QuadPart, + request->Length, + 0, + queue->NonSysInplaceConcealedPlaintextLength - 1, + &concealIntersectStart, + &concealIntersectLength); + + concealBufferOffset = (uint32) (concealIntersectStart - (uint64) request->Offset.QuadPart); + for (i = 0; i < concealIntersectLength; ++i) + request->Data[concealBufferOffset + i] ^= TC_NTFS_CONCEAL_CONSTANT; + } // Dump("Read sector %lld count %d\n", request->Offset.QuadPart >> 9, request->Length >> 9); // Update subst sectors if((queue->SecRegionData != NULL) && (queue->SecRegionSize > 512)) { @@ -577,6 +598,17 @@ static VOID CompletionThreadProc(PVOID threadArg) static NTSTATUS TCCachedRead (EncryptedIoQueue *queue, IO_STATUS_BLOCK *ioStatus, PVOID buffer, LARGE_INTEGER offset, ULONG length) { + if (queue->NonSysInplaceRecoveryMode) + { + queue->LastReadLength = 0; + queue->ReadAheadBufferValid = FALSE; + + if (queue->IsFilterDevice) + return TCReadDevice (queue->LowerDeviceObject, buffer, offset, length); + + return ZwReadFile (queue->HostFileHandle, NULL, NULL, NULL, ioStatus, buffer, length, &offset, NULL); + } + queue->LastReadOffset = offset; queue->LastReadLength = length; @@ -741,6 +773,7 @@ static VOID IoThreadProc (PVOID threadArg) request->Data = request->OrigDataBufferFragment; if (request->CompleteOriginalIrp + && !queue->NonSysInplaceRecoveryMode && queue->LastReadLength > 0 && NT_SUCCESS (request->Item->Status) && InterlockedExchangeAdd (&queue->IoThreadPendingRequestCount, 0) == 0) @@ -959,6 +992,12 @@ static VOID MainThreadProc (PVOID threadArg) Dump ("Q %I64d [%I64d] %c len=%d\n", item->OriginalOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), item->Write ? 'W' : 'R', item->OriginalLength); #endif + if (queue->NonSysInplaceRecoveryMode && item->Write) + { + QueueIrpCompletionFromItem(queue, item, STATUS_MEDIA_WRITE_PROTECTED); + continue; + } + if (!queue->IsFilterDevice) { // Adjust the offset for host file or device @@ -1044,6 +1083,80 @@ static VOID MainThreadProc (PVOID threadArg) continue; } + if (queue->NonSysInplaceRecoveryMode) + { + dataRemaining = item->OriginalLength; + fragmentOffset = item->OriginalOffset; + + while (dataRemaining > 0) + { + ULONG queueFragmentSize = queue->FragmentSize; + ULONG dataFragmentLength = dataRemaining <= queueFragmentSize ? dataRemaining : queueFragmentSize; + BOOL encryptedFragment; + BOOL isLastFragment; + LARGE_INTEGER physicalFragmentOffset; + uint64 logicalFragmentOffset = (uint64) fragmentOffset.QuadPart; + + if (logicalFragmentOffset < (uint64) queue->NonSysInplaceLogicalEncryptedStart) + { + uint64 bytesToEncryptedStart = (uint64) queue->NonSysInplaceLogicalEncryptedStart - logicalFragmentOffset; + if ((uint64) dataFragmentLength > bytesToEncryptedStart) + dataFragmentLength = (ULONG) bytesToEncryptedStart; + + encryptedFragment = FALSE; + physicalFragmentOffset = fragmentOffset; + } + else + { + encryptedFragment = TRUE; + hResult = ULongLongAdd (logicalFragmentOffset, TC_VOLUME_DATA_OFFSET, &addResult); + if (hResult != S_OK || addResult > (ULONGLONG) _I64_MAX) + { + QueueIrpCompletionFromItem (queue, item, STATUS_INVALID_PARAMETER); + break; + } + + physicalFragmentOffset.QuadPart = addResult; + } + + isLastFragment = dataRemaining == dataFragmentLength; + activeFragmentBuffer = (activeFragmentBuffer == queue->FragmentBufferA ? queue->FragmentBufferB : queue->FragmentBufferA); + + InterlockedIncrement (&queue->IoThreadPendingRequestCount); + + request = GetPoolBuffer (queue, sizeof (EncryptedIoRequest)); + if (!request) + { + InterlockedDecrement(&queue->IoThreadPendingRequestCount); + QueueIrpCompletionFromItem (queue, item, STATUS_INSUFFICIENT_RESOURCES); + break; + } + + request->Item = item; + request->CompleteOriginalIrp = isLastFragment; + request->Offset = physicalFragmentOffset; + request->Data = activeFragmentBuffer; + request->OrigDataBufferFragment = dataBuffer; + request->Length = dataFragmentLength; + request->EncryptedOffset = 0; + request->EncryptedLength = encryptedFragment ? dataFragmentLength : 0; + + AcquireFragmentBuffer (queue, activeFragmentBuffer); + + ExInterlockedInsertTailList (&queue->IoThreadQueue, &request->ListEntry, &queue->IoThreadQueueLock); + KeSetEvent (&queue->IoThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); + + if (isLastFragment) + break; + + dataRemaining -= dataFragmentLength; + dataBuffer += dataFragmentLength; + fragmentOffset.QuadPart += dataFragmentLength; + } + + continue; + } + // Divide data block to fragments to enable efficient overlapping of encryption and IO operations dataRemaining = item->OriginalLength; diff --git a/src/Driver/EncryptedIoQueue.h b/src/Driver/EncryptedIoQueue.h index d0c9ab75..675fbf66 100644 --- a/src/Driver/EncryptedIoQueue.h +++ b/src/Driver/EncryptedIoQueue.h @@ -62,6 +62,10 @@ typedef struct _EncryptedIoQueueStruct // File-handle-based IO HANDLE HostFileHandle; BOOL bSupportPartialEncryption; + BOOL NonSysInplaceRecoveryMode; + int64 NonSysInplaceLogicalEncryptedStart; + int64 NonSysInplaceLogicalEncryptedEnd; + uint32 NonSysInplaceConcealedPlaintextLength; int64 VirtualDeviceLength; SECURITY_CLIENT_CONTEXT *SecurityClientContext; diff --git a/src/Driver/Ntdriver.c b/src/Driver/Ntdriver.c index ed30903f..0515dda1 100644 --- a/src/Driver/Ntdriver.c +++ b/src/Driver/Ntdriver.c @@ -795,6 +795,112 @@ IOCTL_STORAGE_GET_HOTPLUG_INFO 0x002D0C14 IOCTL_STORAGE_QUERY_PROPERTY 0x002D1400 */ +static NTSTATUS VerifyBackingReadRange (PEXTENSION Extension, PVOID buffer, ULONG bufferSize, ULONGLONG physicalOffset, ULONG length) +{ + IO_STATUS_BLOCK ioStatus; + LARGE_INTEGER offset; + ULONG remainingBytes = length; + + if (physicalOffset > (ULONGLONG) _I64_MAX) + return STATUS_INVALID_PARAMETER; + + offset.QuadPart = (LONGLONG) physicalOffset; + + while (remainingBytes) + { + ULONG readCount = min (bufferSize, remainingBytes); + NTSTATUS status = ZwReadFile (Extension->hDeviceFile, NULL, NULL, NULL, &ioStatus, buffer, readCount, &offset, NULL); + + if (NT_SUCCESS (status) && ioStatus.Information != readCount) + return STATUS_INVALID_PARAMETER; + else if (!NT_SUCCESS (status)) + return status; + + remainingBytes -= readCount; + offset.QuadPart += (ULONGLONG) readCount; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS VerifyVolumeReadRange (PEXTENSION Extension, ULONGLONG logicalOffset, ULONG length) +{ + HRESULT hResult; + ULONGLONG logicalEndOffset; + DWORD bufferSize; + PVOID buffer; + NTSTATUS status = STATUS_SUCCESS; + + if (S_OK != ULongLongAdd (logicalOffset, (ULONGLONG) length, &logicalEndOffset) + || logicalEndOffset > (ULONGLONG) Extension->DiskLength) + { + return STATUS_INVALID_PARAMETER; + } + + if (length == 0) + return STATUS_SUCCESS; + + bufferSize = min (length, 16 * PAGE_SIZE); + buffer = TCalloc (bufferSize); + if (!buffer) + return STATUS_INSUFFICIENT_RESOURCES; + + if (Extension->Queue.NonSysInplaceRecoveryMode) + { + ULONG remainingBytes = length; + ULONGLONG currentLogicalOffset = logicalOffset; + + while (remainingBytes) + { + ULONG fragmentLength = min (bufferSize, remainingBytes); + ULONGLONG physicalOffset; + + if (currentLogicalOffset < (ULONGLONG) Extension->Queue.NonSysInplaceLogicalEncryptedStart) + { + ULONGLONG bytesToEncryptedStart = (ULONGLONG) Extension->Queue.NonSysInplaceLogicalEncryptedStart - currentLogicalOffset; + if ((ULONGLONG) fragmentLength > bytesToEncryptedStart) + fragmentLength = (ULONG) bytesToEncryptedStart; + + physicalOffset = currentLogicalOffset; + } + else + { + hResult = ULongLongAdd (currentLogicalOffset, (ULONGLONG) TC_VOLUME_DATA_OFFSET, &physicalOffset); + if (hResult != S_OK) + { + status = STATUS_INVALID_PARAMETER; + break; + } + } + + status = VerifyBackingReadRange (Extension, buffer, bufferSize, physicalOffset, fragmentLength); + if (!NT_SUCCESS (status)) + break; + + currentLogicalOffset += fragmentLength; + remainingBytes -= fragmentLength; + } + } + else + { + ULONGLONG physicalOffset; + ULONGLONG volumeOffset = Extension->cryptoInfo->hiddenVolume + ? Extension->cryptoInfo->hiddenVolumeOffset + : Extension->cryptoInfo->volDataAreaOffset; + + hResult = ULongLongAdd (logicalOffset, volumeOffset, &physicalOffset); + if (hResult != S_OK) + status = STATUS_INVALID_PARAMETER; + else + status = VerifyBackingReadRange (Extension, buffer, bufferSize, physicalOffset, length); + } + + burn (buffer, bufferSize); + TCfree (buffer); + + return status; +} + NTSTATUS ProcessVolumeDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, PIRP Irp) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); @@ -1339,65 +1445,16 @@ NTSTATUS ProcessVolumeDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION case IOCTL_DISK_VERIFY: Dump ("ProcessVolumeDeviceControlIrp (IOCTL_DISK_VERIFY)\n"); + Irp->IoStatus.Information = 0; if (ValidateIOBufferSize (Irp, sizeof (VERIFY_INFORMATION), ValidateInput)) { - HRESULT hResult; - ULONGLONG ullStartingOffset, ullNewOffset, ullEndOffset; PVERIFY_INFORMATION pVerifyInformation; - ULONGLONG volumeOffset = Extension->cryptoInfo->hiddenVolume - ? Extension->cryptoInfo->hiddenVolumeOffset - : Extension->cryptoInfo->volDataAreaOffset; pVerifyInformation = (PVERIFY_INFORMATION) Irp->AssociatedIrp.SystemBuffer; - ullStartingOffset = (ULONGLONG) pVerifyInformation->StartingOffset.QuadPart; - hResult = ULongLongAdd(ullStartingOffset, - volumeOffset, - &ullNewOffset); - if (hResult != S_OK) - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - else if (S_OK != ULongLongAdd(ullStartingOffset, (ULONGLONG) pVerifyInformation->Length, &ullEndOffset)) - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - else if (ullEndOffset > (ULONGLONG) Extension->DiskLength) + if (pVerifyInformation->StartingOffset.QuadPart < 0) Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; else - { - IO_STATUS_BLOCK ioStatus; - DWORD dwBuffersize = min (pVerifyInformation->Length, 16 * PAGE_SIZE); - PVOID buffer = TCalloc (dwBuffersize); - - if (!buffer) - { - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - } - else - { - LARGE_INTEGER offset; - DWORD dwRemainingBytes = pVerifyInformation->Length, dwReadCount; - offset.QuadPart = ullNewOffset; - - while (dwRemainingBytes) - { - dwReadCount = min (dwBuffersize, dwRemainingBytes); - Irp->IoStatus.Status = ZwReadFile (Extension->hDeviceFile, NULL, NULL, NULL, &ioStatus, buffer, dwReadCount, &offset, NULL); - - if (NT_SUCCESS (Irp->IoStatus.Status) && ioStatus.Information != dwReadCount) - { - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - break; - } - else if (!NT_SUCCESS (Irp->IoStatus.Status)) - break; - - dwRemainingBytes -= dwReadCount; - offset.QuadPart += (ULONGLONG) dwReadCount; - } - - burn (buffer, dwBuffersize); - TCfree (buffer); - } - } - - Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = VerifyVolumeReadRange (Extension, (ULONGLONG) pVerifyInformation->StartingOffset.QuadPart, pVerifyInformation->Length); } break; @@ -2614,6 +2671,14 @@ NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Ex EnsureNullTerminatedString (mount->wszVolume, sizeof (mount->wszVolume)); EnsureNullTerminatedString (mount->wszLabel, sizeof (mount->wszLabel)); + if (mount->NonSysInplaceRecoveryReadOnly) + { + mount->UseBackupHeader = TRUE; + mount->bMountReadOnly = TRUE; + mount->bProtectHiddenVolume = FALSE; + mount->bPartitionInInactiveSysEncScope = FALSE; + } + Irp->IoStatus.Information = sizeof (MOUNT_STRUCT); Irp->IoStatus.Status = MountDevice (DeviceObject, mount); @@ -3094,7 +3159,33 @@ VOID VolumeThreadProc (PVOID Context) Extension->Queue.HostFileHandle = Extension->hDeviceFile; Extension->Queue.VirtualDeviceLength = Extension->DiskLength; Extension->Queue.MaxReadAheadOffset.QuadPart = Extension->HostLength; - if (bDevice && pThreadBlock->mount->bPartitionInInactiveSysEncScope + if (bDevice && pThreadBlock->mount->NonSysInplaceRecoveryReadOnly) + { + int64 logicalEncryptedStart; + int64 logicalEncryptedEnd; + uint32 concealedPlaintextLength; + + if (!GetNonSysInplaceRecoveryGeometry (Extension->cryptoInfo, &logicalEncryptedStart, &logicalEncryptedEnd, &concealedPlaintextLength)) + { + TCCloseVolume (DeviceObject, Extension); + pThreadBlock->mount->nReturnCode = ERR_NONSYS_INPLACE_RECOVERY_READONLY_UNSUPPORTED; + pThreadBlock->ntCreateStatus = STATUS_SUCCESS; + KeSetEvent (&Extension->keCreateEvent, 0, FALSE); + PsTerminateSystemThread (STATUS_SUCCESS); + return; /* Make static analyzer happy */ + } + + Extension->Queue.NonSysInplaceRecoveryMode = TRUE; + Extension->Queue.NonSysInplaceLogicalEncryptedStart = logicalEncryptedStart; + Extension->Queue.NonSysInplaceLogicalEncryptedEnd = logicalEncryptedEnd; + Extension->Queue.NonSysInplaceConcealedPlaintextLength = concealedPlaintextLength; + Extension->Queue.EncryptedAreaStart = -1; + Extension->Queue.EncryptedAreaEnd = -1; + + Dump ("Non-system in-place recovery mount: logical encrypted area %I64d-%I64d, concealed plaintext bytes %u\n", + logicalEncryptedStart, logicalEncryptedEnd, concealedPlaintextLength); + } + else if (bDevice && pThreadBlock->mount->bPartitionInInactiveSysEncScope && (!Extension->cryptoInfo->hiddenVolume) && (Extension->cryptoInfo->EncryptedAreaLength.Value != Extension->cryptoInfo->VolumeSize.Value) ) diff --git a/src/Driver/Ntvol.c b/src/Driver/Ntvol.c index 470d50c8..40b3163a 100644 --- a/src/Driver/Ntvol.c +++ b/src/Driver/Ntvol.c @@ -36,6 +36,62 @@ volatile BOOL ProbingHostDeviceForWrite = FALSE; +BOOL GetNonSysInplaceRecoveryGeometry (PCRYPTO_INFO cryptoInfo, int64 *logicalEncryptedStart, int64 *logicalEncryptedEnd, uint32 *concealedPlaintextLength) +{ + uint64 volumeSize; + uint64 encryptedAreaStart; + uint64 encryptedAreaLength; + uint64 encryptedAreaEndExclusive; + uint64 expectedAreaEndExclusive; + uint64 logicalStart; + + if (!cryptoInfo) + return FALSE; + + volumeSize = cryptoInfo->VolumeSize.Value; + encryptedAreaStart = cryptoInfo->EncryptedAreaStart.Value; + encryptedAreaLength = cryptoInfo->EncryptedAreaLength.Value; + + if (cryptoInfo->hiddenVolume + || (cryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) == 0 + || (cryptoInfo->HeaderFlags & TC_HEADER_FLAG_ENCRYPTED_SYSTEM) != 0 + || volumeSize == 0 + || encryptedAreaLength == 0 + || encryptedAreaLength >= volumeSize + || encryptedAreaStart < TC_VOLUME_DATA_OFFSET + || volumeSize > (uint64) _I64_MAX + || encryptedAreaStart > (uint64) _I64_MAX + || encryptedAreaLength > (uint64) _I64_MAX + || encryptedAreaStart > ((uint64) _I64_MAX - encryptedAreaLength) + || volumeSize > ((uint64) _I64_MAX - TC_VOLUME_DATA_OFFSET)) + { + return FALSE; + } + + encryptedAreaEndExclusive = encryptedAreaStart + encryptedAreaLength; + expectedAreaEndExclusive = TC_VOLUME_DATA_OFFSET + volumeSize; + + if (encryptedAreaEndExclusive != expectedAreaEndExclusive) + return FALSE; + + logicalStart = encryptedAreaStart - TC_VOLUME_DATA_OFFSET; + + if (logicalStart >= volumeSize + || (logicalStart % ENCRYPTION_DATA_UNIT_SIZE) != 0) + { + return FALSE; + } + + if (logicalEncryptedStart) + *logicalEncryptedStart = (int64) logicalStart; + if (logicalEncryptedEnd) + *logicalEncryptedEnd = (int64) (logicalStart + encryptedAreaLength - 1); + if (concealedPlaintextLength) + *concealedPlaintextLength = logicalStart < TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE ? (uint32) logicalStart : TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE; + + return TRUE; +} + NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, @@ -58,6 +114,7 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, BOOL forceAccessCheck = !bRawDevice; BOOL disableBuffering = TRUE; BOOL exclusiveAccess = mount->bExclusiveAccess; + int volumeTypeCount; /* when mounting with hidden volume protection, we cache the passwords after both outer and hidden volumes are mounted successfully*/ BOOL bAutoCachePassword = mount->bProtectHiddenVolume? FALSE : mount->bCache; @@ -91,6 +148,21 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, mount->VolumeMountedReadOnlyAfterPartialSysEnc = FALSE; mount->VolumeMasterKeyVulnerable = FALSE; + if (mount->NonSysInplaceRecoveryReadOnly) + { + mount->UseBackupHeader = TRUE; + mount->bMountReadOnly = TRUE; + mount->bProtectHiddenVolume = FALSE; + bAutoCachePassword = mount->bCache; + + if (!bRawDevice || mount->bPartitionInInactiveSysEncScope) + { + mount->nReturnCode = ERR_NONSYS_INPLACE_RECOVERY_READONLY_UNSUPPORTED; + ntStatus = STATUS_SUCCESS; + goto error; + } + } + // If we are opening a device, query its size first if (bRawDevice) { @@ -467,8 +539,10 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, } // Go through all volume types (e.g., normal, hidden) + volumeTypeCount = mount->NonSysInplaceRecoveryReadOnly ? TC_VOLUME_TYPE_NORMAL + 1 : TC_VOLUME_TYPE_COUNT; + for (volumeType = TC_VOLUME_TYPE_NORMAL; - volumeType < TC_VOLUME_TYPE_COUNT; + volumeType < volumeTypeCount; volumeType++) { Dump ("Trying to open volume type %d\n", volumeType); @@ -660,7 +734,19 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, if (volumeType == TC_VOLUME_TYPE_NORMAL) { - if (mount->bPartitionInInactiveSysEncScope) + if (mount->NonSysInplaceRecoveryReadOnly) + { + if (!GetNonSysInplaceRecoveryGeometry (Extension->cryptoInfo, NULL, NULL, NULL)) + { + mount->nReturnCode = ERR_NONSYS_INPLACE_RECOVERY_READONLY_UNSUPPORTED; + ntStatus = STATUS_SUCCESS; + goto error; + } + + Extension->bReadOnly = mount->bMountReadOnly = TRUE; + Extension->TrimEnabled = FALSE; + } + else if (mount->bPartitionInInactiveSysEncScope) { if (Extension->cryptoInfo->EncryptedAreaStart.Value > (unsigned __int64) partitionStartingOffset || Extension->cryptoInfo->EncryptedAreaStart.Value + Extension->cryptoInfo->VolumeSize.Value <= (unsigned __int64) partitionStartingOffset) @@ -709,7 +795,12 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, Extension->cryptoInfo->hiddenVolume = FALSE; - if (mount->bPartitionInInactiveSysEncScope) + if (mount->NonSysInplaceRecoveryReadOnly) + { + Extension->cryptoInfo->volDataAreaOffset = 0; + Extension->DiskLength = Extension->cryptoInfo->VolumeSize.Value; + } + else if (mount->bPartitionInInactiveSysEncScope) { Extension->cryptoInfo->volDataAreaOffset = 0; Extension->DiskLength = lDiskLength.QuadPart; diff --git a/src/Driver/Ntvol.h b/src/Driver/Ntvol.h index 0672c58c..b5964273 100644 --- a/src/Driver/Ntvol.h +++ b/src/Driver/Ntvol.h @@ -15,6 +15,7 @@ extern volatile BOOL ProbingHostDeviceForWrite; NTSTATUS TCOpenVolume ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension , MOUNT_STRUCT *mount , PWSTR pwszMountVolume , BOOL bRawDevice ); void TCCloseVolume ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension ); +BOOL GetNonSysInplaceRecoveryGeometry ( PCRYPTO_INFO cryptoInfo , int64 *logicalEncryptedStart , int64 *logicalEncryptedEnd , uint32 *concealedPlaintextLength ); NTSTATUS TCCompletion ( PDEVICE_OBJECT DeviceObject , PIRP Irp , PVOID pUserBuffer ); NTSTATUS TCSendHostDeviceIoControlRequest ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension , ULONG IoControlCode , void *OutputBuffer , ULONG OutputBufferSize ); NTSTATUS TCSendHostDeviceIoControlRequestEx ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension , ULONG IoControlCode , void *InputBuffer , ULONG InputBufferSize , void *OutputBuffer , ULONG OutputBufferSize ); diff --git a/src/Format/InPlace.c b/src/Format/InPlace.c index 2e75cbfd..34590cb8 100644 --- a/src/Format/InPlace.c +++ b/src/Format/InPlace.c @@ -57,8 +57,6 @@ using namespace VeraCrypt; #endif #define TC_MAX_NONSYS_INPLACE_ENC_WORK_CHUNK_SIZE (2048 * BYTES_PER_KB) -#define TC_INITIAL_NTFS_CONCEAL_PORTION_SIZE (2 * TC_MAX_VOLUME_SECTOR_SIZE) -#define TC_NTFS_CONCEAL_CONSTANT 0xFF #define TC_NONSYS_INPLACE_ENC_HEADER_UPDATE_INTERVAL (64 * BYTES_PER_MB) #define TC_NONSYS_INPLACE_ENC_MIN_VOL_SIZE (TC_TOTAL_VOLUME_HEADERS_SIZE + TC_MIN_NTFS_FS_SIZE * 2) diff --git a/src/Mount/Mount.c b/src/Mount/Mount.c index 964314b5..14c6c177 100644 --- a/src/Mount/Mount.c +++ b/src/Mount/Mount.c @@ -1089,6 +1089,7 @@ void LoadSettingsAndCheckModified (HWND hwndDlg, BOOL bOnlyCheckModified, BOOL* defaultMountOptions.ProtectedHidVolPim = 0; defaultMountOptions.PartitionInInactiveSysEncScope = FALSE; defaultMountOptions.RecoveryMode = FALSE; + defaultMountOptions.NonSysInplaceRecoveryReadOnly = FALSE; defaultMountOptions.UseBackupHeader = FALSE; defaultMountOptions.SkipCachedPasswords = FALSE; @@ -3923,8 +3924,10 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM case WM_INITDIALOG: { BOOL protect; + BOOL nonSysInplaceRecovery; pMountOptions = (MountOptions *) lParam; + nonSysInplaceRecovery = !bPrebootPasswordDlgMode && pMountOptions->NonSysInplaceRecoveryReadOnly; LocalizeDialog (hwndDlg, "IDD_MOUNT_OPTIONS"); @@ -3935,6 +3938,9 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM SendDlgItemMessage (hwndDlg, IDC_DISABLE_MOUNT_MANAGER, BM_SETCHECK, pMountOptions->DisableMountManager ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage (hwndDlg, IDC_NONSYS_INPLACE_RECOVERY_READONLY, BM_SETCHECK, + nonSysInplaceRecovery ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage (hwndDlg, IDC_PROTECT_HIDDEN_VOL, BM_SETCHECK, pMountOptions->ProtectHiddenVolume ? BST_CHECKED : BST_UNCHECKED, 0); @@ -3946,7 +3952,18 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM SendDlgItemMessage (hwndDlg, IDC_USE_EMBEDDED_HEADER_BAK, BM_SETCHECK, pMountOptions->UseBackupHeader ? BST_CHECKED : BST_UNCHECKED, 0); - EnableWindow (GetDlgItem (hwndDlg, IDC_MOUNT_SYSENC_PART_WITHOUT_PBA), !bPrebootPasswordDlgMode); + if (nonSysInplaceRecovery) + { + SendDlgItemMessage (hwndDlg, IDC_MOUNT_READONLY, BM_SETCHECK, BST_CHECKED, 0); + SendDlgItemMessage (hwndDlg, IDC_USE_EMBEDDED_HEADER_BAK, BM_SETCHECK, BST_CHECKED, 0); + SendDlgItemMessage (hwndDlg, IDC_PROTECT_HIDDEN_VOL, BM_SETCHECK, BST_UNCHECKED, 0); + SendDlgItemMessage (hwndDlg, IDC_MOUNT_SYSENC_PART_WITHOUT_PBA, BM_SETCHECK, BST_UNCHECKED, 0); + } + + EnableWindow (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY), !nonSysInplaceRecovery); + EnableWindow (GetDlgItem (hwndDlg, IDC_USE_EMBEDDED_HEADER_BAK), !nonSysInplaceRecovery); + EnableWindow (GetDlgItem (hwndDlg, IDC_MOUNT_SYSENC_PART_WITHOUT_PBA), !bPrebootPasswordDlgMode && !nonSysInplaceRecovery); + EnableWindow (GetDlgItem (hwndDlg, IDC_NONSYS_INPLACE_RECOVERY_READONLY), !bPrebootPasswordDlgMode); SetDlgItemTextW (hwndDlg, IDC_VOLUME_LABEL, pMountOptions->Label); SendDlgItemMessage (hwndDlg, IDC_VOLUME_LABEL, EM_LIMITTEXT, 32, 0); // 32 is the maximum possible length for a drive label in Windows @@ -3975,8 +3992,8 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM protect = IsButtonChecked (GetDlgItem (hwndDlg, IDC_PROTECT_HIDDEN_VOL)); - EnableWindow (GetDlgItem (hwndDlg, IDC_PROTECT_HIDDEN_VOL), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY))); - EnableWindow (GetDlgItem (hwndDlg, IDT_HIDDEN_VOL_PROTECTION), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY))); + EnableWindow (GetDlgItem (hwndDlg, IDC_PROTECT_HIDDEN_VOL), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY)) && !nonSysInplaceRecovery); + EnableWindow (GetDlgItem (hwndDlg, IDT_HIDDEN_VOL_PROTECTION), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY)) && !nonSysInplaceRecovery); EnableWindow (GetDlgItem (hwndDlg, IDC_PASSWORD_PROT_HIDVOL), protect); EnableWindow (GetDlgItem (hwndDlg, IDC_SHOW_PASSWORD_MO), protect); EnableWindow (GetDlgItem (hwndDlg, IDT_HIDDEN_PROT_PASSWD), protect); @@ -4113,6 +4130,10 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM if (lw == IDOK) { wchar_t tmp[MAX_PASSWORD+1]; + BOOL nonSysInplaceRecovery = IsButtonChecked (GetDlgItem (hwndDlg, IDC_NONSYS_INPLACE_RECOVERY_READONLY)); + + if (nonSysInplaceRecovery && AskWarnYesNo ("NONSYS_INPLACE_RECOVERY_READONLY_WARNING", hwndDlg) != IDYES) + return 1; pMountOptions->ReadOnly = IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY)); pMountOptions->Removable = IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_REMOVABLE)); @@ -4120,6 +4141,15 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM pMountOptions->ProtectHiddenVolume = IsButtonChecked (GetDlgItem (hwndDlg, IDC_PROTECT_HIDDEN_VOL)); pMountOptions->PartitionInInactiveSysEncScope = IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_SYSENC_PART_WITHOUT_PBA)); pMountOptions->UseBackupHeader = IsButtonChecked (GetDlgItem (hwndDlg, IDC_USE_EMBEDDED_HEADER_BAK)); + pMountOptions->NonSysInplaceRecoveryReadOnly = nonSysInplaceRecovery; + + if (pMountOptions->NonSysInplaceRecoveryReadOnly) + { + pMountOptions->ReadOnly = TRUE; + pMountOptions->UseBackupHeader = TRUE; + pMountOptions->ProtectHiddenVolume = FALSE; + pMountOptions->PartitionInInactiveSysEncScope = FALSE; + } GetDlgItemTextW (hwndDlg, IDC_VOLUME_LABEL, pMountOptions->Label, sizeof (pMountOptions->Label) /sizeof (wchar_t)); @@ -4154,15 +4184,31 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM return 1; } - if (lw == IDC_MOUNT_READONLY || lw == IDC_PROTECT_HIDDEN_VOL || lw == IDC_DISABLE_MOUNT_MANAGER) + if (lw == IDC_MOUNT_READONLY || lw == IDC_PROTECT_HIDDEN_VOL || lw == IDC_DISABLE_MOUNT_MANAGER || lw == IDC_NONSYS_INPLACE_RECOVERY_READONLY) { BOOL protect; + BOOL nonSysInplaceRecovery = IsButtonChecked (GetDlgItem (hwndDlg, IDC_NONSYS_INPLACE_RECOVERY_READONLY)); + + if (lw == IDC_NONSYS_INPLACE_RECOVERY_READONLY) + { + if (nonSysInplaceRecovery) + { + SendDlgItemMessage (hwndDlg, IDC_MOUNT_READONLY, BM_SETCHECK, BST_CHECKED, 0); + SendDlgItemMessage (hwndDlg, IDC_USE_EMBEDDED_HEADER_BAK, BM_SETCHECK, BST_CHECKED, 0); + SendDlgItemMessage (hwndDlg, IDC_PROTECT_HIDDEN_VOL, BM_SETCHECK, BST_UNCHECKED, 0); + SendDlgItemMessage (hwndDlg, IDC_MOUNT_SYSENC_PART_WITHOUT_PBA, BM_SETCHECK, BST_UNCHECKED, 0); + } + + EnableWindow (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY), !nonSysInplaceRecovery); + EnableWindow (GetDlgItem (hwndDlg, IDC_USE_EMBEDDED_HEADER_BAK), !nonSysInplaceRecovery); + EnableWindow (GetDlgItem (hwndDlg, IDC_MOUNT_SYSENC_PART_WITHOUT_PBA), !bPrebootPasswordDlgMode && !nonSysInplaceRecovery); + } if (lw == IDC_MOUNT_READONLY) { SendDlgItemMessage (hwndDlg, IDC_PROTECT_HIDDEN_VOL, BM_SETCHECK, BST_UNCHECKED, 0); - EnableWindow (GetDlgItem (hwndDlg, IDC_PROTECT_HIDDEN_VOL), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY))); - EnableWindow (GetDlgItem (hwndDlg, IDT_HIDDEN_VOL_PROTECTION), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY))); + EnableWindow (GetDlgItem (hwndDlg, IDC_PROTECT_HIDDEN_VOL), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY)) && !nonSysInplaceRecovery); + EnableWindow (GetDlgItem (hwndDlg, IDT_HIDDEN_VOL_PROTECTION), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY)) && !nonSysInplaceRecovery); } if (lw == IDC_DISABLE_MOUNT_MANAGER) @@ -4171,7 +4217,10 @@ BOOL CALLBACK MountOptionsDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM EnableWindow (GetDlgItem (hwndDlg, IDT_VOLUME_LABEL), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_DISABLE_MOUNT_MANAGER))); } - protect = IsButtonChecked (GetDlgItem (hwndDlg, IDC_PROTECT_HIDDEN_VOL)); + EnableWindow (GetDlgItem (hwndDlg, IDC_PROTECT_HIDDEN_VOL), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY)) && !nonSysInplaceRecovery); + EnableWindow (GetDlgItem (hwndDlg, IDT_HIDDEN_VOL_PROTECTION), !IsButtonChecked (GetDlgItem (hwndDlg, IDC_MOUNT_READONLY)) && !nonSysInplaceRecovery); + + protect = IsButtonChecked (GetDlgItem (hwndDlg, IDC_PROTECT_HIDDEN_VOL)) && !nonSysInplaceRecovery; EnableWindow (GetDlgItem (hwndDlg, IDC_PASSWORD_PROT_HIDVOL), protect); EnableWindow (GetDlgItem (hwndDlg, IDT_HIDDEN_PROT_PASSWD), protect); @@ -9893,6 +9942,14 @@ void ExtractCommandLine (HWND hwndDlg, wchar_t *lpszCommandLine) else if (!_wcsicmp (szTmp, L"recovery")) mountOptions.RecoveryMode = TRUE; + else if (!_wcsicmp (szTmp, L"inplacerecovery") || !_wcsicmp (szTmp, L"nonsysinplacerecovery") || !_wcsicmp (szTmp, L"recoveryro")) + { + mountOptions.NonSysInplaceRecoveryReadOnly = TRUE; + mountOptions.ReadOnly = TRUE; + mountOptions.UseBackupHeader = TRUE; + mountOptions.ProtectHiddenVolume = FALSE; + mountOptions.PartitionInInactiveSysEncScope = FALSE; + } else if ((wcslen(szTmp) > 6) && (wcslen(szTmp) <= 38) && !_wcsnicmp (szTmp, L"label=", 6)) { // get the label