I returned this past weekend after a family function. It had me very busy and thus, no blogging for sometime for me 🙂
That said, today I was working with one of my friends over email – she was stuck about getting to launch a process in a specific user context, using managed code. Since the .NET Framework doesn’t support such functionality out of the box, we tried different ways to do this:
1) LogonUser followed by WindowsIdentity impersonation using the obtained token – the token wasn’t getting down to the spawned process even after we attempted Impersonate method of WindowsIdentity.
2) WMI also tried – it works but needs to be slightly modified when running on a local machine compared to a remote machine (we cannot specify credentials for local machine in the options).
Finally, we thought about working on CreateProcessWithLogonW. It turned out to be rather simple and clean way of achieving our goal. For those who are interested, the following snippet uses CreateProcessWithLogonW via PInvoke and launches a process under the specified user context:
The following are the PInvoke declarations and data types required:
[Flags]
enumLogonFlags
{
LOGON_WITH_PROFILE = 0x00000001,
LOGON_NETCREDENTIALS_ONLY = 0x00000002
}
[Flags]
enumCreationFlags
{
CREATE_SUSPENDED = 0x00000004,
CREATE_NEW_CONSOLE = 0x00000010,
CREATE_NEW_PROCESS_GROUP = 0x00000200,
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
CREATE_SEPARATE_WOW_VDM = 0x00000800,
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
}
[StructLayout(LayoutKind.Sequential)]
structProcessInfo
{
publicIntPtr hProcess;
publicIntPtr hThread;
public uintdwProcessId;
public uintdwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
structStartupInfo
{
public intcb;
public stringreserved1;
public stringdesktop;
public stringtitle;
public uintdwX;
public uintdwY;
public uintdwXSize;
public uintdwYSize;
public uintdwXCountChars;
public uintdwYCountChars;
public uintdwFillAttribute;
public uintdwFlags;
public ushortwShowWindow;
public shortreserved2;
public intreserved3;
publicIntPtr hStdInput;
publicIntPtr hStdOutput;
publicIntPtr hStdError;
}
[DllImport(“advapi32.dll”, CharSet=CharSet.Unicode, ExactSpelling=true, SetLastError=true)]
static extern boolCreateProcessWithLogonW(
stringprincipal,
stringauthority,
stringpassword,
LogonFlags logonFlags,
stringappName,
stringcmdLine,
CreationFlags creationFlags,
IntPtr environmentBlock,
stringcurrentDirectory,
refStartupInfo startupInfo,
outProcessInfo processInfo);
[DllImport(“kernel32.dll”)]
static extern boolCloseHandle(IntPtr h);
The following is the code snippet that does the PInvoke and spawns the process under a specific user context:
StartupInfo si = newStartupInfo();
si.cb = Marshal.SizeOf(typeof(StartupInfo));
si.title = “This is impersonated command prompt”;
ProcessInfo pi = newProcessInfo();
stringapp = Path.Combine(Environment.SystemDirectory, “cmd.exe”);
if(CreateProcessWithLogonW(“username”, “domain”, “password”,
LogonFlags.LOGON_WITH_PROFILE,
app, null,
0, IntPtr.Zero, null,
refsi, outpi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
elseConsole.WriteLine(“Error code: {0}”, Marshal.GetLastWin32Error());