Search
Close this search box.

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: .NET Insights

Related Posts