mirror of
https://github.com/pbatard/rufus.git
synced 2025-05-09 12:31:57 -04:00
[wue] add experimental option to replace Windows bootloaders with the 24H2 _EX versions
* This aims at creating installation media that is compatible with systems where 'Microsoft Windows Production PCA 2011' has been revoked. * Doesn't work, since the bootloaders being applied by the first stage installer come from \sources\install.wim[#]\windows\system32\Recovery\Winre.wim[#]\Windows\Boot\ (instead of \sources\boot.wim[#]\Windows\Boot\ as one would naturally expect) and Microsoft botched the ones they included there by using completely vulnerable (and therefore revoked) ones. See https://github.com/pbatard/rufus/issues/2244#issuecomment-2400380839. * Still, I sure haven't gone through this excruciating ACL bullshit for nothing, so you get an experimental option, behind the expert mode curtain.
This commit is contained in:
parent
c800448c62
commit
fd5c366938
9 changed files with 360 additions and 49 deletions
|
@ -609,6 +609,7 @@ t MSG_346 "Restrict Windows to S-Mode (INCOMPATIBLE with online account bypass)"
|
|||
t MSG_347 "Expert Mode"
|
||||
t MSG_348 "Extracting archive files: %s"
|
||||
t MSG_349 "Use Rufus MBR"
|
||||
t MSG_350 "Use 'Windows UEFI CA 2023' signed bootloaders [EXPERIMENTAL]"
|
||||
# The following messages are for the Windows Store listing only and are not used by the application
|
||||
t MSG_900 "Rufus is a utility that helps format and create bootable USB flash drives, such as USB keys/pendrives, memory sticks, etc."
|
||||
t MSG_901 "Official site: %s"
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <stdio.h>
|
||||
#include <shlobj.h>
|
||||
#include <ctype.h>
|
||||
#include <aclapi.h>
|
||||
#include <accctrl.h>
|
||||
#include <commdlg.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlwapi.h>
|
||||
|
@ -89,6 +91,9 @@ static __inline char* wchar_to_utf8(const wchar_t* wstr)
|
|||
int size = 0;
|
||||
char* str = NULL;
|
||||
|
||||
if (wstr == NULL)
|
||||
return NULL;
|
||||
|
||||
// Convert the empty string too
|
||||
if (wstr[0] == 0)
|
||||
return (char*)calloc(1, 1);
|
||||
|
@ -568,6 +573,52 @@ static __inline const char* PathFindFileNameU(const char* szPath)
|
|||
return &szPath[i];
|
||||
}
|
||||
|
||||
static __inline char* PathCombineU(char* lpDest, char* lpDir, char* lpFile)
|
||||
{
|
||||
wchar_t* wret = NULL;
|
||||
DWORD err = ERROR_INVALID_DATA;
|
||||
wchar_t wlpDest[MAX_PATH];
|
||||
wconvert(lpDir);
|
||||
wconvert(lpFile);
|
||||
wret = PathCombineW(wlpDest, wlpDir, wlpFile);
|
||||
err = GetLastError();
|
||||
wfree(lpDir);
|
||||
wfree(lpFile);
|
||||
if (wret == NULL)
|
||||
return NULL;
|
||||
wchar_to_utf8_no_alloc(wlpDest, lpDest, MAX_PATH);
|
||||
SetLastError(err);
|
||||
return lpDest;
|
||||
}
|
||||
|
||||
static __inline HANDLE FindFirstFileU(char* lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
|
||||
{
|
||||
HANDLE ret = INVALID_HANDLE_VALUE;
|
||||
WIN32_FIND_DATAW wFindFileData = { 0 };
|
||||
wconvert(lpFileName);
|
||||
ret = FindFirstFileW(wlpFileName, &wFindFileData);
|
||||
if (ret != INVALID_HANDLE_VALUE) {
|
||||
memcpy(lpFindFileData, &wFindFileData, offsetof(WIN32_FIND_DATAW, cFileName));
|
||||
wchar_to_utf8_no_alloc(wFindFileData.cFileName, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
|
||||
wchar_to_utf8_no_alloc(wFindFileData.cAlternateFileName, lpFindFileData->cAlternateFileName, sizeof(lpFindFileData->cAlternateFileName));
|
||||
}
|
||||
wfree(lpFileName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __inline BOOL FindNextFileU(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
WIN32_FIND_DATAW wFindFileData = { 0 };
|
||||
ret = FindNextFileW(hFindFile, &wFindFileData);
|
||||
if (ret) {
|
||||
memcpy(lpFindFileData, &wFindFileData, offsetof(WIN32_FIND_DATAW, cFileName));
|
||||
wchar_to_utf8_no_alloc(wFindFileData.cFileName, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
|
||||
wchar_to_utf8_no_alloc(wFindFileData.cAlternateFileName, lpFindFileData->cAlternateFileName, sizeof(lpFindFileData->cAlternateFileName));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// This function differs from regular GetTextExtentPoint in that it uses a zero terminated string
|
||||
static __inline BOOL GetTextExtentPointU(HDC hdc, const char* lpString, LPSIZE lpSize)
|
||||
{
|
||||
|
@ -819,6 +870,28 @@ static __inline BOOL SetFileAttributesU(const char* lpFileName, DWORD dwFileAttr
|
|||
return ret;
|
||||
}
|
||||
|
||||
static __inline DWORD GetNamedSecurityInfoU(const char* lpObjectName, SE_OBJECT_TYPE ObjectType,
|
||||
SECURITY_INFORMATION SecurityInfo, PSID* ppsidOwner, PSID* ppsidGroup, PACL* ppDacl,
|
||||
PACL* ppSacl, PSECURITY_DESCRIPTOR* ppSecurityDescriptor)
|
||||
{
|
||||
DWORD ret;
|
||||
wconvert(lpObjectName);
|
||||
ret = GetNamedSecurityInfoW(wlpObjectName, ObjectType, SecurityInfo, ppsidOwner, ppsidGroup,
|
||||
ppDacl, ppSacl, ppSecurityDescriptor);
|
||||
wfree(lpObjectName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __inline DWORD SetNamedSecurityInfoU(const char* lpObjectName, SE_OBJECT_TYPE ObjectType,
|
||||
SECURITY_INFORMATION SecurityInfo, PSID psidOwner, PSID psidGroup, PACL pDacl, PACL pSacl)
|
||||
{
|
||||
DWORD ret;
|
||||
wconvert(lpObjectName);
|
||||
ret = SetNamedSecurityInfoW(wlpObjectName, ObjectType, SecurityInfo, psidOwner, psidGroup, pDacl, pSacl);
|
||||
wfree(lpObjectName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __inline int SHCreateDirectoryExU(HWND hwnd, const char* pszPath, SECURITY_ATTRIBUTES *psa)
|
||||
{
|
||||
int ret = ERROR_INVALID_DATA;
|
||||
|
|
31
src/parser.c
31
src/parser.c
|
@ -1267,7 +1267,7 @@ out:
|
|||
}
|
||||
|
||||
/*
|
||||
* Replace all 'c' characters in string 'src' with the substring 'rep'
|
||||
* Replace all 'c' characters in string 'src' with the substring 'rep'.
|
||||
* The returned string is allocated and must be freed by the caller.
|
||||
*/
|
||||
char* replace_char(const char* src, const char c, const char* rep)
|
||||
|
@ -1300,6 +1300,30 @@ char* replace_char(const char* src, const char c, const char* rep)
|
|||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all instances of substring 'sub' form string 'src.
|
||||
* The returned string is allocated and must be freed by the caller.
|
||||
*/
|
||||
char* remove_substr(const char* src, const char* sub)
|
||||
{
|
||||
size_t i, j, str_len = safe_strlen(src), sub_len = safe_strlen(sub);
|
||||
char* res;
|
||||
|
||||
if ((src == NULL) || (sub == NULL) || (sub_len > str_len))
|
||||
return NULL;
|
||||
|
||||
res = (char*)calloc(str_len + 1, 1);
|
||||
if (res == NULL)
|
||||
return NULL;
|
||||
for (i = 0, j = 0; i <= str_len; ) {
|
||||
if (i <= str_len - sub_len && memcmp(&src[i], sub, sub_len) == 0)
|
||||
i += sub_len;
|
||||
else
|
||||
res[j++] = src[i++];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal recursive call for get_data_from_asn1(). Returns FALSE on error, TRUE otherwise.
|
||||
*/
|
||||
|
@ -1697,8 +1721,7 @@ uint8_t* RvaToPhysical(uint8_t* buf, uint32_t rva)
|
|||
static BOOL FoundResourceRva = FALSE;
|
||||
uint32_t FindResourceRva(const wchar_t* name, uint8_t* root, uint8_t* dir, uint32_t* len)
|
||||
{
|
||||
uint32_t rva;
|
||||
WORD i;
|
||||
uint32_t i, rva;
|
||||
IMAGE_RESOURCE_DIRECTORY* _dir = (IMAGE_RESOURCE_DIRECTORY*)dir;
|
||||
IMAGE_RESOURCE_DIRECTORY_ENTRY* dir_entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)&_dir[1];
|
||||
IMAGE_RESOURCE_DIR_STRING_U* dir_string;
|
||||
|
@ -1711,7 +1734,7 @@ uint32_t FindResourceRva(const wchar_t* name, uint8_t* root, uint8_t* dir, uint3
|
|||
if (root == dir)
|
||||
FoundResourceRva = FALSE;
|
||||
|
||||
for (i = 0; i < _dir->NumberOfNamedEntries + _dir->NumberOfIdEntries; i++) {
|
||||
for (i = 0; i < (uint32_t)_dir->NumberOfNamedEntries + _dir->NumberOfIdEntries; i++) {
|
||||
if (!FoundResourceRva && i < _dir->NumberOfNamedEntries) {
|
||||
dir_string = (IMAGE_RESOURCE_DIR_STRING_U*)(root + dir_entry[i].NameOffset);
|
||||
if (dir_string->Length != wcslen(name) ||
|
||||
|
|
|
@ -1586,6 +1586,10 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
|
|||
StrArrayAdd(&options, lmprintf(MSG_335), TRUE);
|
||||
MAP_BIT(UNATTEND_DISABLE_BITLOCKER);
|
||||
if (expert_mode) {
|
||||
if (!appstore_version && img_report.win_version.build >= 26100) {
|
||||
StrArrayAdd(&options, lmprintf(MSG_350), TRUE);
|
||||
MAP_BIT(UNATTEND_USE_MS2023_BOOTLOADERS);
|
||||
}
|
||||
StrArrayAdd(&options, lmprintf(MSG_346), TRUE);
|
||||
MAP_BIT(UNATTEND_FORCE_S_MODE);
|
||||
}
|
||||
|
@ -1597,7 +1601,7 @@ static DWORD WINAPI BootCheckThread(LPVOID param)
|
|||
i = remap16(i, map, TRUE);
|
||||
unattend_xml_path = CreateUnattendXml(arch, i);
|
||||
// Remember the user preferences for the current session.
|
||||
unattend_xml_mask &= ~(remap16(0x1ff, map, TRUE));
|
||||
unattend_xml_mask &= ~(remap16(UNATTEND_FULL_MASK, map, TRUE));
|
||||
unattend_xml_mask |= i;
|
||||
WriteSetting32(SETTING_WUE_OPTIONS, (UNATTEND_DEFAULT_MASK << 16) | unattend_xml_mask);
|
||||
}
|
||||
|
|
12
src/rufus.h
12
src/rufus.h
|
@ -629,6 +629,8 @@ typedef struct {
|
|||
#define UNATTEND_SET_USER 0x00040
|
||||
#define UNATTEND_DISABLE_BITLOCKER 0x00080
|
||||
#define UNATTEND_FORCE_S_MODE 0x00100
|
||||
#define UNATTEND_USE_MS2023_BOOTLOADERS 0x00200
|
||||
#define UNATTEND_FULL_MASK 0x003FF
|
||||
#define UNATTEND_DEFAULT_MASK 0x000FF
|
||||
#define UNATTEND_WINDOWS_TO_GO 0x10000 // Special flag for Windows To Go
|
||||
|
||||
|
@ -636,10 +638,15 @@ typedef struct {
|
|||
#define UNATTEND_SPECIALIZE_DEPLOYMENT_MASK (UNATTEND_NO_ONLINE_ACCOUNT)
|
||||
#define UNATTEND_OOBE_SHELL_SETUP_MASK (UNATTEND_NO_DATA_COLLECTION | UNATTEND_SET_USER | UNATTEND_DUPLICATE_LOCALE)
|
||||
#define UNATTEND_OOBE_INTERNATIONAL_MASK (UNATTEND_DUPLICATE_LOCALE)
|
||||
#define UNATTEND_OOBE_MASK (UNATTEND_OOBE_SHELL_SETUP_MASK | UNATTEND_OOBE_INTERNATIONAL_MASK | UNATTEND_DISABLE_BITLOCKER)
|
||||
#define UNATTEND_OOBE_MASK (UNATTEND_OOBE_SHELL_SETUP_MASK | UNATTEND_OOBE_INTERNATIONAL_MASK | UNATTEND_DISABLE_BITLOCKER | UNATTEND_USE_MS2023_BOOTLOADERS)
|
||||
#define UNATTEND_OFFLINE_SERVICING_MASK (UNATTEND_OFFLINE_INTERNAL_DRIVES | UNATTEND_FORCE_S_MODE)
|
||||
#define UNATTEND_DEFAULT_SELECTION_MASK (UNATTEND_SECUREBOOT_TPM_MINRAM | UNATTEND_NO_ONLINE_ACCOUNT | UNATTEND_OFFLINE_INTERNAL_DRIVES)
|
||||
|
||||
/* Used with ListDirectoryContent */
|
||||
#define LIST_DIR_TYPE_FILE 0x01
|
||||
#define LIST_DIR_TYPE_DIRECTORY 0x02
|
||||
#define LIST_DIR_TYPE_RECURSIVE 0x80
|
||||
|
||||
/* Hash tables */
|
||||
typedef struct htab_entry {
|
||||
uint32_t used;
|
||||
|
@ -786,6 +793,7 @@ extern char* get_token_data_buffer(const char* token, unsigned int n, const char
|
|||
extern char* insert_section_data(const char* filename, const char* section, const char* data, BOOL dos2unix);
|
||||
extern char* replace_in_token_data(const char* filename, const char* token, const char* src, const char* rep, BOOL dos2unix);
|
||||
extern char* replace_char(const char* src, const char c, const char* rep);
|
||||
extern char* remove_substr(const char* src, const char* sub);
|
||||
extern void parse_update(char* buf, size_t len);
|
||||
extern void* get_data_from_asn1(const uint8_t* buf, size_t buf_len, const char* oid_str, uint8_t asn1_type, size_t* data_len);
|
||||
extern int sanitize_label(char* label);
|
||||
|
@ -835,6 +843,8 @@ 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);
|
||||
extern DWORD ListDirectoryContent(StrArray* arr, char* dir, uint8_t type);
|
||||
extern BOOL TakeOwnership(LPCSTR lpszOwnFile);
|
||||
#define GetTextWidth(hDlg, id) GetTextSize(GetDlgItem(hDlg, id), NULL).cx
|
||||
|
||||
DWORD WINAPI HashThread(void* param);
|
||||
|
|
10
src/rufus.rc
10
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.2200"
|
||||
CAPTION "Rufus 4.6.2201"
|
||||
FONT 9, "Segoe UI Symbol", 400, 0, 0x0
|
||||
BEGIN
|
||||
LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP
|
||||
|
@ -399,8 +399,8 @@ END
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,6,2200,0
|
||||
PRODUCTVERSION 4,6,2200,0
|
||||
FILEVERSION 4,6,2201,0
|
||||
PRODUCTVERSION 4,6,2201,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -418,13 +418,13 @@ BEGIN
|
|||
VALUE "Comments", "https://rufus.ie"
|
||||
VALUE "CompanyName", "Akeo Consulting"
|
||||
VALUE "FileDescription", "Rufus"
|
||||
VALUE "FileVersion", "4.6.2200"
|
||||
VALUE "FileVersion", "4.6.2201"
|
||||
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.2200"
|
||||
VALUE "ProductVersion", "4.6.2201"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
72
src/stdfn.c
72
src/stdfn.c
|
@ -25,6 +25,8 @@
|
|||
#include <sddl.h>
|
||||
#include <gpedit.h>
|
||||
#include <assert.h>
|
||||
#include <accctrl.h>
|
||||
#include <aclapi.h>
|
||||
|
||||
#include "re.h"
|
||||
#include "rufus.h"
|
||||
|
@ -1242,3 +1244,73 @@ BOOL UnmountRegistryHive(const HKEY key, const char* pszHiveName)
|
|||
|
||||
return (status == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Take administrative ownership of a file or directory, and grant all access rights.
|
||||
*/
|
||||
BOOL TakeOwnership(LPCSTR lpszOwnFile)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
HANDLE hToken = NULL;
|
||||
PSID pSIDAdmin = NULL;
|
||||
PACL pOldDACL = NULL, pNewDACL = NULL;
|
||||
PSECURITY_DESCRIPTOR pSD = NULL;
|
||||
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
|
||||
EXPLICIT_ACCESS ea = { 0 };
|
||||
|
||||
if (lpszOwnFile == NULL)
|
||||
return FALSE;
|
||||
|
||||
// Create a SID for the BUILTIN\Administrators group.
|
||||
if (!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
|
||||
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSIDAdmin))
|
||||
goto out;
|
||||
|
||||
// Open a handle to the access token for the calling process.
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
|
||||
goto out;
|
||||
|
||||
// Enable the SE_TAKE_OWNERSHIP_NAME privilege.
|
||||
if (!SetPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, TRUE))
|
||||
goto out;
|
||||
|
||||
// Set the owner in the object's security descriptor.
|
||||
if (SetNamedSecurityInfoU(lpszOwnFile, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION,
|
||||
pSIDAdmin, NULL, NULL, NULL) != ERROR_SUCCESS)
|
||||
goto out;
|
||||
|
||||
// Disable the SE_TAKE_OWNERSHIP_NAME privilege.
|
||||
if (!SetPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, FALSE))
|
||||
goto out;
|
||||
|
||||
// Get a pointer to the existing DACL.
|
||||
if (GetNamedSecurityInfoU(lpszOwnFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
|
||||
NULL, NULL, &pOldDACL, NULL, &pSD) != ERROR_SUCCESS)
|
||||
goto out;
|
||||
|
||||
// Initialize an EXPLICIT_ACCESS structure for the new ACE
|
||||
// with full control for Administrators.
|
||||
ea.grfAccessPermissions = GENERIC_ALL;
|
||||
ea.grfAccessMode = GRANT_ACCESS;
|
||||
ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
|
||||
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
|
||||
ea.Trustee.ptstrName = (LPTSTR)pSIDAdmin;
|
||||
|
||||
// Create a new ACL that merges the new ACE into the existing DACL.
|
||||
if (SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL) != ERROR_SUCCESS)
|
||||
goto out;
|
||||
|
||||
// Try to modify the object's DACL.
|
||||
if (SetNamedSecurityInfoU(lpszOwnFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
|
||||
NULL, NULL, pNewDACL, NULL) != ERROR_SUCCESS)
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
FreeSid(pSIDAdmin);
|
||||
LocalFree(pNewDACL);
|
||||
safe_closehandle(hToken);
|
||||
return ret;
|
||||
}
|
||||
|
|
61
src/stdio.c
61
src/stdio.c
|
@ -203,7 +203,7 @@ void DumpBufferHex(void *buf, size_t size)
|
|||
uprintf("%s\n", line);
|
||||
line[0] = 0;
|
||||
sprintf(&line[strlen(line)], " %08x ", (unsigned int)i);
|
||||
for(j=0,k=0; k<16; j++,k++) {
|
||||
for(j = 0,k = 0; k < 16; j++,k++) {
|
||||
if (i+j < size) {
|
||||
sprintf(&line[strlen(line)], "%02x", buffer[i+j]);
|
||||
} else {
|
||||
|
@ -212,7 +212,7 @@ void DumpBufferHex(void *buf, size_t size)
|
|||
sprintf(&line[strlen(line)], " ");
|
||||
}
|
||||
sprintf(&line[strlen(line)], " ");
|
||||
for(j=0,k=0; k<16; j++,k++) {
|
||||
for(j = 0,k = 0; k < 16; j++,k++) {
|
||||
if (i+j < size) {
|
||||
if ((buffer[i+j] < 32) || (buffer[i+j] > 126)) {
|
||||
sprintf(&line[strlen(line)], ".");
|
||||
|
@ -920,3 +920,60 @@ BOOL ExtractZip(const char* src_zip, const char* dest_dir)
|
|||
bled_exit();
|
||||
return (extracted_bytes > 0);
|
||||
}
|
||||
|
||||
// Returns a list of all the files or folders from a directory
|
||||
DWORD ListDirectoryContent(StrArray* arr, char* dir, uint8_t type)
|
||||
{
|
||||
WIN32_FIND_DATAA FindFileData = { 0 };
|
||||
HANDLE hFind;
|
||||
DWORD dwError, dwResult;
|
||||
char mask[MAX_PATH + 1], path[MAX_PATH + 1];
|
||||
|
||||
if (arr == NULL || dir == NULL || (type & 0x03) == 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (PathCombineU(mask, dir, "*") == NULL)
|
||||
return GetLastError();
|
||||
|
||||
hFind = FindFirstFileU(mask, &FindFileData);
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
return GetLastError();
|
||||
|
||||
dwResult = ERROR_FILE_NOT_FOUND;
|
||||
do {
|
||||
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (strcmp(FindFileData.cFileName, ".") == 0 ||
|
||||
strcmp(FindFileData.cFileName, "..") == 0 ||
|
||||
(type & LIST_DIR_TYPE_RECURSIVE) == 0)
|
||||
continue;
|
||||
if (PathCombineU(path, dir, FindFileData.cFileName) == NULL)
|
||||
break;
|
||||
// Append a trailing backslash to directories
|
||||
if (path[strlen(path) - 1] != '\\') {
|
||||
path[strlen(path) + 1] = '\0';
|
||||
path[strlen(path)] = '\\';
|
||||
}
|
||||
if (type & LIST_DIR_TYPE_DIRECTORY)
|
||||
StrArrayAdd(arr, path, TRUE);
|
||||
dwError = ListDirectoryContent(arr, path, type);
|
||||
if (dwError != NO_ERROR && dwError != ERROR_FILE_NOT_FOUND) {
|
||||
SetLastError(dwError);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (type & LIST_DIR_TYPE_FILE) {
|
||||
if (PathCombineU(path, dir, FindFileData.cFileName) == NULL)
|
||||
break;
|
||||
StrArrayAdd(arr, path, TRUE);
|
||||
}
|
||||
dwResult = NO_ERROR;
|
||||
}
|
||||
} while (FindNextFileU(hFind, &FindFileData));
|
||||
|
||||
dwError = GetLastError();
|
||||
FindClose(hFind);
|
||||
|
||||
if (dwError != ERROR_NO_MORE_FILES)
|
||||
return dwError;
|
||||
return dwResult;
|
||||
}
|
||||
|
|
143
src/wue.c
143
src/wue.c
|
@ -51,6 +51,7 @@ extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files;
|
|||
extern BOOL validate_md5sum;
|
||||
extern uint64_t md5sum_totalbytes;
|
||||
extern StrArray modified_files;
|
||||
extern const char* efi_archname[ARCH_MAX];
|
||||
|
||||
/// <summary>
|
||||
/// Create an installation answer file containing the sections specified by the flags.
|
||||
|
@ -162,43 +163,56 @@ char* CreateUnattendXml(int arch, int flags)
|
|||
free(tzstr);
|
||||
}
|
||||
}
|
||||
if (flags & UNATTEND_SET_USER) {
|
||||
for (i = 0; (i < ARRAYSIZE(unallowed_account_names)) && (stricmp(unattend_username, unallowed_account_names[i]) != 0); i++);
|
||||
if (i < ARRAYSIZE(unallowed_account_names)) {
|
||||
uprintf("WARNING: '%s' is not allowed as local account name - Option ignored", unattend_username);
|
||||
} else if (unattend_username[0] != 0) {
|
||||
uprintf("• Use '%s' for local account name", unattend_username);
|
||||
// If we create a local account in unattend.xml, then we can get Windows 11
|
||||
// 22H2 to skip MSA even if the network is connected during installation.
|
||||
fprintf(fd, " <UserAccounts>\n");
|
||||
fprintf(fd, " <LocalAccounts>\n");
|
||||
fprintf(fd, " <LocalAccount wcm:action=\"add\">\n");
|
||||
fprintf(fd, " <Name>%s</Name>\n", unattend_username);
|
||||
fprintf(fd, " <DisplayName>%s</DisplayName>\n", unattend_username);
|
||||
fprintf(fd, " <Group>Administrators;Power Users</Group>\n");
|
||||
// Sets an empty password for the account (which, in Microsoft's convoluted ways,
|
||||
// needs to be initialized to the Base64 encoded UTF-16 string "Password").
|
||||
// The use of an empty password has both the advantage of not having to ask users
|
||||
// to type in a password in Rufus (which they might be weary of) as well as allowing
|
||||
// automated logon during setup.
|
||||
fprintf(fd, " <Password>\n");
|
||||
fprintf(fd, " <Value>UABhAHMAcwB3AG8AcgBkAA==</Value>\n");
|
||||
fprintf(fd, " <PlainText>false</PlainText>\n");
|
||||
fprintf(fd, " </Password>\n");
|
||||
fprintf(fd, " </LocalAccount>\n");
|
||||
fprintf(fd, " </LocalAccounts>\n");
|
||||
fprintf(fd, " </UserAccounts>\n");
|
||||
// Since we set a blank password, we'll ask the user to change it at next logon.
|
||||
if (flags & UNATTEND_SET_USER || flags & UNATTEND_USE_MS2023_BOOTLOADERS) {
|
||||
if (flags & UNATTEND_SET_USER) {
|
||||
for (i = 0; (i < ARRAYSIZE(unallowed_account_names)) && (stricmp(unattend_username, unallowed_account_names[i]) != 0); i++);
|
||||
if (i < ARRAYSIZE(unallowed_account_names)) {
|
||||
uprintf("WARNING: '%s' is not allowed as local account name - Option ignored", unattend_username);
|
||||
} else if (unattend_username[0] != 0) {
|
||||
uprintf("• Use '%s' for local account name", unattend_username);
|
||||
// If we create a local account in unattend.xml, then we can get Windows 11
|
||||
// 22H2 to skip MSA even if the network is connected during installation.
|
||||
fprintf(fd, " <UserAccounts>\n");
|
||||
fprintf(fd, " <LocalAccounts>\n");
|
||||
fprintf(fd, " <LocalAccount wcm:action=\"add\">\n");
|
||||
fprintf(fd, " <Name>%s</Name>\n", unattend_username);
|
||||
fprintf(fd, " <DisplayName>%s</DisplayName>\n", unattend_username);
|
||||
fprintf(fd, " <Group>Administrators;Power Users</Group>\n");
|
||||
// Sets an empty password for the account (which, in Microsoft's convoluted ways,
|
||||
// needs to be initialized to the Base64 encoded UTF-16 string "Password").
|
||||
// The use of an empty password has both the advantage of not having to ask users
|
||||
// to type in a password in Rufus (which they might be weary of) as well as allowing
|
||||
// automated logon during setup.
|
||||
fprintf(fd, " <Password>\n");
|
||||
fprintf(fd, " <Value>UABhAHMAcwB3AG8AcgBkAA==</Value>\n");
|
||||
fprintf(fd, " <PlainText>false</PlainText>\n");
|
||||
fprintf(fd, " </Password>\n");
|
||||
fprintf(fd, " </LocalAccount>\n");
|
||||
fprintf(fd, " </LocalAccounts>\n");
|
||||
fprintf(fd, " </UserAccounts>\n");
|
||||
// Since we set a blank password, we'll ask the user to change it at next logon.
|
||||
fprintf(fd, " <FirstLogonCommands>\n");
|
||||
fprintf(fd, " <SynchronousCommand wcm:action=\"add\">\n");
|
||||
fprintf(fd, " <Order>%d</Order>\n", order++);
|
||||
fprintf(fd, " <CommandLine>net user "%s" /logonpasswordchg:yes</CommandLine>\n", unattend_username);
|
||||
fprintf(fd, " </SynchronousCommand>\n");
|
||||
// Some people report that using the `net user` command above might reset the password expiration to 90 days...
|
||||
// To alleviate that, blanket set passwords on the target machine to never expire.
|
||||
fprintf(fd, " <SynchronousCommand wcm:action=\"add\">\n");
|
||||
fprintf(fd, " <Order>%d</Order>\n", order++);
|
||||
fprintf(fd, " <CommandLine>net accounts /maxpwage:unlimited</CommandLine>\n");
|
||||
fprintf(fd, " </SynchronousCommand>\n");
|
||||
fprintf(fd, " </FirstLogonCommands>\n");
|
||||
}
|
||||
}
|
||||
if (flags & UNATTEND_USE_MS2023_BOOTLOADERS) {
|
||||
uprintf("• Use 'Windows UEFI CA 2023' signed bootloaders");
|
||||
// TODO: Validate that we can have multiple <FirstLogonCommands> sections
|
||||
fprintf(fd, " <FirstLogonCommands>\n");
|
||||
fprintf(fd, " <SynchronousCommand wcm:action=\"add\">\n");
|
||||
fprintf(fd, " <Order>%d</Order>\n", order++);
|
||||
fprintf(fd, " <CommandLine>net user "%s" /logonpasswordchg:yes</CommandLine>\n", unattend_username);
|
||||
fprintf(fd, " </SynchronousCommand>\n");
|
||||
// Some people report that using the `net user` command above might reset the password expiration to 90 days...
|
||||
// To alleviate that, blanket set passwords on the target machine to never expire.
|
||||
fprintf(fd, " <SynchronousCommand wcm:action=\"add\">\n");
|
||||
fprintf(fd, " <Order>%d</Order>\n", order++);
|
||||
fprintf(fd, " <CommandLine>net accounts /maxpwage:unlimited</CommandLine>\n");
|
||||
// TODO: Validate the actual value on a machine where updates have been applied
|
||||
fprintf(fd, " <CommandLine>reg add HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Secureboot /v AvailableUpdates /t REG_DWORD /d 0x3c0 /f\n");
|
||||
fprintf(fd, " </SynchronousCommand>\n");
|
||||
fprintf(fd, " </FirstLogonCommands>\n");
|
||||
}
|
||||
|
@ -845,7 +859,7 @@ BOOL ApplyWindowsCustomization(char drive_letter, int flags)
|
|||
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");
|
||||
uprintf("WARNING: Unsupported arch 0x%x -- in-place upgrade wrapper will not be added", setup_arch);
|
||||
} else if (!MoveFileExU(setup_exe, setup_dll, 0)) {
|
||||
uprintf("Could not rename '%s': %s", setup_exe, WindowsErrorString());
|
||||
} else {
|
||||
|
@ -865,7 +879,8 @@ BOOL ApplyWindowsCustomization(char drive_letter, int flags)
|
|||
UpdateProgressWithInfoForce(OP_PATCH, MSG_325, 0, PATCH_PROGRESS_TOTAL);
|
||||
// We only need to mount boot.wim if we have windowsPE data to deal with. If
|
||||
// not, we can just copy our unattend.xml in \sources\$OEM$\$$\Panther\.
|
||||
if (flags & UNATTEND_WINPE_SETUP_MASK) {
|
||||
// We also need to mount it if we use the 'Windows UEFI CA 2023' signed bootloaders.
|
||||
if (flags & UNATTEND_WINPE_SETUP_MASK || flags & UNATTEND_USE_MS2023_BOOTLOADERS) {
|
||||
if (validate_md5sum)
|
||||
md5sum_totalbytes -= _filesizeU(boot_wim_path);
|
||||
uprintf("Mounting '%s[%d]'...", boot_wim_path, wim_index);
|
||||
|
@ -969,6 +984,62 @@ BOOL ApplyWindowsCustomization(char drive_letter, int flags)
|
|||
}
|
||||
UpdateProgressWithInfoForce(OP_PATCH, MSG_325, 103, PATCH_PROGRESS_TOTAL);
|
||||
}
|
||||
|
||||
if (flags & UNATTEND_USE_MS2023_BOOTLOADERS) {
|
||||
if_not_assert(mount_path != NULL)
|
||||
goto out;
|
||||
static_sprintf(path, "%s\\Windows\\Boot\\EFI_EX\\bootmgfw_EX.efi", mount_path);
|
||||
if (!PathFileExistsU(path)) {
|
||||
uprintf("Could not find 2023 signed UEFI bootloader - Ignoring option");
|
||||
} else {
|
||||
char path2[MAX_PATH], *rep;
|
||||
StrArray files, dirs;
|
||||
// Replace /EFI/Boot/boot###.efi
|
||||
for (i = 1; i < ARRAYSIZE(efi_archname); i++) {
|
||||
static_sprintf(path2, "%c:\\efi\\boot\\boot%s.efi", drive_letter, efi_archname[i]);
|
||||
if (!PathFileExistsA(path2))
|
||||
continue;
|
||||
if (!CopyFileU(path, path2, FALSE))
|
||||
uprintf("WARNING: Could not replace 'boot%s.efi': %s", efi_archname[i], WindowsErrorString());
|
||||
break;
|
||||
}
|
||||
// Replace /bootmgr.efi
|
||||
static_sprintf(path, "%s\\Windows\\Boot\\EFI_EX\\bootmgr_EX.efi", mount_path);
|
||||
static_sprintf(path2, "%c:\\bootmgr.efi", drive_letter);
|
||||
if (!CopyFileU(path, path2, FALSE))
|
||||
uprintf("WARNING: Could not replace 'bootmgr.efi': %s", WindowsErrorString());
|
||||
// Microsoft "secures" the Windows\Boot\ dir through their SUPER OBNOXIOUS AND
|
||||
// WORTHLESS use of DACLs + read-only flags, so we first need to re-take control
|
||||
// of all directories under there recursively.
|
||||
StrArrayCreate(&dirs, 64);
|
||||
StrArrayCreate(&files, 64);
|
||||
static_sprintf(path, "%s\\Windows\\Boot\\", mount_path);
|
||||
StrArrayAdd(&dirs, path, TRUE);
|
||||
static_sprintf(path, "%s\\Windows\\Boot\\EFI_EX\\", mount_path);
|
||||
StrArrayAdd(&dirs, path, TRUE);
|
||||
ListDirectoryContent(&dirs, path, LIST_DIR_TYPE_DIRECTORY | LIST_DIR_TYPE_RECURSIVE);
|
||||
for (i = 0; i < (int)dirs.Index; i++) {
|
||||
rep = remove_substr(dirs.String[i], "_EX");
|
||||
assert(rep != NULL);
|
||||
TakeOwnership(rep);
|
||||
safe_free(rep);
|
||||
}
|
||||
// Now that we should be able to write to the destination directories, copy the content.
|
||||
ListDirectoryContent(&files, path, LIST_DIR_TYPE_FILE | LIST_DIR_TYPE_RECURSIVE);
|
||||
for (i = 0; r && i < (int)files.Index; i++) {
|
||||
rep = remove_substr(files.String[i], "_EX");
|
||||
assert(rep != NULL);
|
||||
TakeOwnership(rep);
|
||||
if (!CopyFileU(files.String[i], rep, FALSE) && rep != NULL)
|
||||
uprintf("WARNING: Could not replace '%s': %s", &rep[strlen(mount_path) + 1], WindowsErrorString());
|
||||
safe_free(rep);
|
||||
}
|
||||
StrArrayDestroy(&dirs);
|
||||
StrArrayDestroy(&files);
|
||||
uprintf("Replaced EFI bootloader files with 'Windows UEFI CA 2023' signed versions");
|
||||
}
|
||||
}
|
||||
|
||||
r = TRUE;
|
||||
|
||||
out:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue