@@ -46,83 +46,243 @@ static BOOL IsElevated(void) {
4646/* Attempt to relaunch self as standard user using Explorer's token */
4747static BOOL RelaunchAsStandardUser (void ) {
4848 HWND hShellWnd = GetShellWindow ();
49- if (!hShellWnd ) return FALSE;
49+ if (!hShellWnd ) {
50+ LOG_WARNING ("GetShellWindow() returned NULL, Explorer may not be running." );
51+ return FALSE;
52+ }
5053
51- DWORD dwShellPID ;
54+ DWORD dwShellPID = 0 ;
5255 GetWindowThreadProcessId (hShellWnd , & dwShellPID );
56+ if (dwShellPID == 0 ) {
57+ LOG_WARNING ("Failed to get Explorer PID." );
58+ return FALSE;
59+ }
5360
5461 HANDLE hShellProcess = OpenProcess (PROCESS_QUERY_INFORMATION , FALSE, dwShellPID );
55- if (!hShellProcess ) return FALSE;
62+ if (!hShellProcess ) {
63+ LOG_WARNING ("Failed to open Explorer process, error: %lu" , GetLastError ());
64+ return FALSE;
65+ }
5666
5767 HANDLE hShellToken = NULL ;
5868 if (!OpenProcessToken (hShellProcess , TOKEN_DUPLICATE , & hShellToken )) {
69+ LOG_WARNING ("Failed to open Explorer token, error: %lu" , GetLastError ());
5970 CloseHandle (hShellProcess );
6071 return FALSE;
6172 }
6273
6374 HANDLE hNewToken = NULL ;
6475 /* Duplicate the shell's token (which is medium integrity/standard user) */
6576 if (!DuplicateTokenEx (hShellToken , MAXIMUM_ALLOWED , NULL , SecurityImpersonation , TokenPrimary , & hNewToken )) {
77+ LOG_WARNING ("Failed to duplicate token, error: %lu" , GetLastError ());
6678 CloseHandle (hShellToken );
6779 CloseHandle (hShellProcess );
6880 return FALSE;
6981 }
7082
7183 wchar_t szPath [MAX_PATH ];
7284 GetModuleFileNameW (NULL , szPath , MAX_PATH );
73-
85+
7486 /* Reconstruct command line safely - CreateProcess might modify the buffer */
7587 wchar_t * pszOriginalCmdLine = GetCommandLineW ();
7688 size_t cmdLen = wcslen (pszOriginalCmdLine ) + 1 ;
7789 wchar_t * pszCmdLineCopy = (wchar_t * )malloc (cmdLen * sizeof (wchar_t ));
78-
90+
7991 if (!pszCmdLineCopy ) {
92+ LOG_WARNING ("Failed to allocate command line buffer." );
8093 CloseHandle (hNewToken );
8194 CloseHandle (hShellToken );
8295 CloseHandle (hShellProcess );
8396 return FALSE;
8497 }
85-
98+
8699 wcscpy_s (pszCmdLineCopy , cmdLen , pszOriginalCmdLine );
87-
100+
88101 STARTUPINFOW si = {sizeof (STARTUPINFOW )};
89102 PROCESS_INFORMATION pi = {0 };
90-
103+
91104 BOOL bResult = CreateProcessWithTokenW (hNewToken , LOGON_WITH_PROFILE , NULL , pszCmdLineCopy , 0 , NULL , NULL , & si , & pi );
92105
93106 free (pszCmdLineCopy );
94107
95108 if (bResult ) {
96- LOG_INFO ("Relaunched self as standard user (PID: %lu)" , pi .dwProcessId );
109+ /* Verify the new process is actually running */
110+ DWORD exitCode = 0 ;
111+ if (WaitForSingleObject (pi .hProcess , 100 ) == WAIT_TIMEOUT ) {
112+ /* Process is still running after 100ms - success */
113+ LOG_INFO ("Relaunched self as standard user (PID: %lu)" , pi .dwProcessId );
114+ } else if (GetExitCodeProcess (pi .hProcess , & exitCode ) && exitCode != STILL_ACTIVE ) {
115+ /* Process exited immediately - something went wrong */
116+ LOG_WARNING ("New process exited immediately with code: %lu" , exitCode );
117+ bResult = FALSE;
118+ }
97119 CloseHandle (pi .hProcess );
98120 CloseHandle (pi .hThread );
99121 } else {
100- LOG_ERROR ( "Failed to relaunch as standard user , error: %lu" , GetLastError ());
122+ LOG_WARNING ( "CreateProcessWithTokenW failed , error: %lu" , GetLastError ());
101123 }
102124
103125 CloseHandle (hNewToken );
104126 CloseHandle (hShellToken );
105127 CloseHandle (hShellProcess );
106-
128+
107129 return bResult ;
108130}
109131
110- /* Drop Administrator privileges if present to ensure Drag & Drop works from Explorer */
111- static void DropPrivileges (void ) {
112- if (IsElevated ()) {
113- LOG_INFO ("Elevated privileges detected. Attempting to switch to standard user..." );
114-
115- /* Prevent infinite loop if relaunch fails or we are genuinely the admin user (e.g. built-in Admin) */
116- /* But here we just try once. If it succeeds, we exit. */
117-
118- if (RelaunchAsStandardUser ()) {
119- /* Exit this elevated instance */
120- ExitProcess (0 );
132+ /* Check if UAC is enabled - returns FALSE if disabled or uncertain */
133+ static BOOL IsUACEnabled (void ) {
134+ HKEY hKey ;
135+ DWORD dwValue = 0 ; /* Default to DISABLED for safety - skip privilege drop if uncertain */
136+ DWORD dwSize = sizeof (DWORD );
137+
138+ LSTATUS status = RegOpenKeyExW (HKEY_LOCAL_MACHINE ,
139+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System" ,
140+ 0 , KEY_READ , & hKey );
141+
142+ if (status != ERROR_SUCCESS ) {
143+ LOG_WARNING ("Cannot read UAC registry key (error: %ld), assuming UAC disabled for safety" , status );
144+ return FALSE;
145+ }
146+
147+ status = RegQueryValueExW (hKey , L"EnableLUA" , NULL , NULL , (LPBYTE )& dwValue , & dwSize );
148+ RegCloseKey (hKey );
149+
150+ if (status != ERROR_SUCCESS ) {
151+ LOG_WARNING ("Cannot read EnableLUA value (error: %ld), assuming UAC disabled for safety" , status );
152+ return FALSE;
153+ }
154+
155+ LOG_INFO ("UAC EnableLUA registry value: %lu" , dwValue );
156+ return dwValue != 0 ;
157+ }
158+
159+ /* Check if Secondary Logon service is running - required for CreateProcessWithTokenW */
160+ static BOOL IsSecondaryLogonServiceRunning (void ) {
161+ SC_HANDLE hSCManager = OpenSCManagerW (NULL , NULL , SC_MANAGER_CONNECT );
162+ if (!hSCManager ) {
163+ LOG_WARNING ("Cannot open SCManager (error: %lu), assuming service unavailable" , GetLastError ());
164+ return FALSE;
165+ }
166+
167+ SC_HANDLE hService = OpenServiceW (hSCManager , L"seclogon" , SERVICE_QUERY_STATUS );
168+ if (!hService ) {
169+ DWORD err = GetLastError ();
170+ CloseServiceHandle (hSCManager );
171+ if (err == ERROR_SERVICE_DOES_NOT_EXIST ) {
172+ LOG_WARNING ("Secondary Logon service does not exist on this system" );
121173 } else {
122- LOG_WARNING ("Failed to switch to standard user. Drag & Drop may be restricted." );
174+ LOG_WARNING ("Cannot open Secondary Logon service (error: %lu)" , err );
123175 }
176+ return FALSE;
177+ }
178+
179+ SERVICE_STATUS status ;
180+ BOOL bRunning = FALSE;
181+ if (QueryServiceStatus (hService , & status )) {
182+ bRunning = (status .dwCurrentState == SERVICE_RUNNING );
183+ LOG_INFO ("Secondary Logon service state: %lu (running=%s)" ,
184+ status .dwCurrentState , bRunning ? "yes" : "no" );
124185 } else {
186+ LOG_WARNING ("Cannot query Secondary Logon service status (error: %lu)" , GetLastError ());
187+ }
188+
189+ CloseServiceHandle (hService );
190+ CloseServiceHandle (hSCManager );
191+ return bRunning ;
192+ }
193+
194+ /* Check if running on Windows Server edition */
195+ static BOOL IsWindowsServer (void ) {
196+ OSVERSIONINFOEXW osvi = {sizeof (OSVERSIONINFOEXW )};
197+ DWORDLONG dwlConditionMask = 0 ;
198+
199+ VER_SET_CONDITION (dwlConditionMask , VER_PRODUCT_TYPE , VER_EQUAL );
200+ osvi .wProductType = VER_NT_WORKSTATION ;
201+
202+ /* If this is NOT a workstation, it's a server */
203+ BOOL bIsWorkstation = VerifyVersionInfoW (& osvi , VER_PRODUCT_TYPE , dwlConditionMask );
204+
205+ if (!bIsWorkstation ) {
206+ LOG_INFO ("Detected Windows Server edition" );
207+ return TRUE;
208+ }
209+ return FALSE;
210+ }
211+
212+ /* Check if Explorer (shell) is running as elevated */
213+ static BOOL IsShellElevated (void ) {
214+ HWND hShellWnd = GetShellWindow ();
215+ if (!hShellWnd ) return TRUE; /* Assume elevated if no shell */
216+
217+ DWORD dwShellPID = 0 ;
218+ GetWindowThreadProcessId (hShellWnd , & dwShellPID );
219+ if (dwShellPID == 0 ) return TRUE;
220+
221+ HANDLE hShellProcess = OpenProcess (PROCESS_QUERY_INFORMATION , FALSE, dwShellPID );
222+ if (!hShellProcess ) return TRUE;
223+
224+ HANDLE hShellToken = NULL ;
225+ BOOL bShellElevated = TRUE; /* Default to TRUE (skip privilege drop) */
226+
227+ if (OpenProcessToken (hShellProcess , TOKEN_QUERY , & hShellToken )) {
228+ TOKEN_ELEVATION elevation ;
229+ DWORD cbSize = sizeof (TOKEN_ELEVATION );
230+ if (GetTokenInformation (hShellToken , TokenElevation , & elevation , sizeof (elevation ), & cbSize )) {
231+ bShellElevated = elevation .TokenIsElevated ;
232+ }
233+ CloseHandle (hShellToken );
234+ }
235+
236+ CloseHandle (hShellProcess );
237+ return bShellElevated ;
238+ }
239+
240+ /* Drop Administrator privileges if present to ensure Drag & Drop works from Explorer */
241+ static void DropPrivileges (void ) {
242+ /* CRITICAL: This function must NEVER prevent the application from running.
243+ * All checks are designed to fail-safe (skip privilege drop on any uncertainty).
244+ */
245+
246+ if (!IsElevated ()) {
125247 LOG_INFO ("Running as standard user (optimal for Drag & Drop)." );
248+ return ;
249+ }
250+
251+ LOG_INFO ("Elevated privileges detected. Checking if privilege drop is safe..." );
252+
253+ /* Check 1: Windows Server - often has different security policies */
254+ if (IsWindowsServer ()) {
255+ LOG_INFO ("Running on Windows Server, skipping privilege drop for compatibility." );
256+ return ;
257+ }
258+
259+ /* Check 2: UAC disabled - privilege drop is meaningless and may cause issues */
260+ if (!IsUACEnabled ()) {
261+ LOG_INFO ("UAC is disabled, skipping privilege drop (Drag & Drop may be restricted)." );
262+ return ;
263+ }
264+
265+ /* Check 3: Secondary Logon service - required for CreateProcessWithTokenW */
266+ if (!IsSecondaryLogonServiceRunning ()) {
267+ LOG_INFO ("Secondary Logon service not running, skipping privilege drop." );
268+ return ;
269+ }
270+
271+ /* Check 4: Explorer itself is elevated - privilege drop would be pointless */
272+ if (IsShellElevated ()) {
273+ LOG_INFO ("Explorer is also elevated, skipping privilege drop." );
274+ return ;
275+ }
276+
277+ LOG_INFO ("All preconditions met. Attempting to switch to standard user..." );
278+
279+ if (RelaunchAsStandardUser ()) {
280+ LOG_INFO ("Relaunch successful, exiting elevated instance." );
281+ /* Exit this elevated instance */
282+ ExitProcess (0 );
283+ } else {
284+ LOG_WARNING ("Failed to switch to standard user. Continuing with elevated privileges." );
285+ LOG_WARNING ("Drag & Drop from Explorer may be restricted." );
126286 }
127287}
128288
0 commit comments