@@ -24,34 +24,21 @@ private static void Main(string[] args)
2424 string ? targetFileDirectory = Path . GetDirectoryName ( targetFilePath ) ;
2525 Debug . Assert ( targetFileDirectory is not null ) ;
2626
27- if ( ! NativeMethods . OpenProcessToken ( NativeMethods . CURRENT_PROCESS_HANDLE , NativeMethods . TOKEN_QUERY | NativeMethods . TOKEN_DUPLICATE , out nint tokenHandle ) )
27+ if ( ! NativeMethods . OpenProcessToken ( NativeMethods . CURRENT_PROCESS_HANDLE , NativeMethods . TOKEN_QUERY , out nint tokenHandleWithTokenQueryPrivilege ) )
2828 {
29- if ( TryExplorerShellExecute ( targetFilePath , targetFileDirectory ) )
30- {
31- return ;
32- }
33-
34- if ( ! Launch ( targetFilePath , targetFileDirectory ) )
35- {
36- ShellExecute ( targetFilePath , targetFileDirectory ) ;
37- }
38-
29+ Launch ( targetFilePath , targetFileDirectory ) ;
3930 return ;
4031 }
4132
4233 try
4334 {
44- if ( ! IsTokenElevated ( tokenHandle ) )
35+ if ( ! IsTokenElevated ( tokenHandleWithTokenQueryPrivilege ) )
4536 {
46- if ( ! Launch ( targetFilePath , targetFileDirectory ) )
47- {
48- ShellExecute ( targetFilePath , targetFileDirectory ) ;
49- }
50-
37+ Launch ( targetFilePath , targetFileDirectory ) ;
5138 return ;
5239 }
5340
54- if ( TryTokenDeElevation ( targetFilePath , targetFileDirectory , tokenHandle ) )
41+ if ( TryTokenDeElevation ( targetFilePath , targetFileDirectory ) )
5542 {
5643 return ;
5744 }
@@ -61,26 +48,23 @@ private static void Main(string[] args)
6148 return ;
6249 }
6350
64- if ( ! Launch ( targetFilePath , targetFileDirectory ) )
65- {
66- ShellExecute ( targetFilePath , targetFileDirectory ) ;
67- }
51+ Launch ( targetFilePath , targetFileDirectory ) ;
6852 }
6953 finally
7054 {
71- if ( tokenHandle is not 0 )
55+ if ( tokenHandleWithTokenQueryPrivilege is not 0 )
7256 {
73- _ = NativeMethods . CloseHandle ( tokenHandle ) ;
57+ _ = NativeMethods . CloseHandle ( tokenHandleWithTokenQueryPrivilege ) ;
7458 }
7559 }
7660 }
7761
78- private static bool IsTokenElevated ( nint tokenHandle )
62+ private static bool IsTokenElevated ( nint tokenHandleWithTokenQueryPrivilege )
7963 {
80- return NativeMethods . GetTokenInformation ( tokenHandle , NativeMethods . TOKEN_INFORMATION_CLASS . TokenElevation , out NativeMethods . TOKEN_ELEVATION elevation , Unsafe . SizeOf < NativeMethods . TOKEN_ELEVATION > ( ) , out _ ) && elevation . TokenIsElevated is not 0 ;
64+ return NativeMethods . GetTokenInformation ( tokenHandleWithTokenQueryPrivilege , NativeMethods . TOKEN_INFORMATION_CLASS . TokenElevation , out NativeMethods . TOKEN_ELEVATION elevation , Unsafe . SizeOf < NativeMethods . TOKEN_ELEVATION > ( ) , out _ ) && elevation . TokenIsElevated is not 0 ;
8165 }
8266
83- private static bool Launch ( string targetFilePath , string targetFileDirectory )
67+ private static void Launch ( string targetFilePath , string targetFileDirectory )
8468 {
8569 NativeMethods . STARTUPINFO startupInfo = new ( )
8670 {
@@ -94,60 +78,89 @@ private static bool Launch(string targetFilePath, string targetFileDirectory)
9478 {
9579 _ = NativeMethods . CloseHandle ( processInformation . hProcess ) ;
9680 _ = NativeMethods . CloseHandle ( processInformation . hThread ) ;
97- return true ;
81+ return ;
9882 }
9983
100- return false ;
84+ // CreateProcessW can't run shortcuts but ShellExecuteW can
85+ ShellExecute ( targetFilePath , targetFileDirectory ) ;
10186 }
10287
103- private static bool TryTokenDeElevation ( string targetFilePath , string targetFileDirectory , nint tokenHandle )
88+ private static bool TryTokenDeElevation ( string targetFilePath , string targetFileDirectory )
10489 {
90+ nint currentProcessTokenHandle = 0 ;
10591 nint newTokenHandle = 0 ;
92+ nint environment = 0 ;
10693 try
10794 {
108- if ( ! NativeMethods . GetTokenInformation ( tokenHandle , NativeMethods . TOKEN_INFORMATION_CLASS . TokenLinkedToken , out NativeMethods . TOKEN_LINKED_TOKEN linkedToken , Unsafe . SizeOf < NativeMethods . TOKEN_LINKED_TOKEN > ( ) , out _ ) )
95+ if ( NativeMethods . OpenProcessToken ( NativeMethods . CURRENT_PROCESS_HANDLE , NativeMethods . TOKEN_QUERY | NativeMethods . TOKEN_DUPLICATE | NativeMethods . TOKEN_ASSIGN_PRIMARY , out currentProcessTokenHandle ) )
10996 {
110- if ( ! NativeMethods . CreateRestrictedToken ( tokenHandle , NativeMethods . LUA_TOKEN , 0 , 0 , 0 , 0 , 0 , 0 , out newTokenHandle ) )
97+ // Requires TOKEN_QUERY
98+ if ( NativeMethods . GetTokenInformation ( currentProcessTokenHandle , NativeMethods . TOKEN_INFORMATION_CLASS . TokenLinkedToken , out NativeMethods . TOKEN_LINKED_TOKEN linkedToken , Unsafe . SizeOf < NativeMethods . TOKEN_LINKED_TOKEN > ( ) , out _ ) )
11199 {
112- return false ;
100+ newTokenHandle = linkedToken . LinkedToken ;
101+ }
102+ else
103+ {
104+ // Requires TOKEN_DUPLICATE
105+ _ = NativeMethods . CreateRestrictedToken ( currentProcessTokenHandle , NativeMethods . LUA_TOKEN , 0 , 0 , 0 , 0 , 0 , 0 , out newTokenHandle ) ;
113106 }
114- }
115- else
116- {
117- newTokenHandle = linkedToken . LinkedToken ;
118- }
119-
120- NativeMethods . STARTUPINFO startupInfo = new ( )
121- {
122- cb = Unsafe . SizeOf < NativeMethods . STARTUPINFO > ( ) ,
123- dwFlags = NativeMethods . STARTF_USESHOWWINDOW ,
124- wShowWindow = NativeMethods . SW_SHOWNORMAL
125- } ;
126107
127- bool success = NativeMethods . CreateProcessWithTokenW ( newTokenHandle , 0 , targetFilePath , null , 0 , 0 , targetFileDirectory , ref startupInfo , out NativeMethods . PROCESS_INFORMATION processInfo ) ;
128- if ( success )
129- {
130- _ = NativeMethods . CloseHandle ( processInfo . hProcess ) ;
131- _ = NativeMethods . CloseHandle ( processInfo . hThread ) ;
108+ if ( newTokenHandle is not 0 )
109+ {
110+ NativeMethods . STARTUPINFO startupInfo = new ( )
111+ {
112+ cb = Unsafe . SizeOf < NativeMethods . STARTUPINFO > ( ) ,
113+ dwFlags = NativeMethods . STARTF_USESHOWWINDOW ,
114+ wShowWindow = NativeMethods . SW_SHOWNORMAL
115+ } ;
116+
117+ environment = NativeMethods . GetEnvironmentStringsW ( ) ;
118+
119+ // Requires TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY and SE_IMPERSONATE_NAME
120+ // We don't need to set AdjustTokenPrivileges since for an elevated process we already have SE_IMPERSONATE_NAME
121+ bool success = NativeMethods . CreateProcessWithTokenW ( newTokenHandle , 0 , targetFilePath , null , NativeMethods . CREATE_UNICODE_ENVIRONMENT , environment , targetFileDirectory , ref startupInfo , out NativeMethods . PROCESS_INFORMATION processInfo ) ;
122+ if ( success )
123+ {
124+ _ = NativeMethods . CloseHandle ( processInfo . hProcess ) ;
125+ _ = NativeMethods . CloseHandle ( processInfo . hThread ) ;
126+ }
127+
128+ return success ;
129+ }
132130 }
133131
134- return success ;
132+ return false ;
135133 }
136134 catch
137135 {
138136 return false ;
139137 }
140138 finally
141139 {
140+ if ( currentProcessTokenHandle is not 0 )
141+ {
142+ _ = NativeMethods . CloseHandle ( currentProcessTokenHandle ) ;
143+ }
144+
142145 if ( newTokenHandle is not 0 )
143146 {
144147 _ = NativeMethods . CloseHandle ( newTokenHandle ) ;
145148 }
149+
150+ if ( environment is not 0 )
151+ {
152+ _ = NativeMethods . FreeEnvironmentStringsW ( environment ) ;
153+ }
146154 }
147155 }
148156
149157 private static bool TryExplorerShellExecute ( string targetFilePath , string targetFileDirectory )
150158 {
159+ if ( NativeMethods . GetShellWindow ( ) is 0 )
160+ {
161+ return false ;
162+ }
163+
151164 try
152165 {
153166 // ReSharper disable once SuspiciousTypeConversion.Global
0 commit comments