diff --git a/.github/workflows/setup.yml b/.github/workflows/setup.yml new file mode 100644 index 00000000000..98c62dc3d70 --- /dev/null +++ b/.github/workflows/setup.yml @@ -0,0 +1,52 @@ +name: setup +run-name: Windows setup wrapper build + +on: + workflow_dispatch: + branches: [ master ] + +env: + SOLUTION_FILE_PATH: ./res/setup/setup.sln + BUILD_CONFIGURATION: Release + +jobs: + VS2022-Build: + runs-on: windows-latest + + strategy: + matrix: + TARGET_PLATFORM: [x64, arm64] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v2 + with: + msbuild-architecture: x64 + + - name: Build + shell: cmd + run: | + msbuild ${{ env.SOLUTION_FILE_PATH }} /m /p:Configuration=${{ env.BUILD_CONFIGURATION }},Platform=${{ matrix.TARGET_PLATFORM }} + move .\res\setup\${{ matrix.TARGET_PLATFORM }}\Release\setup.exe .\setup_${{ matrix.TARGET_PLATFORM }}.exe + + - name: Display SHA-256 + run: sha256sum ./setup_${{ matrix.TARGET_PLATFORM }}.exe + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.TARGET_PLATFORM }} + path: ./*.exe + + Extra-Step-To-Merge-Artifacts-Thanks-To-Upload-Artifact-v4-Breaking-Backwards-Compatibility: + runs-on: windows-latest + needs: VS2022-Build + steps: + - name: Merge Artifacts + uses: actions/upload-artifact/merge@v4 + with: + name: setup + delete-merged: true diff --git a/res/icons/license.txt b/res/icons/license.txt index 4d78ab299ab..cd9f039c16b 100644 --- a/res/icons/license.txt +++ b/res/icons/license.txt @@ -8,6 +8,11 @@ o hash-*.png, info-*.png, lang-*.png, log-*.png, save-*.png, settings-*.png CC BY-ND 4.0 when used as native resolution bitmaps, Commercial License otherwise See https://www.axialis.com/icongenerator/iconset-license.html#free +o setup.ico + Base SVG from https://tablericons.com/ + No attribution required. + With arrow overlay from Axialis Icon Set. + NB: To be on the safe side with regards to icon use and redistribution, we did purchase a Commercial License for the Axialis icons. If you are planning to use these icons outside of this project, you may diff --git a/res/icons/setup.ico b/res/icons/setup.ico new file mode 100644 index 00000000000..fcfba3ec325 Binary files /dev/null and b/res/icons/setup.ico differ diff --git a/res/icons/setup.svg b/res/icons/setup.svg new file mode 100644 index 00000000000..0d500c4f787 --- /dev/null +++ b/res/icons/setup.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/res/setup/.editorconfig b/res/setup/.editorconfig new file mode 100644 index 00000000000..12383cacede --- /dev/null +++ b/res/setup/.editorconfig @@ -0,0 +1,9 @@ +# indicate this is the root of the project +root = true + +[*] +indent_style = tab +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 diff --git a/res/setup/readme.txt b/res/setup/readme.txt new file mode 100644 index 00000000000..74622688f51 --- /dev/null +++ b/res/setup/readme.txt @@ -0,0 +1,29 @@ +Rufus: The Reliable USB Formatting Utility - Windows 11 setup.exe wrapper + +# Description + +This small executable aims at solving the issue of Windows 11 24H2 having made the +bypass requirements for in-place upgrades more difficult to enact. + +Basically, per https://github.com/pbatard/rufus/issues/2568#issuecomment-2387934171, +and if the user chose to apply the hardware requirement bypasses in Rufus, you want +to apply a set of registry key creation and deletion *before* setup.exe is run. + +While we could obviously provide a simple batch file to accomplish this, the fact +that the registry commands require elevation, combined with expectations of just +being able double click setup.exe to upgrade makes us want to accomplish this in +a more user-friendly manner. + +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: +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. diff --git a/res/setup/resource.h b/res/setup/resource.h new file mode 100644 index 00000000000..75aac6a490a --- /dev/null +++ b/res/setup/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by setup.rc +// +#define IDI_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/res/setup/setup.c b/res/setup/setup.c new file mode 100644 index 00000000000..1ad736b581c --- /dev/null +++ b/res/setup/setup.c @@ -0,0 +1,125 @@ +/* + * Setup - Wrapper around Microsoft's setup.exe that adds registry + * bypasses for in-place Windows 11 upgrade. + * + * Copyright © 2024 Pete Batard + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +static BOOL RegDeleteNodeRecurse(HKEY hKeyRoot, CHAR* lpSubKey) +{ + CHAR* lpEnd; + LONG lResult; + DWORD dwSize; + CHAR szName[MAX_PATH]; + HKEY hKey; + FILETIME ftWrite; + + // First, see if we can delete the key without having to recurse. + if (RegDeleteKeyA(hKeyRoot, lpSubKey) == ERROR_SUCCESS) + return TRUE; + + lResult = RegOpenKeyExA(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey); + if (lResult != ERROR_SUCCESS) + return (lResult == ERROR_FILE_NOT_FOUND); + + // Check for an ending slash and add one if it is missing. + lpEnd = lpSubKey + strlen(lpSubKey); + if (*(lpEnd - 1) != '\\') { + *lpEnd++ = '\\'; + *lpEnd = '\0'; + } + + // Enumerate the keys + dwSize = MAX_PATH; + if (RegEnumKeyExA(hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite) == ERROR_SUCCESS) { + do { + *lpEnd = '\0'; + strcat_s(lpSubKey, MAX_PATH, szName); + if (!RegDeleteNodeRecurse(hKeyRoot, lpSubKey)) + break; + dwSize = MAX_PATH; + lResult = RegEnumKeyExA(hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite); + } while (lResult == ERROR_SUCCESS); + } + + *--lpEnd = '\0'; + RegCloseKey(hKey); + + // Try again to delete the key. + return (RegDeleteKeyA(hKeyRoot, lpSubKey) == ERROR_SUCCESS); +} + +static BOOL RegDeleteNode(HKEY hKeyRoot, CHAR* lpSubKey) +{ + CHAR szDelKey[MAX_PATH]; + + strcpy_s(szDelKey, MAX_PATH, lpSubKey); + return RegDeleteNodeRecurse(hKeyRoot, szDelKey); +} + +static BOOL RegWriteKey(HKEY hKeyRoot, CHAR* lpKeyParent, CHAR* lpKeyName, DWORD dwType, LPBYTE lpData, DWORD dwDataSize) +{ + BOOL r = FALSE; + HKEY hRoot = NULL, hApp = NULL; + DWORD dwDisp; + HKEY hKey; + + 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); + + return r; +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + CHAR lpBypasses[] = "SQ_SecureBootCapable=TRUE\0SQ_SecureBootEnabled=TRUE\0SQ_TpmVersion=2\0SQ_RamMB=8192\0"; + DWORD dwUpgrade = 1, dwAttrib; + STARTUPINFOA si = { 0 }; + PROCESS_INFORMATION pi = { 0 }; + SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + + // Make sure we have 'setup.dll' in the same directory + dwAttrib = GetFileAttributesA("setup.dll"); + 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 + 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"); + RegWriteKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\HwReqChk", + "HwReqChkVars", REG_MULTI_SZ, lpBypasses, sizeof(lpBypasses)); + RegWriteKey(HKEY_LOCAL_MACHINE, "SYSTEM\\Setup\\MoSetup", "AllowUpgradesWithUnsupportedTPMOrCPU", + REG_DWORD, (LPBYTE)&dwUpgrade, sizeof(dwUpgrade)); + + // Launch the original 'setup.exe' (that was renamed to 'setup.dll') + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOWNORMAL; + CreateProcessA("setup.dll", NULL, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return GetLastError(); +} diff --git a/res/setup/setup.rc b/res/setup/setup.rc new file mode 100644 index 00000000000..0be07642278 --- /dev/null +++ b/res/setup/setup.rc @@ -0,0 +1,116 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#ifndef _USING_V110_SDK71_ +#define _USING_V110_SDK71_ +#endif +#include +#ifndef IDC_STATIC +#define IDC_STATIC -1 +#endif + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (Neutral) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON ICON "../icons/setup.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", "Akeo Consulting" + VALUE "FileDescription", "Windows Setup Wrapper" + VALUE "FileVersion", "1.0" + VALUE "InternalName", "Setup" + VALUE "LegalCopyright", "© 2024 Pete Batard (GPL v3)" + VALUE "LegalTrademarks", "https://rufus.ie/setup" + VALUE "OriginalFilename", "setup.exe" + VALUE "ProductName", "Setup" + VALUE "ProductVersion", "1.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + +#endif // English (Neutral) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/res/setup/setup.sln b/res/setup/setup.sln new file mode 100644 index 00000000000..dc034893b6e --- /dev/null +++ b/res/setup/setup.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29926.136 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "setup", "setup.vcxproj", "{6C2BED99-5A0A-42A2-AEBE-66717FA92232}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|arm64 = Debug|arm64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|arm64 = Release|arm64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Debug|arm64.ActiveCfg = Debug|ARM64 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Debug|arm64.Build.0 = Debug|ARM64 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Debug|x64.ActiveCfg = Debug|x64 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Debug|x64.Build.0 = Debug|x64 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Debug|x86.ActiveCfg = Debug|Win32 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Debug|x86.Build.0 = Debug|Win32 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Release|arm64.ActiveCfg = Release|ARM64 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Release|arm64.Build.0 = Release|ARM64 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Release|x64.ActiveCfg = Release|x64 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Release|x64.Build.0 = Release|x64 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Release|x86.ActiveCfg = Release|Win32 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7994FC0C-9D7C-4AD7-855B-C3525FD8709A} + EndGlobalSection +EndGlobal diff --git a/res/setup/setup.vcxproj b/res/setup/setup.vcxproj new file mode 100644 index 00000000000..b4043bc7a0e --- /dev/null +++ b/res/setup/setup.vcxproj @@ -0,0 +1,272 @@ + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {6C2BED99-5A0A-42A2-AEBE-66717FA92232} + Win32Proj + baseconsole + + + + Application + true + Unicode + v143 + + + Application + false + true + Unicode + v143 + + + Application + true + Unicode + v143 + + + Application + true + Unicode + v143 + + + Application + false + true + Unicode + v143 + + + Application + false + true + Unicode + v143 + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)arm64\$(Configuration)\ + $(SolutionDir)arm64\$(Configuration)\$(ProjectName)\ + $(SolutionDir)arm64\$(Configuration)\ + $(SolutionDir)arm64\$(Configuration)\$(ProjectName)\ + $(SolutionDir)x86\$(Configuration)\ + $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ + $(SolutionDir)x86\$(Configuration)\ + $(SolutionDir)x86\$(Configuration)\$(ProjectName)\ + $(SolutionDir)x64\$(Configuration)\ + $(SolutionDir)x64\$(Configuration)\$(ProjectName)\ + $(SolutionDir)x64\$(Configuration)\ + $(SolutionDir)x64\$(Configuration)\$(ProjectName)\ + false + false + false + false + false + false + + + true + + + true + + + true + + + false + + + false + + + false + + + + /DAPP_VERSION=$(AppVersion) %(AdditionalOptions) + + + + + /DAPP_FILE_VERSION=$(AppFileVersion) %(AdditionalOptions) + + + + + /DAPP_COMMENTS="$(AppComments)" %(AdditionalOptions) + + + + + Level3 + WIN32;_DEBUG;%(PreprocessorDefinitions) + false + CompileAsC + MultiThreadedDebug + + + + + Windows + true + RequireAdministrator + + + + + Level3 + _DEBUG;%(PreprocessorDefinitions) + false + CompileAsC + MultiThreadedDebug + + + + + Windows + true + RequireAdministrator + + + + + Level3 + _DEBUG;%(PreprocessorDefinitions) + false + CompileAsC + MultiThreadedDebug + + + + + Windows + true + RequireAdministrator + + + + + Level3 + WIN32;NDEBUG;%(PreprocessorDefinitions) + false + CompileAsC + MultiThreaded + + + MinSpace + Size + + + Windows + true + true + true + RequireAdministrator + + + + + Level3 + NDEBUG;%(PreprocessorDefinitions) + false + CompileAsC + MultiThreaded + + + MinSpace + Size + + + Windows + true + true + true + RequireAdministrator + + + + + Level3 + NDEBUG;%(PreprocessorDefinitions) + false + CompileAsC + MultiThreaded + + + MinSpace + Size + + + Windows + true + true + true + RequireAdministrator + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/setup/setup.vcxproj.filters b/res/setup/setup.vcxproj.filters new file mode 100644 index 00000000000..31d4927c846 --- /dev/null +++ b/res/setup/setup.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {e9f75410-9e5d-4ea8-b75a-0c45bb23e331} + + + + + Source Files + + + + + Resources + + + + + Source Files + + + \ No newline at end of file diff --git a/src/rufus.rc b/src/rufus.rc index ffba8ce100a..9c702576631 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -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.2198" +CAPTION "Rufus 4.6.2199" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -397,8 +397,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,6,2198,0 - PRODUCTVERSION 4,6,2198,0 + FILEVERSION 4,6,2199,0 + PRODUCTVERSION 4,6,2199,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -416,13 +416,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "4.6.2198" + VALUE "FileVersion", "4.6.2199" VALUE "InternalName", "Rufus" - VALUE "LegalCopyright", "� 2011-2024 Pete Batard (GPL v3)" + 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.2198" + VALUE "ProductVersion", "4.6.2199" END END BLOCK "VarFileInfo"