Skip to content

Commit

Permalink
[wue] add setup wrapper to add bypasses for in-place upgrades of Wind…
Browse files Browse the repository at this point in the history
…ows 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 #2568
* Also fix some MinGW build warnings.
* Also fix the annoyance of TortoiseGit/Notepad++ altering the copyright symbol of rufus.rc.
  • Loading branch information
pbatard committed Oct 6, 2024
1 parent 98a42a2 commit c800448
Show file tree
Hide file tree
Showing 12 changed files with 96 additions and 32 deletions.
1 change: 1 addition & 0 deletions _pre-commit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 22 additions & 8 deletions res/setup/readme.txt
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
5 changes: 3 additions & 2 deletions res/setup/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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");
Expand Down
Binary file added res/setup/setup_arm64.exe
Binary file not shown.
Binary file added res/setup/setup_x64.exe
Binary file not shown.
2 changes: 1 addition & 1 deletion src/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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'; ) {
Expand Down
33 changes: 24 additions & 9 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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)
{
Expand Down
2 changes: 2 additions & 0 deletions src/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 4 additions & 6 deletions src/rufus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions src/rufus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 9 additions & 5 deletions src/rufus.rc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
30 changes: 29 additions & 1 deletion src/wue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:");
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit c800448

Please sign in to comment.