1 /******************************************************************************
4 * Copyright (c) 2022, longpanda <admin@ventoy.net>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 3 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
27 #include <VersionHelpers.h>
31 #define PROMPT_MAX 1024
32 typedef struct VTOY_MUI_DATA
34 WCHAR Prompt
[PROMPT_MAX
];
35 struct VTOY_MUI_DATA
* pNext
;
38 static VTOY_MUI_DATA
* g_MuiDataHead
= NULL
;
39 static FILETIME g_SetupMonStartTime
;
54 //sz_Or_Ord windowClass;
55 //WCHAR title[titleLen];
60 //WCHAR typeface[stringLen];
72 // sz_Or_Ord windowClass;
80 #define wchar_to_utf8_no_alloc(wsrc, dest, dest_size) \
81 WideCharToMultiByte(CP_UTF8, 0, wsrc, -1, dest, dest_size, NULL, NULL)
82 #define utf8_to_wchar_no_alloc(src, wdest, wdest_size) \
83 MultiByteToWideChar(CP_UTF8, 0, src, -1, wdest, wdest_size)
85 #define VTOY_DLG_SKIP_SZ_ORD(pWord) \
87 if (*pWord == 0x0000)\
91 else if (*pWordData == 0xFFFF)\
104 #define VTOY_DLG_SKIP_SZ(pWord) \
114 static BOOL
LoadSetupRebootDialogPrompt(HMODULE hMui
, DWORD ID
, WCHAR
* Prompt
, DWORD Len
)
118 WORD
* pWordData
= NULL
;
120 HGLOBAL hTemplate
= NULL
;
121 DLGTEMPLATEEX
* pDlgTempEx
= NULL
;
122 DLGITEMTEMPLATEEX
* pDlgItemTempEx
= NULL
;
124 hDlg
= FindResource(hMui
, MAKEINTRESOURCE(ID
), RT_DIALOG
);
130 hTemplate
= LoadResource(hMui
, hDlg
);
136 pDlgTempEx
= (DLGTEMPLATEEX
*)LockResource(hTemplate
);
142 if (pDlgTempEx
->signature
!= 0xFFFF)
147 pWordData
= (WORD
*)(pDlgTempEx
+ 1);
150 VTOY_DLG_SKIP_SZ_ORD(pWordData
);
153 VTOY_DLG_SKIP_SZ_ORD(pWordData
);
156 VTOY_DLG_SKIP_SZ(pWordData
);
158 //skip pointsize/weight/italic + charset
162 VTOY_DLG_SKIP_SZ(pWordData
);
165 Addr
= (UINT64
)pWordData
;
168 Addr
+= 4 - (Addr
% 4);
169 pWordData
= (WORD
*)Addr
;
173 pDlgItemTempEx
= (DLGITEMTEMPLATEEX
*)pWordData
;
175 if (pDlgItemTempEx
->id
!= 1023)
180 pWordData
= (WORD
*)(pDlgItemTempEx
+ 1);
183 VTOY_DLG_SKIP_SZ_ORD(pWordData
);
185 for (i
= 0; i
< Len
; i
++)
187 Prompt
[i
] = pWordData
[i
];
197 static int LoadPromptString(const char* szDir
)
201 CHAR MuiFile
[MAX_PATH
];
202 WCHAR Prompt
[PROMPT_MAX
];
203 VTOY_MUI_DATA
* Node
= NULL
;
205 sprintf_s(MuiFile
, sizeof(MuiFile
), "X:\\Sources\\%s\\w32uires.dll.mui", szDir
);
207 hMui
= LoadLibraryA(MuiFile
);
210 Log("Failed to loadlibrary <%s> %u", MuiFile
, LASTERR
);
214 bRet
= LoadSetupRebootDialogPrompt(hMui
, 103, Prompt
, PROMPT_MAX
);
217 Node
= malloc(sizeof(VTOY_MUI_DATA
));
220 memset(Node
, 0, sizeof(VTOY_MUI_DATA
));
221 memcpy(Node
->Prompt
, Prompt
, sizeof(Prompt
));
222 if (g_MuiDataHead
== NULL
)
224 g_MuiDataHead
= Node
;
228 Node
->pNext
= g_MuiDataHead
;
229 g_MuiDataHead
= Node
;
232 Log("Successfully add prompt string for <%s>", szDir
);
237 Log("Failed to load prompt string from %s", szDir
);
244 static int FindAllPromptStrings(void)
247 WIN32_FIND_DATAA FindData
;
249 hFind
= FindFirstFileA("X:\\Sources\\*", &FindData
);
250 if (hFind
== INVALID_HANDLE_VALUE
)
252 Log("FindFirstFileA failed %u", LASTERR
);
258 if ((FindData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) && (strlen(FindData
.cFileName
) == 5))
260 if (IsFileExist("X:\\Sources\\%s\\w32uires.dll.mui", FindData
.cFileName
))
262 LoadPromptString(FindData
.cFileName
);
265 } while (FindNextFileA(hFind
, &FindData
));
270 static BOOL CALLBACK
SetupPromptCallback(HWND hWnd
, LPARAM lParam
)
272 VTOY_MUI_DATA
* Node
= NULL
;
273 WCHAR Prompt
[PROMPT_MAX
] = { 0 };
274 BOOL
* found
= (BOOL
*)lParam
;
276 if (GetWindowTextW(hWnd
, Prompt
, PROMPT_MAX
- 1) > 0)
278 for (Node
= g_MuiDataHead
; Node
; Node
= Node
->pNext
)
280 if (wcscmp(Node
->Prompt
, Prompt
) == 0)
291 static BOOL CALLBACK
EnumWindowsProcFunc(HWND hWnd
, LPARAM lParam
)
293 EnumChildWindows(hWnd
, SetupPromptCallback
, lParam
);
298 static CHAR
FindWindowsInstallDstDrive(void)
303 CHAR ChkFilePath
[MAX_PATH
];
305 UINT64 Time1
, Time2
, Time3
;
308 SYSTEMTIME cur_systime
;
310 GetSystemTime(&cur_systime
);
311 SystemTimeToFileTime(&cur_systime
, &sysftime
);
313 memcpy(&Time1
, &g_SetupMonStartTime
, 8);
314 memcpy(&Time2
, &sysftime
, 8);
316 Drives
= GetLogicalDrives();
317 Log("Find Windows: Logical Drives: 0x%x", Drives
);
319 while ((Drives
> 0) && (ret
== 0))
321 if ((Drives
& 1) && Drv
!= 'X')
323 sprintf_s(ChkFilePath
, sizeof(ChkFilePath
), "%C:\\$WINDOWS.~BT\\Sources\\Panther\\setupact.log", Drv
);
324 hFile
= CreateFileA(ChkFilePath
, FILE_READ_EA
, FILE_SHARE_READ
, 0, OPEN_EXISTING
, 0, 0);
325 if (hFile
!= INVALID_HANDLE_VALUE
)
327 Log("%s Exist", ChkFilePath
);
329 if (GetFileTime(hFile
, NULL
, NULL
, &fTime
))
331 memcpy(&Time3
, &fTime
, 8);
333 Log("### %s %llu %llu %llu", ChkFilePath
, Time1
, Time2
, Time3
);
335 if (Time3
> Time1
&& Time3
< Time2
)
337 Log("Detect Windows11 install drive:<%C>", Drv
);
345 Log("%s NOT Exist %u", ChkFilePath
, LASTERR
);
356 static int AddBypassNROReg(const CHAR
* OfflineRegPath
)
367 TOKEN_PRIVILEGES
* p
= NULL
;
369 Log("AddBypassNROReg<%s>", OfflineRegPath
);
371 if (!IsFileExist(OfflineRegPath
))
376 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES
, &htoken
))
378 Log("Open process token failed! %u", LASTERR
);
382 s
= sizeof(TOKEN_PRIVILEGES
) + 2 * sizeof(LUID_AND_ATTRIBUTES
);
383 p
= (PTOKEN_PRIVILEGES
)malloc(s
);
386 Log("Failed to alloc privileges memory");
390 if (!LookupPrivilegeValue(NULL
, SE_DEBUG_NAME
, &(p
->Privileges
[0].Luid
)) ||
391 !LookupPrivilegeValue(NULL
, SE_BACKUP_NAME
, &(p
->Privileges
[1].Luid
)) ||
392 !LookupPrivilegeValue(NULL
, SE_RESTORE_NAME
, &(p
->Privileges
[2].Luid
)))
394 Log("LookupPrivilegeValue failed!");
398 p
->PrivilegeCount
= 3;
399 p
->Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
400 p
->Privileges
[1].Attributes
= SE_PRIVILEGE_ENABLED
;
401 p
->Privileges
[2].Attributes
= SE_PRIVILEGE_ENABLED
;
403 if (!AdjustTokenPrivileges(htoken
, FALSE
, p
, s
, NULL
, NULL
) || GetLastError() != ERROR_SUCCESS
)
405 Log("AdjustTokenPrivileges failed!");
409 Log("AdjustTokenPrivileges success");
411 Status
= RegLoadKeyA(HKEY_LOCAL_MACHINE
, "VTOYNEWSW", OfflineRegPath
);
412 if (Status
!= ERROR_SUCCESS
)
414 Log("RegLoadKey Failed 0x%x", Status
);
419 Status
= RegCreateKeyExA(HKEY_LOCAL_MACHINE
, "VTOYNEWSW\\Microsoft\\Windows\\CurrentVersion", 0, NULL
, REG_OPTION_BACKUP_RESTORE
, KEY_ALL_ACCESS
, NULL
, &hKey
, &dwSize
);
420 if (ERROR_SUCCESS
!= Status
)
422 Log("Failed to create reg key VTOYNEWSW\\Microsoft\\Windows\\CurrentVersion %u %u", LASTERR
, Status
);
426 Status
= RegCreateKeyExA(hKey
, "OOBE", 0, NULL
, 0, KEY_SET_VALUE
| KEY_QUERY_VALUE
| KEY_CREATE_SUB_KEY
, NULL
, &hSubKey
, &dwSize
);
427 if (ERROR_SUCCESS
!= Status
)
429 Log("Failed to create OOBE reg %u %u", LASTERR
, Status
);
433 Status
= RegSetValueExA(hSubKey
, "BypassNRO", 0, REG_DWORD
, (LPBYTE
)&dwValue
, sizeof(DWORD
));
434 Log("Create BypassNRO registry %s %u", (Status
== ERROR_SUCCESS
) ? "SUCCESS" : "FAILED", Status
);
436 Status
= RegFlushKey(hSubKey
);
437 Status
+= RegCloseKey(hSubKey
);
438 Log("Flush and close subkey %s %u", (Status
== ERROR_SUCCESS
) ? "SUCCESS" : "FAILED", Status
);
440 Status
= RegFlushKey(hKey
);
441 Status
+= RegCloseKey(hKey
);
442 Log("Flush and close key %s %u", (Status
== ERROR_SUCCESS
) ? "SUCCESS" : "FAILED", Status
);
449 Status
= RegUnLoadKeyA(HKEY_LOCAL_MACHINE
, "VTOYNEWSW");
450 Log("RegUnLoadKey %s %u", (Status
== ERROR_SUCCESS
) ? "SUCCESS" : "FAILED", Status
);
461 static DWORD WINAPI
WaitSetupFinishThread(void* Param
)
465 CHAR OfflineRegPath
[MAX_PATH
];
470 EnumWindows(EnumWindowsProcFunc
, (LPARAM
)&Found
);
473 Log("### Setup finish detected");
475 // Add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE\BypassNRO
476 Drv
= FindWindowsInstallDstDrive();
477 Log("Find Windows install drive %d", Drv
);
480 sprintf_s(OfflineRegPath
, sizeof(OfflineRegPath
), "%C:\\Windows\\system32\\config\\SOFTWARE", Drv
);
481 AddBypassNROReg(OfflineRegPath
);
487 int SetupMonNroStart(const char *isopath
)
491 Log("SetupMonNroStart ...");
493 FindAllPromptStrings();
497 Log("Prompt not found, add default");
498 g_MuiDataHead
= malloc(sizeof(VTOY_MUI_DATA
));
501 wcscpy_s(g_MuiDataHead
->Prompt
, PROMPT_MAX
, L
"Windows needs to restart to continue");
502 g_MuiDataHead
->pNext
= NULL
;
506 Log("Wait for setup finish...");
507 GetSystemTime(&systime
);
508 SystemTimeToFileTime(&systime
, &g_SetupMonStartTime
);
510 CreateThread(NULL
, 0, WaitSetupFinishThread
, NULL
, 0, NULL
);