Managed CreateProcessWithLogonW

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\]
	enum LogonFlags 
{
	LOGON\_WITH\_PROFILE         = 0x00000001,
	LOGON\_NETCREDENTIALS\_ONLY  = 0x00000002        
}

\[Flags\]
	enum CreationFlags 
{
	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)\]
	struct ProcessInfo 
{
	public IntPtr hProcess; 
	public IntPtr hThread; 
	public uint dwProcessId; 
	public uint dwThreadId; 
}

\[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)\]
	struct StartupInfo 
{
	public int     cb; 
	public string  reserved1; 
	public string  desktop; 
	public string  title; 
	public uint    dwX; 
	public uint    dwY; 
	public uint    dwXSize; 
	public uint    dwYSize; 
	public uint    dwXCountChars; 
	public uint    dwYCountChars; 
	public uint    dwFillAttribute; 
	public uint    dwFlags; 
	public ushort  wShowWindow; 
	public short   reserved2; 
	public int     reserved3; 
	public IntPtr  hStdInput; 
	public IntPtr  hStdOutput; 
	public IntPtr  hStdError; 
}

\[DllImport("advapi32.dll", CharSet=CharSet.Unicode, ExactSpelling=true, SetLastError=true)\]
static extern bool CreateProcessWithLogonW(
	string principal,
	string authority,
	string password,
	LogonFlags logonFlags,
	string appName,
	string cmdLine,
	CreationFlags creationFlags,
	IntPtr environmentBlock,
	string currentDirectory,
	ref StartupInfo startupInfo,
	out ProcessInfo processInfo);

\[DllImport("kernel32.dll")\]
static extern bool CloseHandle(IntPtr h);

The following is the code snippet that does the PInvoke and spawns the process under a specific user context:

            StartupInfo si = new StartupInfo();
	si.cb = Marshal.SizeOf(typeof(StartupInfo));
	si.title = "This is impersonated command prompt";

	ProcessInfo pi = new ProcessInfo();
	string app = Path.Combine(Environment.SystemDirectory, "cmd.exe");

		if (CreateProcessWithLogonW("username", "domain", "password",
		LogonFlags.LOGON\_WITH\_PROFILE,
		app, null,
		0, IntPtr.Zero, null,
		ref si, out pi)) 
	{
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
	}
	else Console.WriteLine("Error code: {0}", Marshal.GetLastWin32Error());
This article is part of the GWB Archives. Original Author: Gaurav Khanna

New on Geeks with Blogs