[wue] add setup wrapper to add bypasses for in-place upgrades of Windows 11 24H2

* Per https://forums.mydigitallife.net/threads/win-11-boot-and-upgrade-fix-kit-v5-0-released.83724/
  Windows 11 24H2 requires new registry bypasses to be applied to perform in-place upgrade on
  non officially supported platforms, and those need to be enacted before running setup.exe.
* In order to streamline this, and because those registry bypasses require elevation, we rename
  setup.exe to setup.dll and add our own setup.exe wrapper to set the registry and then call the
  original setup.exe (through setup.dll).
* See https://github.com/pbatard/rufus/issues/2568
* Also fix some MinGW build warnings.
* Also fix the annoyance of TortoiseGit/Notepad++ altering the copyright symbol of rufus.rc.
This commit is contained in:
Pete Batard 2024-10-06 13:39:53 +01:00
parent 98a42a235f
commit c800448c62
No known key found for this signature in database
GPG key ID: 38E0CF5E69EDD671
12 changed files with 96 additions and 32 deletions

View file

@ -32,6 +32,7 @@ s/^\([ \t]*\)*\(FILE\|PRODUCT\)VERSION\([ \t]*\)\([0-9]*\),\([0-9]*\),[0-9]*,\(.
s/^\([ \t]*\)VALUE\([ \t]*\)"\(File\|Product\)Version",\([ \t]*\)"\(.*\)\..*"[ \t]*/\1VALUE\2"\3Version",\4"\5.@@BUILD@@"/
s/^\(.*\)"Rufus \(.*\)\..*"\(.*\)/\1"Rufus \2.@@BUILD@@"\3/
s/^\([ \t]*\)Version="\([0-9]*\)\.\([0-9]*\)\.[0-9]*\.\([0-9]*\)"\(.*\)/\1Version="\2.\3.@@BUILD@@.\4"\5/
s/\xef\xbf\xbd/\xa9/
_EOF
# First run sed to substitute our variable in the sed command file

View file

@ -1,4 +1,4 @@
Rufus: The Reliable USB Formatting Utility - Windows 11 setup.exe wrapper
Rufus: The Reliable USB Formatting Utility - Windows 11 Setup wrapper
# Description
@ -18,12 +18,26 @@ Our solution then is to have Rufus rename the original 'setup.exe' to 'setup.dll
insert a small 'setup.exe' that'll perform elevation, add the registry key, and
launch the original setup, which is exactly what this project does.
Now, obviously, the fact that we "inject" a setup executable may leave people
uncomfortable about the possibility that we might use this as a malware vector,
which is also why we make sure that the one we sign and embed in Rufus does get
built using GitHub Actions and can be validated to not have been tampered through
SHA-256 validation (Since we produce SHA-256 hashes during the build process per:
Oh and it should be noted that, the issues you might see with Setup not restarting
in the foreground after it updates, or not being able to launch at all for a while
if you happen to cancel before starting the installation, have *NOTHING* to do with
using this setup wrapper, but come from Microsoft themselves. You can validate that
these issues exist even when running setup.exe without the wrapper...
# Security considerations
Obviously, the fact that we "inject" a setup executable may leave people uncomfortable
about the possibility that we might use this as a malware vector, which is also why we
make sure that the one we sign and embed in Rufus does get built using GitHub Actions
and can be validated to not have been tampered through SHA-256 validation (Since we
produce SHA-256 hashes during the build process per:
https://github.com/pbatard/rufus/blob/master/.github/workflows/setup.yml).
Also note that, since these are the only platforms Windows 11 supports, we only
build for x64 and ARM64.
Per the https://github.com/pbatard/rufus/actions/runs/11195726475 GitHub Actions
workflow run, the SHA-256 for the executables (before signature was applied) were:
* 4e99f49b456781c92d2010a626706557df69334c6fc71ac129625f41fa311dd8 *./setup_x64.exe
* a0d7dea2228415eb5afe34419a31eeda90f9b51338f47bc8a5ef591054277f4b *./setup_arm64.exe
You will also find the VirusTotal reports for the current signed executable at:
* https://www.virustotal.com/gui/file/a74dbfc0e2a5b2e3fd4ad3f9fdaea0060c5d29a16151b9a570a9bd653db966a7/detection
* https://www.virustotal.com/gui/file/629fdda27e200ec98703a7606ca4e2d0e44c578341779e4d5c447d45fc860c69/detection

View file

@ -84,7 +84,7 @@ static BOOL RegWriteKey(HKEY hKeyRoot, CHAR* lpKeyParent, CHAR* lpKeyName, DWORD
if (RegCreateKeyExA(hKeyRoot, lpKeyParent, 0, NULL, 0, KEY_SET_VALUE | KEY_QUERY_VALUE, NULL, &hKey, &dwDisp) != ERROR_SUCCESS)
return FALSE;
r = (RegSetValueExA(hKey, lpKeyName, 0, dwType, lpData, dwDataSize) == ERROR_SUCCESS);
RegCloseKey(hKey);
@ -104,7 +104,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
if (dwAttrib == INVALID_FILE_ATTRIBUTES || dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
MessageBoxA(NULL, "ERROR: 'setup.dll' was not found", "Windows setup error", MB_OK | MB_ICONWARNING);
// Apply the registry bypasses to enable Windows 11 24H2 in-place upgrade
// Apply the registry bypasses to enable Windows 11 24H2 in-place upgrade. Credits to:
// https://forums.mydigitallife.net/threads/win-11-boot-and-upgrade-fix-kit-v5-0-released.83724/
RegDeleteNode(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\CompatMarkers");
RegDeleteNode(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Shared");
RegDeleteNode(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\TargetVersionUpgradeExperienceIndicators");

BIN
res/setup/setup_arm64.exe Normal file

Binary file not shown.

BIN
res/setup/setup_x64.exe Normal file

Binary file not shown.

View file

@ -2091,7 +2091,7 @@ BOOL IsRevokedBySbat(uint8_t* buf, uint32_t len)
// Look for a .sbat section
sbat = GetPeSection(buf, ".sbat", &sbat_len);
if (sbat == NULL || sbat < buf || sbat >= buf + len)
if (sbat == NULL || sbat < (char*)buf || sbat >= (char*)buf + len)
return FALSE;
for (i = 0; sbat[i] != '\0'; ) {

View file

@ -1554,18 +1554,20 @@ sbat_entry_t* GetSbatEntries(char* sbatlevel)
BOOL eol, eof;
char* version_str;
uint32_t i, num_entries;
sbat_entry_t* sbat_entries;
sbat_entry_t* _sbat_entries;
if (sbatlevel == NULL)
return FALSE;
return NULL;
num_entries = 0;
for (i = 0; sbatlevel[i] != '\0'; i++)
if (sbatlevel[i] == '\n')
num_entries++;
sbat_entries = calloc(num_entries + 2, sizeof(sbat_entry_t));
if (sbat_entries == NULL)
if (num_entries == 0)
return NULL;
_sbat_entries = calloc(num_entries + 2, sizeof(sbat_entry_t));
if (_sbat_entries == NULL)
return NULL;
num_entries = 0;
@ -1581,7 +1583,7 @@ sbat_entry_t* GetSbatEntries(char* sbatlevel)
i++;
continue;
}
sbat_entries[num_entries].product = &sbatlevel[i];
_sbat_entries[num_entries].product = &sbatlevel[i];
for (; sbatlevel[i] != ',' && sbatlevel[i] != '\0' && sbatlevel[i] != '\n'; i++);
if (sbatlevel[i] == '\0' || sbatlevel[i] == '\n')
break;
@ -1595,22 +1597,35 @@ sbat_entry_t* GetSbatEntries(char* sbatlevel)
i++;
// Allow the provision of an hex version
if (version_str[0] == '0' && version_str[1] == 'x')
sbat_entries[num_entries].version = strtoul(version_str, NULL, 16);
_sbat_entries[num_entries].version = strtoul(version_str, NULL, 16);
else
sbat_entries[num_entries].version = strtoul(version_str, NULL, 10);
_sbat_entries[num_entries].version = strtoul(version_str, NULL, 10);
if (!eol)
for (; sbatlevel[i] != '\0' && sbatlevel[i] != '\n'; i++);
if (sbat_entries[num_entries].version != 0)
if (_sbat_entries[num_entries].version != 0)
num_entries++;
}
return sbat_entries;
return _sbat_entries;
}
/*
* PE parsing functions
*/
// Return the arch of a PE executable buffer
uint16_t GetPeArch(uint8_t* buf)
{
IMAGE_DOS_HEADER* dos_header = (IMAGE_DOS_HEADER*)buf;
IMAGE_NT_HEADERS* pe_header;
if (buf == NULL)
return IMAGE_FILE_MACHINE_UNKNOWN;
pe_header = (IMAGE_NT_HEADERS*)&buf[dos_header->e_lfanew];
return pe_header->FileHeader.Machine;
}
// Return the address and (optionally) the length of a PE section from a PE buffer
uint8_t* GetPeSection(uint8_t* buf, const char* name, uint32_t* len)
{

View file

@ -69,6 +69,8 @@
#define IDR_LC_RUFUS_LOC 500
#define IDR_XT_HOGGER 501
#define IDR_UEFI_NTFS 502
#define IDR_SETUP_X64 503
#define IDR_SETUP_ARM64 504
// The following should match the ArchType array values + 600
#define IDR_MD5_BOOT 600
#define IDR_MD5_BOOTIA32 601

View file

@ -1401,10 +1401,12 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
{
int i, r, username_index = -1;
FILE *fd;
DWORD len;
uint32_t len;
uint8_t* buf = NULL;
WPARAM ret = BOOTCHECK_CANCEL;
BOOL in_files_dir = FALSE, esp_already_asked = FALSE;
BOOL is_windows_to_go = ((image_options & IMOP_WINTOGO) && (ComboBox_GetCurItemData(hImageOption) == IMOP_WIN_TO_GO));
const char* msg;
const char* grub = "grub";
const char* core_img = "core.img";
const char* ldlinux = "ldlinux";
@ -1603,10 +1605,6 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
// Check UEFI bootloaders for revocation
if (IS_EFI_BOOTABLE(img_report)) {
uint8_t* buf = NULL;
uint32_t len;
const char* msg;
for (i = 0; i < ARRAYSIZE(img_report.efi_boot_path) && img_report.efi_boot_path[i][0] != 0; i++) {
static const char* revocation_type[] = { "UEFI DBX", "Windows SSP", "Linux SBAT", "Windows SVN" };
len = ReadISOFileToBuffer(image_path, img_report.efi_boot_path[i], &buf);
@ -1720,7 +1718,7 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
if ((partition_type == PARTITION_STYLE_MBR) && HAS_SYSLINUX(img_report)) {
if (SL_MAJOR(img_report.sl_version) < 5) {
IGNORE_RETVAL(_chdirU(app_data_dir));
for (i = 0; i<NB_OLD_C32; i++) {
for (i = 0; i < NB_OLD_C32; i++) {
if (img_report.has_old_c32[i]) {
if (!in_files_dir) {
IGNORE_RETVAL(_mkdir(FILES_DIR));

View file

@ -831,6 +831,7 @@ extern HANDLE CreatePreallocatedFile(const char* lpFileName, DWORD dwDesiredAcce
DWORD dwFlagsAndAttributes, LONGLONG fileSize);
extern uint32_t ResolveDllAddress(dll_resolver_t* resolver);
extern sbat_entry_t* GetSbatEntries(char* sbatlevel);
extern uint16_t GetPeArch(uint8_t* buf);
extern uint8_t* GetPeSection(uint8_t* buf, const char* name, uint32_t* len);
extern uint8_t* RvaToPhysical(uint8_t* buf, uint32_t rva);
extern uint32_t FindResourceRva(const wchar_t* name, uint8_t* root, uint8_t* dir, uint32_t* len);

View file

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 232, 326
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 4.6.2199"
CAPTION "Rufus 4.6.2200"
FONT 9, "Segoe UI Symbol", 400, 0, 0x0
BEGIN
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
@ -305,6 +305,8 @@ BEGIN
"IDR_FD_EGA18_CPX RCDATA ""../res/freedos/EGA18.CPX""\r\n"
"IDR_XT_HOGGER RCDATA ""../res/hogger/hogger.exe""\r\n"
"IDR_UEFI_NTFS RCDATA ""../res/uefi/uefi-ntfs.img""\r\n"
"IDR_SETUP_X64 RCDATA ""../res/setup/setup_x64.exe""\r\n"
"IDR_SETUP_ARM64 RCDATA ""../res/setup/setup_arm64.exe""\r\n"
"IDR_MD5_BOOTIA32 RCDATA ""../res/md5/bootia32.efi""\r\n"
"IDR_MD5_BOOTX64 RCDATA ""../res/md5/bootx64.efi""\r\n"
"IDR_MD5_BOOTARM RCDATA ""../res/md5/bootarm.efi""\r\n"
@ -397,8 +399,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,6,2199,0
PRODUCTVERSION 4,6,2199,0
FILEVERSION 4,6,2200,0
PRODUCTVERSION 4,6,2200,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -416,13 +418,13 @@ BEGIN
VALUE "Comments", "https://rufus.ie"
VALUE "CompanyName", "Akeo Consulting"
VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "4.6.2199"
VALUE "FileVersion", "4.6.2200"
VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2024 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html"
VALUE "OriginalFilename", "rufus-4.6.exe"
VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "4.6.2199"
VALUE "ProductVersion", "4.6.2200"
END
END
BLOCK "VarFileInfo"
@ -490,6 +492,8 @@ IDR_FD_EGA17_CPX RCDATA "../res/freedos/EGA17.CPX"
IDR_FD_EGA18_CPX RCDATA "../res/freedos/EGA18.CPX"
IDR_XT_HOGGER RCDATA "../res/hogger/hogger.exe"
IDR_UEFI_NTFS RCDATA "../res/uefi/uefi-ntfs.img"
IDR_SETUP_X64 RCDATA "../res/setup/setup_x64.exe"
IDR_SETUP_ARM64 RCDATA "../res/setup/setup_arm64.exe"
IDR_MD5_BOOTIA32 RCDATA "../res/md5/bootia32.efi"
IDR_MD5_BOOTX64 RCDATA "../res/md5/bootx64.efi"
IDR_MD5_BOOTARM RCDATA "../res/md5/bootarm.efi"

View file

@ -790,10 +790,14 @@ BOOL ApplyWindowsCustomization(char drive_letter, int flags)
char boot_wim_path[] = "?:\\sources\\boot.wim", key_path[64];
char appraiserres_dll_src[] = "?:\\sources\\appraiserres.dll";
char appraiserres_dll_dst[] = "?:\\sources\\appraiserres.bak";
char setup_exe[] = "?:\\setup.exe";
char setup_dll[] = "?:\\setup.dll";
char *mount_path = NULL, path[MAX_PATH];
uint8_t* buf = NULL;
uint16_t setup_arch;
HKEY hKey = NULL, hSubKey = NULL;
LSTATUS status;
DWORD dwDisp, dwVal = 1;
DWORD dwDisp, dwVal = 1, dwSize;
assert(unattend_xml_path != NULL);
uprintf("Applying Windows customization:");
@ -832,6 +836,30 @@ BOOL ApplyWindowsCustomization(char drive_letter, int flags)
StrArrayAdd(&modified_files, appraiserres_dll_src, TRUE);
}
}
// Apply the 'setup.exe' wrapper for Windows 11 24H2 in-place upgrades
if (img_report.win_version.build >= 26000) {
setup_exe[0] = drive_letter;
setup_dll[0] = drive_letter;
dwSize = read_file(setup_exe, &buf);
if (dwSize != 0) {
setup_arch = GetPeArch(buf);
free(buf);
if (setup_arch != IMAGE_FILE_MACHINE_AMD64 && setup_arch != IMAGE_FILE_MACHINE_ARM64) {
uprintf("WARNING: Unsupported arch 0x%x -- in-place upgrade wrapper can not be added");
} else if (!MoveFileExU(setup_exe, setup_dll, 0)) {
uprintf("Could not rename '%s': %s", setup_exe, WindowsErrorString());
} else {
uprintf("Renamed '%s' → '%s'", setup_exe, setup_dll);
uprintf("Created '%s' bypass wrapper (from embedded)", setup_exe);
buf = GetResource(hMainInstance, MAKEINTRESOURCEA(setup_arch == IMAGE_FILE_MACHINE_AMD64 ? IDR_SETUP_X64 : IDR_SETUP_ARM64),
_RT_RCDATA, "setup.exe", &dwSize, FALSE);
if (buf == NULL)
uprintf("Could not access embedded 'setup.exe'");
else
write_file(setup_exe, buf, dwSize);
}
}
}
}
UpdateProgressWithInfoForce(OP_PATCH, MSG_325, 0, PATCH_PROGRESS_TOTAL);