Skip to content

Commit

Permalink
[wue] add setup wrapper project for Windows 11 24H2 in-place upgrades
Browse files Browse the repository at this point in the history
  • Loading branch information
pbatard committed Oct 5, 2024
1 parent 3e840a9 commit 98a42a2
Show file tree
Hide file tree
Showing 13 changed files with 705 additions and 6 deletions.
52 changes: 52 additions & 0 deletions .github/workflows/setup.yml
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions res/icons/license.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Binary file added res/icons/setup.ico
Binary file not shown.
11 changes: 11 additions & 0 deletions res/icons/setup.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions res/setup/.editorconfig
Original file line number Diff line number Diff line change
@@ -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
29 changes: 29 additions & 0 deletions res/setup/readme.txt
Original file line number Diff line number Diff line change
@@ -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.
16 changes: 16 additions & 0 deletions res/setup/resource.h
Original file line number Diff line number Diff line change
@@ -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
125 changes: 125 additions & 0 deletions res/setup/setup.c
Original file line number Diff line number Diff line change
@@ -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 <pete@akeo.ie>
*
* 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 <http://www.gnu.org/licenses/>.
*/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <sys/types.h>
#include <sys/stat.h>

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();
}
116 changes: 116 additions & 0 deletions res/setup/setup.rc
Original file line number Diff line number Diff line change
@@ -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 <windows.h>
#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

Loading

0 comments on commit 98a42a2

Please sign in to comment.