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

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.