Skip to content

Commit ab803b9

Browse files
committed
Adds LoadUserProfile support to RunAsUser.ps1
1 parent ad0053c commit ab803b9

File tree

1 file changed

+101
-9
lines changed

1 file changed

+101
-9
lines changed

RunAsUser.ps1

+101-9
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ namespace PSCloudbase
2727
{
2828
const int LOGON32_LOGON_SERVICE = 5;
2929
const int LOGON32_PROVIDER_DEFAULT = 0;
30-
30+
const int TOKEN_ALL_ACCESS = 0x000f01ff;
3131
const uint GENERIC_ALL_ACCESS = 0x10000000;
32-
3332
const uint INFINITE = 0xFFFFFFFF;
33+
const uint PI_NOUI = 0x00000001;
34+
const uint WAIT_FAILED = 0xFFFFFFFF;
3435
3536
enum SECURITY_IMPERSONATION_LEVEL
3637
{
@@ -86,6 +87,57 @@ namespace PSCloudbase
8687
public IntPtr hStdError;
8788
}
8889
90+
[StructLayout(LayoutKind.Sequential)]
91+
struct PROFILEINFO {
92+
public int dwSize;
93+
public uint dwFlags;
94+
[MarshalAs(UnmanagedType.LPTStr)]
95+
public String lpUserName;
96+
[MarshalAs(UnmanagedType.LPTStr)]
97+
public String lpProfilePath;
98+
[MarshalAs(UnmanagedType.LPTStr)]
99+
public String lpDefaultPath;
100+
[MarshalAs(UnmanagedType.LPTStr)]
101+
public String lpServerName;
102+
[MarshalAs(UnmanagedType.LPTStr)]
103+
public String lpPolicyPath;
104+
public IntPtr hProfile;
105+
}
106+
107+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
108+
public struct USER_INFO_4
109+
{
110+
public string name;
111+
public string password;
112+
public int password_age;
113+
public uint priv;
114+
public string home_dir;
115+
public string comment;
116+
public uint flags;
117+
public string script_path;
118+
public uint auth_flags;
119+
public string full_name;
120+
public string usr_comment;
121+
public string parms;
122+
public string workstations;
123+
public int last_logon;
124+
public int last_logoff;
125+
public int acct_expires;
126+
public int max_storage;
127+
public int units_per_week;
128+
public IntPtr logon_hours; // This is a PBYTE
129+
public int bad_pw_count;
130+
public int num_logons;
131+
public string logon_server;
132+
public int country_code;
133+
public int code_page;
134+
public IntPtr user_sid; // This is a PSID
135+
public int primary_group_id;
136+
public string profile;
137+
public string home_dir_drive;
138+
public int password_expired;
139+
}
140+
89141
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
90142
extern static bool DuplicateTokenEx(
91143
IntPtr hExistingToken,
@@ -133,14 +185,31 @@ namespace PSCloudbase
133185
static extern bool GetExitCodeProcess(IntPtr hProcess,
134186
out uint lpExitCode);
135187
188+
[DllImport("userenv.dll", SetLastError=true, CharSet=CharSet.Auto)]
189+
[return: MarshalAs(UnmanagedType.Bool)]
190+
static extern bool LoadUserProfile(IntPtr hToken,
191+
ref PROFILEINFO lpProfileInfo);
192+
193+
[DllImport("userenv.dll", SetLastError=true, CharSet=CharSet.Auto)]
194+
[return: MarshalAs(UnmanagedType.Bool)]
195+
static extern bool UnloadUserProfile(IntPtr hToken, IntPtr hProfile);
196+
197+
[DllImport("Netapi32.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
198+
extern static int NetUserGetInfo(
199+
[MarshalAs(UnmanagedType.LPWStr)] string ServerName,
200+
[MarshalAs(UnmanagedType.LPWStr)] string UserName,
201+
int level, out IntPtr BufPtr);
202+
136203
public static uint RunProcess(string userName, string password,
137204
string domain, string cmd,
138-
string arguments)
205+
string arguments,
206+
bool loadUserProfile = true)
139207
{
140208
bool retValue;
141209
IntPtr phToken = IntPtr.Zero;
142210
IntPtr phTokenDup = IntPtr.Zero;
143211
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
212+
PROFILEINFO pi = new PROFILEINFO();
144213
145214
try
146215
{
@@ -164,17 +233,35 @@ namespace PSCloudbase
164233
STARTUPINFO sInfo = new STARTUPINFO();
165234
sInfo.lpDesktop = "";
166235
236+
if(loadUserProfile)
237+
{
238+
IntPtr userInfoPtr = IntPtr.Zero;
239+
int retValueNetUser = NetUserGetInfo(null, userName, 4,
240+
out userInfoPtr);
241+
if(retValueNetUser != 0)
242+
throw new Win32Exception(retValueNetUser);
243+
244+
USER_INFO_4 userInfo = (USER_INFO_4)Marshal.PtrToStructure(
245+
userInfoPtr, typeof(USER_INFO_4));
246+
247+
pi.dwSize = Marshal.SizeOf(pi);
248+
pi.dwFlags = PI_NOUI;
249+
pi.lpUserName = userName;
250+
pi.lpProfilePath = userInfo.profile;
251+
252+
retValue = LoadUserProfile(phTokenDup, ref pi);
253+
if(!retValue)
254+
throw new Win32Exception(GetLastError());
255+
}
256+
167257
retValue = CreateProcessAsUser(phTokenDup, cmd, arguments,
168258
ref sa, ref sa, false, 0,
169259
IntPtr.Zero, null,
170260
ref sInfo, out pInfo);
171261
if(!retValue)
172262
throw new Win32Exception(GetLastError());
173263
174-
WaitForSingleObject(pInfo.hProcess, INFINITE);
175-
176-
var lastErr = GetLastError();
177-
if(lastErr != 0)
264+
if(WaitForSingleObject(pInfo.hProcess, INFINITE) == WAIT_FAILED)
178265
throw new Win32Exception(GetLastError());
179266
180267
uint exitCode;
@@ -186,6 +273,8 @@ namespace PSCloudbase
186273
}
187274
finally
188275
{
276+
if(pi.hProfile != IntPtr.Zero)
277+
UnloadUserProfile(phTokenDup, pi.hProfile);
189278
if(phToken != IntPtr.Zero)
190279
CloseHandle(phToken);
191280
if(phTokenDup != IntPtr.Zero)
@@ -212,7 +301,10 @@ function Start-ProcessAsUser
212301
[string]$Arguments,
213302

214303
[parameter(Mandatory=$true)]
215-
[PSCredential]$Credential
304+
[PSCredential]$Credential,
305+
306+
[parameter()]
307+
[bool]$LoadUserProfile = $true
216308
)
217309
process
218310
{
@@ -226,6 +318,6 @@ function Start-ProcessAsUser
226318

227319
[PSCloudbase.ProcessManager]::RunProcess($nc.UserName, $nc.Password,
228320
$domain, $Command,
229-
$Arguments)
321+
$Arguments, $LoadUserProfile)
230322
}
231323
}

0 commit comments

Comments
 (0)