[ui] add a new selection dialog for Windows 11 setup customization

* This moves the extended Windows 11 options (bypass TPM & Secure Boot) away from
  "Image options" into a new explicit dialog, along with supplementary customization
  such as enabling offline account (for Windows 11 22H2) and skipping all data
  collection questions.
* This customization is now enacted through an unattend.xml file rather than offline
  registry manipulation, so that this *should* also work with the Windows Store version.
* Also update arch detection and rework/reorganize upcoming translation changes.
* Note: The 'Remove "unsupported hardware" desktop watermark' option is *UNTESTED*.
This commit is contained in:
Pete Batard 2022-06-23 13:34:24 +01:00
parent 9690742d91
commit a0d669232c
No known key found for this signature in database
GPG key ID: 38E0CF5E69EDD671
11 changed files with 317 additions and 179 deletions

View file

@ -60,11 +60,21 @@ enum bootcheck_return {
BOOTCHECK_GENERAL_ERROR = -3,
};
#define UNATTEND_SECUREBOOT_TPM_MASK 0x01
#define UNATTEND_MINRAM_MINDISK_MASK 0x02
#define UNATTEND_NO_ONLINE_ACCOUNT_MASK 0x04
#define UNATTEND_REMOVE_WATERMARK_MASK 0x08
#define UNATTEND_NO_DATA_COLLECTION_MASK 0x10
#define UNATTEND_WINPE_SETUP_MASK (UNATTEND_SECUREBOOT_TPM_MASK | UNATTEND_MINRAM_MINDISK_MASK)
#define UNATTEND_SPECIALIZE_DEPLOYMENT_MASK (UNATTEND_NO_ONLINE_ACCOUNT_MASK | UNATTEND_REMOVE_WATERMARK_MASK)
#define UNATTEND_OOBE_SHELL_SETUP (UNATTEND_REMOVE_WATERMARK_MASK | UNATTEND_NO_DATA_COLLECTION_MASK)
static const char* cmdline_hogger = "rufus.com";
static const char* ep_reg = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer";
static const char* vs_reg = "Software\\Microsoft\\VisualStudio";
static const char* arch_name[MAX_ARCHS] = {
"x86_32", "Itanic", "x86_64", "ARM", "ARM64", "EBC","Risc-V 32", "Risc-V 64", "Risc-V 128" };
static const char* arch_name[ARCH_MAX] = {
"unknown", "x86_32", "x86_64", "ARM", "ARM64", "Itanic", "RISC-V 32", "RISC-V 64", "RISC-V 128", "EBC" };
static BOOL existing_key = FALSE; // For LGP set/restore
static BOOL size_check = TRUE;
static BOOL log_displayed = FALSE;
@ -79,6 +89,8 @@ static BOOL allowed_filesystem[FS_MAX] = { 0 };
static int64_t last_iso_blocking_status;
static int selected_pt = -1, selected_fs = FS_UNKNOWN, preselected_fs = FS_UNKNOWN;
static int image_index = 0, select_index = 0;
static int unattend_xml_mask = (UNATTEND_SECUREBOOT_TPM_MASK | UNATTEND_NO_ONLINE_ACCOUNT_MASK |
UNATTEND_REMOVE_WATERMARK_MASK);
static RECT relaunch_rc = { -65536, -65536, 0, 0};
static UINT uMBRChecked = BST_UNCHECKED;
static HANDLE format_thread = NULL;
@ -116,7 +128,7 @@ HWND hDeviceList, hPartitionScheme, hTargetSystem, hFileSystem, hClusterSize, hL
HWND hImageOption, hLogDialog = NULL, hProgress = NULL, hDiskID;
HANDLE dialog_handle = NULL;
BOOL is_x86_32, use_own_c32[NB_OLD_C32] = { FALSE, FALSE }, mbr_selected_by_user = FALSE;
BOOL op_in_progress = TRUE, right_to_left_mode = FALSE, has_uefi_csm = FALSE, its_a_me_mario = FALSE;
BOOL op_in_progress = TRUE, right_to_left_mode = FALSE, has_uefi_csm = FALSE, its_a_me_mario = FALSE, enable_inplace;
BOOL enable_HDDs = FALSE, enable_VHDs = TRUE, enable_ntfs_compression = FALSE, no_confirmation_on_cancel = FALSE, lock_drive = TRUE;
BOOL advanced_mode_device, advanced_mode_format, allow_dual_uefi_bios, detect_fakes, enable_vmdk, force_large_fat32, usb_debug;
BOOL use_fake_units, preserve_timestamps = FALSE, fast_zeroing = FALSE, app_changed_size = FALSE;
@ -125,7 +137,7 @@ BOOL write_as_image = FALSE, write_as_esp = FALSE, use_vds = FALSE, ignore_boot_
BOOL appstore_version = FALSE, is_vds_available = TRUE;
float fScale = 1.0f;
int dialog_showing = 0, selection_default = BT_IMAGE, persistence_unit_selection = -1, imop_win_sel = 0;
int default_fs, fs_type, boot_type, partition_type, target_type; // file system, boot type, partition type, target type
int default_fs, fs_type, boot_type, partition_type, target_type;
int force_update = 0, default_thread_priority = THREAD_PRIORITY_ABOVE_NORMAL;
char szFolderPath[MAX_PATH], app_dir[MAX_PATH], system_dir[MAX_PATH], temp_dir[MAX_PATH], sysnative_dir[MAX_PATH];
char app_data_dir[MAX_PATH], user_dir[MAX_PATH];
@ -133,7 +145,7 @@ char embedded_sl_version_str[2][12] = { "?.??", "?.??" };
char embedded_sl_version_ext[2][32];
char ClusterSizeLabel[MAX_CLUSTER_SIZES][64];
char msgbox[1024], msgbox_title[32], *ini_file = NULL, *image_path = NULL, *short_image_path;
char *archive_path = NULL, image_option_txt[128], *fido_url = NULL;
char *archive_path = NULL, image_option_txt[128], *fido_url = NULL, *unattend_xml_path = NULL;
StrArray BlockingProcess, ImageList;
// Number of steps for each FS for FCC_STRUCTURE_PROGRESS
const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10, 1, 1, 1, 1 };
@ -1168,22 +1180,15 @@ static void UpdateImage(BOOL update_image_option_only)
ComboBox_ResetContent(hImageOption);
if (!img_report.is_windows_img) { // Straight install.wim/install.esd only have Windows To Go option
// Can't remove restrictions if running on Windows 7 or when running the appstore version
if (nWindowsVersion >= WINDOWS_8 && !appstore_version && img_report.win_version.major == 11) {
IGNORE_RETVAL(ComboBox_SetItemData(hImageOption, ComboBox_AddStringU(hImageOption, lmprintf(MSG_322)), IMOP_WIN_STANDARD));
IGNORE_RETVAL(ComboBox_SetItemData(hImageOption, ComboBox_AddStringU(hImageOption, lmprintf(MSG_323)), IMOP_WIN_EXTENDED));
} else {
IGNORE_RETVAL(ComboBox_SetItemData(hImageOption, ComboBox_AddStringU(hImageOption, lmprintf(MSG_117)), IMOP_WIN_STANDARD));
}
}
if (!img_report.is_windows_img) // Straight install.wim/install.esd only have Windows To Go option
IGNORE_RETVAL(ComboBox_SetItemData(hImageOption, ComboBox_AddStringU(hImageOption, lmprintf(MSG_117)), IMOP_WIN_STANDARD));
IGNORE_RETVAL(ComboBox_SetItemData(hImageOption, ComboBox_AddStringU(hImageOption, lmprintf(MSG_118)), IMOP_WIN_TO_GO));
IGNORE_RETVAL(ComboBox_SetCurSel(hImageOption, imop_win_sel));
}
static uint8_t FindArch(const char* filename)
{
uint8_t ret = 0;
uint8_t ret = ARCH_UNKNOWN;
HANDLE hFile = NULL, hFileMapping = NULL;
PIMAGE_DOS_HEADER pImageDOSHeader = NULL;
// NB: The field we are after is at the same location for 32 and 64-bit
@ -1219,31 +1224,31 @@ static uint8_t FindArch(const char* filename)
switch (pImageNTHeader->FileHeader.Machine) {
case IMAGE_FILE_MACHINE_I386:
ret = 1;
break;
case IMAGE_FILE_MACHINE_IA64:
ret = 2;
ret = ARCH_X86_32;
break;
case IMAGE_FILE_MACHINE_AMD64:
ret = 3;
ret = ARCH_X86_64;
break;
case IMAGE_FILE_MACHINE_ARM:
ret = 4;
ret = ARCH_ARM_32;
break;
case IMAGE_FILE_MACHINE_ARM64:
ret = 5;
ret = ARCH_ARM_64;
break;
case IMAGE_FILE_MACHINE_EBC:
ret = 6;
case IMAGE_FILE_MACHINE_IA64:
ret = ARCH_IA_64;
break;
case IMAGE_FILE_MACHINE_RISCV32:
ret = 7;
ret = ARCH_RISCV_32;
break;
case IMAGE_FILE_MACHINE_RISCV64:
ret = 8;
ret = ARCH_RISCV_64;
break;
case IMAGE_FILE_MACHINE_RISCV128:
ret = 9;
ret = ARCH_RISCV_128;
break;
case IMAGE_FILE_MACHINE_EBC:
ret = ARCH_EBC;
break;
}
@ -1252,10 +1257,123 @@ out:
UnmapViewOfFile(pImageDOSHeader);
safe_closehandle(hFileMapping);
safe_closehandle(hFile);
assert(ret <= MAX_ARCHS);
assert(ret < ARCH_MAX);
return ret;
}
static char* CreateUnattendXml(int arch, int mask)
{
static char path[MAX_PATH];
FILE* fd;
int i, order;
// I don't believe there's a version of Windows 11 for ARM32 but whatever...
const char* xml_arch_names[5] = { "x86", "amd64", "arm", "arm64" };
const char* bypass_name[4] = { "BypassTPMCheck", "BypassSecureBootCheck", "BypassRAMCheck", "BypassStorageCheck" };
if (arch < ARCH_X86_32 || arch >= ARCH_ARM_64 || mask == 0)
return NULL;
arch--;
if (GetTempFileNameU(temp_dir, APPLICATION_NAME, 0, path) == 0)
return NULL;
fd = fopen(path, "w");
if (fd == NULL)
return NULL;
enable_inplace = mask & UNATTEND_WINPE_SETUP_MASK;
fprintf(fd, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
fprintf(fd, "<unattend xmlns=\"urn:schemas-microsoft-com:unattend\">\n");
// This part produces the unbecoming display of a command prompt window during initial setup, which
// may scare users... But the Windows Store version doesn't allow us to edit an offline registry...
if (mask & UNATTEND_WINPE_SETUP_MASK) {
order = 1;
fprintf(fd, " <settings pass=\"windowsPE\">\n");
fprintf(fd, " <component name=\"Microsoft-Windows-Setup\" processorArchitecture=\"%s\" language=\"neutral\" "
"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]);
// WinPE will complain if we don't provide a product key. *Any* product key. This is soooo idiotic...
fprintf(fd, " <UserData>\n");
fprintf(fd, " <ProductKey>\n");
fprintf(fd, " <Key>xxxxx-xxxxx-xxxxx-xxxxx-xxxxx</Key>\n");
fprintf(fd, " </ProductKey>\n");
fprintf(fd, " </UserData>\n");
fprintf(fd, " <RunSynchronous>\n");
for (i = 0; i < ARRAYSIZE(bypass_name); i++) {
if (!(mask & (1 << (i/2))))
continue;
fprintf(fd, " <RunSynchronousCommand wcm:action=\"add\">\n");
fprintf(fd, " <Order>%d</Order>\n", order++);
fprintf(fd, " <Path>reg add HKLM\\SYSTEM\\Setup\\LabConfig /v %s /t REG_DWORD /d 1 /f</Path>\n", bypass_name[i]);
fprintf(fd, " </RunSynchronousCommand>\n");
}
fprintf(fd, " </RunSynchronous>\n");
fprintf(fd, " </component>\n");
fprintf(fd, " </settings>\n");
}
// This part and some of OOBE was picked from https://github.com/AveYo/MediaCreationTool.bat/blob/main/bypass11/AutoUnattend.xml
if (mask & UNATTEND_SPECIALIZE_DEPLOYMENT_MASK) {
order = 1;
fprintf(fd, " <settings pass=\"specialize\">\n");
fprintf(fd, " <component name=\"Microsoft-Windows-Deployment\" processorArchitecture=\"%s\" language=\"neutral\" "
"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]);
fprintf(fd, " <RunSynchronous>\n");
if (mask & UNATTEND_NO_ONLINE_ACCOUNT_MASK) {
fprintf(fd, " <RunSynchronousCommand wcm:action=\"add\">\n");
fprintf(fd, " <Order>%d</Order>\n", order++);
fprintf(fd, " <Path>reg add HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\OOBE /v BypassNRO /t REG_DWORD /d 1 /f</Path>\n");
fprintf(fd, " </RunSynchronousCommand>\n");
}
if (mask & UNATTEND_SPECIALIZE_DEPLOYMENT_MASK) {
fprintf(fd, " <RunSynchronousCommand wcm:action=\"add\">\n");
fprintf(fd, " <Order>%d</Order>\n", order++);
fprintf(fd, " <Path>reg add HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate /v TargetReleaseVersion /t REG_DWORD /d 1 /f</Path>\n");
fprintf(fd, " </RunSynchronousCommand>\n");
fprintf(fd, " <RunSynchronousCommand wcm:action=\"add\">\n");
fprintf(fd, " <Order>%d</Order>\n", order++);
fprintf(fd, " <Path>reg add HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate /v TargetReleaseVersionInfo /t REG_SZ /d 25H1 /f</Path>\n");
fprintf(fd, " </RunSynchronousCommand>\n");
}
fprintf(fd, " </RunSynchronous>\n");
fprintf(fd, " </component>\n");
fprintf(fd, " </settings>\n");
}
if (mask & UNATTEND_OOBE_SHELL_SETUP) {
order = 1;
fprintf(fd, " <settings pass=\"oobeSystem\">\n");
fprintf(fd, " <component name=\"Microsoft-Windows-Shell-Setup\" processorArchitecture=\"%s\" language=\"neutral\" "
"xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
"publicKeyToken=\"31bf3856ad364e35\" versionScope=\"nonSxS\">\n", xml_arch_names[arch]);
if (mask & UNATTEND_REMOVE_WATERMARK_MASK) {
fprintf(fd, " <FirstLogonCommands>\n");
fprintf(fd, " <SynchronousCommand wcm:action=\"add\">\n");
fprintf(fd, " <Order>%d</Order>\n", order++);
fprintf(fd, " <CommandLine>reg add \"HKCU\\Control Panel\\UnsupportedHardwareNotificationCache\" /v SV1 /t REG_DWORD /d 0 /f</CommandLine>\n");
fprintf(fd, " </SynchronousCommand>\n");
fprintf(fd, " <SynchronousCommand wcm:action=\"add\">\n");
fprintf(fd, " <Order>%d</Order>\n", order++);
fprintf(fd, " <CommandLine>reg add \"HKCU\\Control Panel\\UnsupportedHardwareNotificationCache\" /v SV2 /t REG_DWORD /d 0 /f</CommandLine>\n");
fprintf(fd, " </SynchronousCommand>\n");
fprintf(fd, " </FirstLogonCommands>\n");
}
// https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-oobe-protectyourpc
// It is really super insidous of Microsoft to call this option "ProtectYourPC", when it's really only about
// data collection. But of course, if it was called "AllowDataCollection", everyone would turn it off...
if (mask & UNATTEND_NO_DATA_COLLECTION_MASK) {
fprintf(fd, " <OOBE>\n");
fprintf(fd, " <ProtectYourPC>3</ProtectYourPC>\n");
fprintf(fd, " </OOBE>\n");
}
fprintf(fd, " </component>\n");
fprintf(fd, " </settings>\n");
}
fprintf(fd, "</unattend>\n");
fclose(fd);
return path;
}
// The scanning process can be blocking for message processing => use a thread
DWORD WINAPI ImageScanThread(LPVOID param)
{
@ -1291,7 +1409,7 @@ DWORD WINAPI ImageScanThread(LPVOID param)
(!img_report.is_iso && (img_report.is_bootable_img <= 0) && !img_report.is_windows_img)) {
// Failed to scan image
if (img_report.is_bootable_img < 0)
MessageBoxExU(hMainDialog, lmprintf(MSG_325, image_path), lmprintf(MSG_042), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid);
MessageBoxExU(hMainDialog, lmprintf(MSG_322, image_path), lmprintf(MSG_042), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid);
else
MessageBoxExU(hMainDialog, lmprintf(MSG_082), lmprintf(MSG_081), MB_OK | MB_ICONINFORMATION | MB_IS_RTL, selected_langid);
// Make sure to relinquish image_path before we call UpdateImage
@ -1315,7 +1433,8 @@ DWORD WINAPI ImageScanThread(LPVOID param)
if (WimExtractFile(image_path, 1, "Windows\\Boot\\EFI\\bootmgr.efi", tmp_path, TRUE)) {
arch = FindArch(tmp_path);
if (arch != 0) {
uprintf(" Image contains a%s %s EFI boot manager", arch_name[arch - 1], (arch < 7) ? "n" : "");
uprintf(" Image contains a%s %s EFI boot manager",
(arch >= ARCH_RISCV_32 && arch <= ARCH_RISCV_128) ? "" : "n", arch_name[arch]);
img_report.has_efi = 1 | (1 << arch);
img_report.has_bootmgr_efi = TRUE;
img_report.wininst_index = 1;
@ -1501,6 +1620,21 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
default:
break;
}
if ((nWindowsVersion >= WINDOWS_8) && IS_WINDOWS_11(img_report) && (img_report.win_version.build >= 22500)) {
StrArray options;
int arch = _log2(img_report.has_efi >> 1);
StrArrayCreate(&options, 4);
StrArrayAdd(&options, lmprintf(MSG_330), TRUE);
StrArrayAdd(&options, lmprintf(MSG_331), TRUE);
StrArrayAdd(&options, lmprintf(MSG_332), TRUE);
i = SelectionDialog(BS_AUTOCHECKBOX, lmprintf(MSG_326), lmprintf(MSG_327), options.String, options.Index, unattend_xml_mask >> 2);
StrArrayDestroy(&options);
if (i < 0)
goto out;
i <<= 2;
unattend_xml_path = CreateUnattendXml(arch, i);
unattend_xml_mask = (unattend_xml_mask & 0x03) | (i & (~0x03));
}
} else if (target_type == TT_UEFI) {
if (!IS_EFI_BOOTABLE(img_report)) {
// Unsupported ISO
@ -1513,6 +1647,28 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
ShellExecuteA(hMainDialog, "open", SEVENZIP_URL, NULL, NULL, SW_SHOWNORMAL);
goto out;
}
if ((nWindowsVersion >= WINDOWS_8) && IS_WINDOWS_11(img_report)) {
StrArray options;
int arch = _log2(img_report.has_efi >> 1);
StrArrayCreate(&options, 5);
StrArrayAdd(&options, lmprintf(MSG_328), TRUE);
StrArrayAdd(&options, lmprintf(MSG_329), TRUE);
if (img_report.win_version.build >= 22500) {
StrArrayAdd(&options, lmprintf(MSG_330), TRUE);
StrArrayAdd(&options, lmprintf(MSG_331), TRUE);
}
StrArrayAdd(&options, lmprintf(MSG_332), TRUE);
i = SelectionDialog(BS_AUTOCHECKBOX, lmprintf(MSG_326), lmprintf(MSG_327), options.String, options.Index, unattend_xml_mask);
StrArrayDestroy(&options);
if (i < 0)
goto out;
unattend_xml_path = CreateUnattendXml(arch, i);
// Remember the user preferences for the current session.
// TODO: Do we want to save the current mask as a permanent setting?
unattend_xml_mask = (unattend_xml_mask & (~0x3)) | (i & 0x03);
if (img_report.win_version.build >= 22500)
unattend_xml_mask = (unattend_xml_mask & 0x03) | (i & (~0x3));
}
} else if ( ((fs_type == FS_NTFS) && !HAS_WINDOWS(img_report) && !HAS_GRUB(img_report) &&
(!HAS_SYSLINUX(img_report) || (SL_MAJOR(img_report.sl_version) <= 5)))
|| ((IS_FAT(fs_type)) && (!HAS_SYSLINUX(img_report)) && (!allow_dual_uefi_bios) && !IS_EFI_BOOTABLE(img_report) &&
@ -2188,7 +2344,7 @@ out:
}
#ifdef RUFUS_TEST
extern BOOL RemoveWindows11Restrictions(char drive_letter);
extern BOOL ApplyWindowsCustomization(char drive_letter);
static DWORD WINAPI TestThread(LPVOID param)
{
@ -2199,7 +2355,7 @@ static DWORD WINAPI TestThread(LPVOID param)
ExitThread(1);
}
processing = TRUE;
RemoveWindows11Restrictions('B');
ApplyWindowsCustomization('B');
processing = FALSE;
ExitThread(0);
}
@ -3032,6 +3188,10 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
if (queued_hotplug_event)
SendMessage(hDlg, UM_MEDIA_CHANGE, 0, 0);
if (wParam == BOOTCHECK_CANCEL) {
if (unattend_xml_path != NULL) {
DeleteFileU(unattend_xml_path);
unattend_xml_path = NULL;
}
EnableControls(TRUE, FALSE);
break;
}
@ -3042,6 +3202,10 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
case UM_FORMAT_COMPLETED:
zero_drive = FALSE;
format_thread = NULL;
if (unattend_xml_path != NULL) {
DeleteFileU(unattend_xml_path);
unattend_xml_path = NULL;
}
// Stop the timer
KillTimer(hMainDialog, TID_APP_TIMER);
// Close the cancel MessageBox and Blocking notification if active