StephaneLenclud@436
|
1 |
#include "UserAccountControl.h"
|
StephaneLenclud@436
|
2 |
|
StephaneLenclud@436
|
3 |
#include <windows.h>
|
StephaneLenclud@436
|
4 |
#pragma comment (lib, "kernel32.lib")
|
StephaneLenclud@436
|
5 |
#pragma comment (lib, "advapi32.lib")
|
StephaneLenclud@436
|
6 |
|
StephaneLenclud@436
|
7 |
#include <vcclr.h>
|
StephaneLenclud@436
|
8 |
#include <msclr\marshal.h>
|
StephaneLenclud@436
|
9 |
#include <msclr\marshal_windows.h>
|
StephaneLenclud@436
|
10 |
|
StephaneLenclud@436
|
11 |
using namespace msclr::interop;
|
StephaneLenclud@436
|
12 |
|
StephaneLenclud@436
|
13 |
using namespace System::ComponentModel;
|
StephaneLenclud@436
|
14 |
using namespace Microsoft::Win32;
|
StephaneLenclud@436
|
15 |
|
StephaneLenclud@436
|
16 |
namespace UacHelpers {
|
StephaneLenclud@436
|
17 |
|
StephaneLenclud@436
|
18 |
Process^ UserAccountControl::CreateProcessAsAdmin(System::String^ exePath, System::String^ arguments)
|
StephaneLenclud@436
|
19 |
{
|
StephaneLenclud@436
|
20 |
ProcessStartInfo^ psi = gcnew ProcessStartInfo(exePath, arguments);
|
StephaneLenclud@436
|
21 |
psi->UseShellExecute = true;
|
StephaneLenclud@436
|
22 |
psi->Verb = "runas";
|
StephaneLenclud@436
|
23 |
return Process::Start(psi);
|
StephaneLenclud@436
|
24 |
}
|
StephaneLenclud@436
|
25 |
|
StephaneLenclud@436
|
26 |
Process^ UserAccountControl::CreateProcessAsStandardUser(System::String^ exePath, System::String^ arguments)
|
StephaneLenclud@436
|
27 |
{
|
StephaneLenclud@436
|
28 |
marshal_context context;
|
StephaneLenclud@436
|
29 |
|
StephaneLenclud@436
|
30 |
//If the current process is not elevated, then there's no reason to go through the hassle --
|
StephaneLenclud@436
|
31 |
//just use the standard System.Diagnostics.Process facilities.
|
StephaneLenclud@436
|
32 |
if (!IsCurrentProcessElevated)
|
StephaneLenclud@436
|
33 |
{
|
StephaneLenclud@436
|
34 |
return Process::Start(exePath, arguments);
|
StephaneLenclud@436
|
35 |
}
|
StephaneLenclud@436
|
36 |
|
StephaneLenclud@436
|
37 |
//The following implementation is roughly based on Aaron Margosis' post:
|
StephaneLenclud@436
|
38 |
//http://blogs.msdn.com/aaron_margosis/archive/2009/06/06/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app.aspx
|
StephaneLenclud@436
|
39 |
|
StephaneLenclud@436
|
40 |
//Enable SeIncreaseQuotaPrivilege in this process. (This requires administrative privileges.)
|
StephaneLenclud@436
|
41 |
HANDLE hProcessToken = NULL;
|
StephaneLenclud@436
|
42 |
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
|
StephaneLenclud@436
|
43 |
{
|
StephaneLenclud@436
|
44 |
throw gcnew Win32Exception(GetLastError());
|
StephaneLenclud@436
|
45 |
}
|
StephaneLenclud@436
|
46 |
else
|
StephaneLenclud@436
|
47 |
{
|
StephaneLenclud@436
|
48 |
TOKEN_PRIVILEGES tkp;
|
StephaneLenclud@436
|
49 |
tkp.PrivilegeCount = 1;
|
StephaneLenclud@436
|
50 |
LookupPrivilegeValueW(NULL, SE_INCREASE_QUOTA_NAME, &tkp.Privileges[0].Luid);
|
StephaneLenclud@436
|
51 |
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
StephaneLenclud@436
|
52 |
AdjustTokenPrivileges(hProcessToken, FALSE, &tkp, 0, NULL, NULL);
|
StephaneLenclud@436
|
53 |
DWORD dwLastErr = GetLastError();
|
StephaneLenclud@436
|
54 |
CloseHandle(hProcessToken);
|
StephaneLenclud@436
|
55 |
if (ERROR_SUCCESS != dwLastErr)
|
StephaneLenclud@436
|
56 |
{
|
StephaneLenclud@436
|
57 |
throw gcnew Win32Exception(dwLastErr);
|
StephaneLenclud@436
|
58 |
}
|
StephaneLenclud@436
|
59 |
}
|
StephaneLenclud@436
|
60 |
|
StephaneLenclud@436
|
61 |
//Get window handle representing the desktop shell. This might not work if there is no shell window, or when
|
StephaneLenclud@436
|
62 |
//using a custom shell. Also note that we're assuming that the shell is not running elevated.
|
StephaneLenclud@436
|
63 |
HWND hShellWnd = GetShellWindow();
|
StephaneLenclud@436
|
64 |
if (hShellWnd == NULL)
|
StephaneLenclud@436
|
65 |
{
|
StephaneLenclud@436
|
66 |
throw gcnew System::InvalidOperationException("Unable to locate shell window; you might be using a custom shell");
|
StephaneLenclud@436
|
67 |
}
|
StephaneLenclud@436
|
68 |
|
StephaneLenclud@436
|
69 |
//Get the ID of the desktop shell process.
|
StephaneLenclud@436
|
70 |
DWORD dwShellPID;
|
StephaneLenclud@436
|
71 |
GetWindowThreadProcessId(hShellWnd, &dwShellPID);
|
StephaneLenclud@436
|
72 |
if (dwShellPID == 0)
|
StephaneLenclud@436
|
73 |
{
|
StephaneLenclud@436
|
74 |
throw gcnew Win32Exception(GetLastError());
|
StephaneLenclud@436
|
75 |
}
|
StephaneLenclud@436
|
76 |
|
StephaneLenclud@436
|
77 |
//Open the desktop shell process in order to get the process token.
|
StephaneLenclud@436
|
78 |
HANDLE hShellProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwShellPID);
|
StephaneLenclud@436
|
79 |
if (hShellProcess == NULL)
|
StephaneLenclud@436
|
80 |
{
|
StephaneLenclud@436
|
81 |
throw gcnew Win32Exception(GetLastError());
|
StephaneLenclud@436
|
82 |
}
|
StephaneLenclud@436
|
83 |
|
StephaneLenclud@436
|
84 |
HANDLE hShellProcessToken = NULL;
|
StephaneLenclud@436
|
85 |
HANDLE hPrimaryToken = NULL;
|
StephaneLenclud@436
|
86 |
try
|
StephaneLenclud@436
|
87 |
{
|
StephaneLenclud@436
|
88 |
//Get the process token of the desktop shell.
|
StephaneLenclud@436
|
89 |
if (!OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, &hShellProcessToken))
|
StephaneLenclud@436
|
90 |
{
|
StephaneLenclud@436
|
91 |
throw gcnew Win32Exception(GetLastError());
|
StephaneLenclud@436
|
92 |
}
|
StephaneLenclud@436
|
93 |
|
StephaneLenclud@436
|
94 |
//Duplicate the shell's process token to get a primary token.
|
StephaneLenclud@436
|
95 |
const DWORD dwTokenRights = TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID;
|
StephaneLenclud@436
|
96 |
if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken))
|
StephaneLenclud@436
|
97 |
{
|
StephaneLenclud@436
|
98 |
throw gcnew Win32Exception(GetLastError());
|
StephaneLenclud@436
|
99 |
}
|
StephaneLenclud@436
|
100 |
|
StephaneLenclud@436
|
101 |
//Start the target process with the new token.
|
StephaneLenclud@436
|
102 |
STARTUPINFO si = {0}; si.cb = sizeof(si);
|
StephaneLenclud@436
|
103 |
PROCESS_INFORMATION pi = {0};
|
StephaneLenclud@436
|
104 |
if (!CreateProcessWithTokenW(hPrimaryToken, 0,
|
StephaneLenclud@436
|
105 |
context.marshal_as<LPCWSTR>(exePath), context.marshal_as<LPWSTR>(exePath + " " + arguments),
|
StephaneLenclud@436
|
106 |
0, NULL, NULL, &si, &pi))
|
StephaneLenclud@436
|
107 |
{
|
StephaneLenclud@436
|
108 |
throw gcnew Win32Exception(GetLastError());
|
StephaneLenclud@436
|
109 |
}
|
StephaneLenclud@436
|
110 |
CloseHandle(pi.hProcess);
|
StephaneLenclud@436
|
111 |
CloseHandle(pi.hThread);
|
StephaneLenclud@436
|
112 |
|
StephaneLenclud@436
|
113 |
return Process::GetProcessById(pi.dwProcessId);
|
StephaneLenclud@436
|
114 |
}
|
StephaneLenclud@436
|
115 |
finally
|
StephaneLenclud@436
|
116 |
{
|
StephaneLenclud@436
|
117 |
if (hShellProcessToken != NULL)
|
StephaneLenclud@436
|
118 |
CloseHandle(hShellProcessToken);
|
StephaneLenclud@436
|
119 |
|
StephaneLenclud@436
|
120 |
if (hPrimaryToken != NULL)
|
StephaneLenclud@436
|
121 |
CloseHandle(hPrimaryToken);
|
StephaneLenclud@436
|
122 |
|
StephaneLenclud@436
|
123 |
if (hShellProcess != NULL)
|
StephaneLenclud@436
|
124 |
CloseHandle(hShellProcess);
|
StephaneLenclud@436
|
125 |
}
|
StephaneLenclud@436
|
126 |
}
|
StephaneLenclud@436
|
127 |
|
StephaneLenclud@436
|
128 |
bool UserAccountControl::IsUserAdmin::get()
|
StephaneLenclud@436
|
129 |
{
|
StephaneLenclud@436
|
130 |
if (UserAccountControl::IsUacEnabled)
|
StephaneLenclud@436
|
131 |
return GetProcessTokenElevationType() != TokenElevationTypeDefault; //split token
|
StephaneLenclud@436
|
132 |
|
StephaneLenclud@436
|
133 |
//If UAC is off, we can't rely on the token; check for Admin group.
|
StephaneLenclud@436
|
134 |
return WindowsPrincipal(WindowsIdentity::GetCurrent()).IsInRole("Administrators");
|
StephaneLenclud@436
|
135 |
}
|
StephaneLenclud@436
|
136 |
|
StephaneLenclud@436
|
137 |
bool UserAccountControl::IsUacEnabled::get()
|
StephaneLenclud@436
|
138 |
{
|
StephaneLenclud@436
|
139 |
//Check the HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA registry value.
|
StephaneLenclud@436
|
140 |
RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, false);
|
StephaneLenclud@436
|
141 |
return key->GetValue(UacRegistryValue)->Equals(1);
|
StephaneLenclud@436
|
142 |
}
|
StephaneLenclud@436
|
143 |
|
StephaneLenclud@436
|
144 |
void UserAccountControl::DisableUac()
|
StephaneLenclud@436
|
145 |
{
|
StephaneLenclud@436
|
146 |
SetUacRegistryValue(false);
|
StephaneLenclud@436
|
147 |
}
|
StephaneLenclud@436
|
148 |
|
StephaneLenclud@436
|
149 |
void UserAccountControl::DisableUacAndRestartWindows()
|
StephaneLenclud@436
|
150 |
{
|
StephaneLenclud@436
|
151 |
DisableUac();
|
StephaneLenclud@436
|
152 |
RestartWindows();
|
StephaneLenclud@436
|
153 |
}
|
StephaneLenclud@436
|
154 |
|
StephaneLenclud@436
|
155 |
void UserAccountControl::EnableUac()
|
StephaneLenclud@436
|
156 |
{
|
StephaneLenclud@436
|
157 |
SetUacRegistryValue(true);
|
StephaneLenclud@436
|
158 |
}
|
StephaneLenclud@436
|
159 |
|
StephaneLenclud@436
|
160 |
void UserAccountControl::EnableUacAndRestartWindows()
|
StephaneLenclud@436
|
161 |
{
|
StephaneLenclud@436
|
162 |
EnableUac();
|
StephaneLenclud@436
|
163 |
RestartWindows();
|
StephaneLenclud@436
|
164 |
}
|
StephaneLenclud@436
|
165 |
|
StephaneLenclud@436
|
166 |
bool UserAccountControl::IsCurrentProcessElevated::get()
|
StephaneLenclud@436
|
167 |
{
|
StephaneLenclud@436
|
168 |
return GetProcessTokenElevationType() == TokenElevationTypeFull; //elevated
|
StephaneLenclud@436
|
169 |
}
|
StephaneLenclud@436
|
170 |
|
StephaneLenclud@436
|
171 |
bool UserAccountControl::IsCurrentProcessVirtualized::get()
|
StephaneLenclud@436
|
172 |
{
|
StephaneLenclud@436
|
173 |
HANDLE hToken;
|
StephaneLenclud@436
|
174 |
try
|
StephaneLenclud@436
|
175 |
{
|
StephaneLenclud@436
|
176 |
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
StephaneLenclud@436
|
177 |
throw gcnew Win32Exception(GetLastError());
|
StephaneLenclud@436
|
178 |
|
StephaneLenclud@436
|
179 |
DWORD virtualizationEnabled;
|
StephaneLenclud@436
|
180 |
DWORD dwSize;
|
StephaneLenclud@436
|
181 |
if (!GetTokenInformation(hToken, TokenVirtualizationEnabled, &virtualizationEnabled, sizeof(virtualizationEnabled), &dwSize))
|
StephaneLenclud@436
|
182 |
throw gcnew Win32Exception(GetLastError());
|
StephaneLenclud@436
|
183 |
|
StephaneLenclud@436
|
184 |
return virtualizationEnabled != 0;
|
StephaneLenclud@436
|
185 |
}
|
StephaneLenclud@436
|
186 |
finally
|
StephaneLenclud@436
|
187 |
{
|
StephaneLenclud@436
|
188 |
CloseHandle(hToken);
|
StephaneLenclud@436
|
189 |
}
|
StephaneLenclud@436
|
190 |
}
|
StephaneLenclud@436
|
191 |
|
StephaneLenclud@436
|
192 |
int UserAccountControl::GetProcessTokenElevationType()
|
StephaneLenclud@436
|
193 |
{
|
StephaneLenclud@436
|
194 |
HANDLE hToken;
|
StephaneLenclud@436
|
195 |
try
|
StephaneLenclud@436
|
196 |
{
|
StephaneLenclud@436
|
197 |
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
StephaneLenclud@436
|
198 |
throw gcnew Win32Exception(GetLastError());
|
StephaneLenclud@436
|
199 |
|
StephaneLenclud@436
|
200 |
TOKEN_ELEVATION_TYPE elevationType;
|
StephaneLenclud@436
|
201 |
DWORD dwSize;
|
StephaneLenclud@436
|
202 |
if (!GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(elevationType), &dwSize))
|
StephaneLenclud@436
|
203 |
throw gcnew Win32Exception(GetLastError());
|
StephaneLenclud@436
|
204 |
|
StephaneLenclud@436
|
205 |
return elevationType;
|
StephaneLenclud@436
|
206 |
}
|
StephaneLenclud@436
|
207 |
finally
|
StephaneLenclud@436
|
208 |
{
|
StephaneLenclud@436
|
209 |
CloseHandle(hToken);
|
StephaneLenclud@436
|
210 |
}
|
StephaneLenclud@436
|
211 |
}
|
StephaneLenclud@436
|
212 |
|
StephaneLenclud@436
|
213 |
void UserAccountControl::SetUacRegistryValue(bool enabled)
|
StephaneLenclud@436
|
214 |
{
|
StephaneLenclud@436
|
215 |
RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, true);
|
StephaneLenclud@436
|
216 |
key->SetValue(UacRegistryValue, enabled ? 1 : 0);
|
StephaneLenclud@436
|
217 |
}
|
StephaneLenclud@436
|
218 |
|
StephaneLenclud@436
|
219 |
void UserAccountControl::RestartWindows()
|
StephaneLenclud@436
|
220 |
{
|
StephaneLenclud@436
|
221 |
InitiateSystemShutdownEx(NULL, NULL, 0/*Timeout*/,
|
StephaneLenclud@436
|
222 |
TRUE/*ForceAppsClosed*/, TRUE/*RebootAfterShutdown*/,
|
StephaneLenclud@436
|
223 |
SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG | SHTDN_REASON_FLAG_PLANNED);
|
StephaneLenclud@436
|
224 |
//This shutdown flag corresponds to: "Operating System: Reconfiguration (Planned)".
|
StephaneLenclud@436
|
225 |
}
|
StephaneLenclud@436
|
226 |
} |