6

I have recently implemented CreateProcessAsUser. I have been trying to get the output results either redirected or piped to a file. Preferable to stream. I have tried this with no luck

Update

Requirements are:

  • Windows Service Application
  • Logged in as the System Account
  • The CreateProcessAsUser is impersonating as an elevated user

Code:

public class CreateProcess
{

    #region Constants

    const UInt32 INFINITE = 0xFFFFFFFF;
    const UInt32 WAIT_FAILED = 0xFFFFFFFF;

    #endregion


    #region ENUMS

    [Flags]
    public enum LogonType
    {
        LOGON32_LOGON_INTERACTIVE = 2,
        LOGON32_LOGON_NETWORK = 3,
        LOGON32_LOGON_BATCH = 4,
        LOGON32_LOGON_SERVICE = 5,
        LOGON32_LOGON_UNLOCK = 7,
        LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
        LOGON32_LOGON_NEW_CREDENTIALS = 9
    }


    [Flags]
    public enum LogonProvider
    {
        LOGON32_PROVIDER_DEFAULT = 0,
        LOGON32_PROVIDER_WINNT35,
        LOGON32_PROVIDER_WINNT40,
        LOGON32_PROVIDER_WINNT50
    }

    #endregion


    #region Structs

    [StructLayout(LayoutKind.Sequential)]
    public struct STARTUPINFO
    {
        public Int32 cb;
        public String lpReserved;
        public String lpDesktop;
        public String lpTitle;
        public Int32 dwX;
        public Int32 dwY;
        public Int32 dwXSize;
        public Int32 dwYSize;
        public Int32 dwXCountChars;
        public Int32 dwYCountChars;
        public Int32 dwFillAttribute;
        public Int32 dwFlags;
        public Int16 wShowWindow;
        public Int16 cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }


    [StructLayout(LayoutKind.Sequential)]
    public struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public Int32 dwProcessId;
        public Int32 dwThreadId;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public int nLength;
        public unsafe byte* lpSecurityDescriptor;
        public int bInheritHandle;
    }

    public enum TOKEN_TYPE
    {
        TokenPrimary = 1,
        TokenImpersonation
    }

    public enum SECURITY_IMPERSONATION_LEVEL
    {
        SecurityAnonymous,
        SecurityIdentification,
        SecurityImpersonation,
        SecurityDelegation
    }

    #endregion


    #region FUNCTIONS (P/INVOKE)

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool RevertToSelf();

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);


    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern Boolean LogonUser
    (
        String UserName,
        String Domain,
        String Password,
        LogonType dwLogonType,
        LogonProvider dwLogonProvider,
        out IntPtr phToken
    );


    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern Boolean CreateProcessAsUser
    (
        IntPtr hToken,
        String lpApplicationName,
        String lpCommandLine,
        IntPtr lpProcessAttributes,
        IntPtr lpThreadAttributes,
        Boolean bInheritHandles,
        Int32 dwCreationFlags,
        IntPtr lpEnvironment,
        String lpCurrentDirectory,
        ref STARTUPINFO lpStartupInfo,
        out PROCESS_INFORMATION lpProcessInformation
    );

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern UInt32 WaitForSingleObject
    (
        IntPtr hHandle,
        UInt32 dwMilliseconds
    );

    [DllImport("kernel32", SetLastError = true)]
    public static extern Boolean CloseHandle(IntPtr handle);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool GetExitCodeProcess(IntPtr process, ref UInt32 exitCode);

    [DllImport("kernel32.dll")]
    static extern bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe,
       ref SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize);

    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, SafeHandle hSourceHandle,
        IntPtr hTargetProcess, out SafeFileHandle targetHandle, int dwDesiredAccess,
        bool bInheritHandle, int dwOptions);
    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    public static extern IntPtr GetCurrentProcess();

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetConsoleOutputCP();

    #endregion

    #region Functions

    public static LaunchProcessInfo LaunchCommand(string command, string domain, string account, string password, bool includeOutput = false)
    {
        LaunchProcessInfo lpi = new LaunchProcessInfo();
        lpi.ProcessId = -1;
        lpi.ExitCode = -1;

        //IntPtr outputHandle = IntPtr.Zero;
        //IntPtr errorHandle = IntPtr.Zero;

        PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
        STARTUPINFO startInfo = new STARTUPINFO();
        if ( includeOutput)
        { 
            //startInfo.hStdOutput = outputHandle;
            //startInfo.hStdError = errorHandle;
        }
        Boolean bResult = false;

        UInt32 uiResultWait = WAIT_FAILED;

        var token = ValidateParametersAndGetFirstLoginToken(domain, account, password);

        var duplicateToken = IntPtr.Zero;
        try
        {

            startInfo.cb = Marshal.SizeOf(startInfo);
            //  startInfo.lpDesktop = "winsta0\\default";

            bResult = CreateProcessAsUser(
                token,
                null,
                command,
                IntPtr.Zero,
                IntPtr.Zero,
                false,
                0,
                IntPtr.Zero,
                null,
                ref startInfo,
                out processInfo
            );

            if (!bResult) { throw new Win32Exception("CreateProcessAsUser error #" + Marshal.GetLastWin32Error()); }

            // Wait for process to end
            uiResultWait = WaitForSingleObject(processInfo.hProcess, INFINITE);
            int errorNo = Marshal.GetLastWin32Error();
            UInt32 exitCode = 0;
            GetExitCodeProcess(processInfo.hProcess, ref exitCode);
            lpi.ExitCode = (int)exitCode;
            lpi.ProcessId = processInfo.dwProcessId;


            lpi.Output = "";
            lpi.Error = "";

            if (uiResultWait == WAIT_FAILED) { throw new Win32Exception("WaitForSingleObject error #" + errorNo); }

        }
        catch (Exception ex)
        {
            ErrorLogger.LogEvent(ex);
            throw;
        }
        finally
        {
            if (token != IntPtr.Zero)
                CloseHandle(token);
            if (duplicateToken != IntPtr.Zero)
                CloseHandle(duplicateToken);
            CloseHandle(processInfo.hProcess);
            CloseHandle(processInfo.hThread);
            //CloseHandle(outputHandle);
            //CloseHandle(errorHandle);
        }
        return lpi;
    }


    private static IntPtr ValidateParametersAndGetFirstLoginToken(string domain, string username, string password)
    {


        if (!RevertToSelf())
        {
            ErrorLogger.LogEvent("RevertToSelf call to remove any prior impersonations failed", System.Diagnostics.EventLogEntryType.Error, "");
            throw new Win32Exception("RevertToSelf call to remove any prior impersonations failed");
        }

        IntPtr token;

        var result = LogonUser(username,
                               domain,
                               password,
                               LogonType.LOGON32_LOGON_INTERACTIVE,
                               LogonProvider.LOGON32_PROVIDER_DEFAULT,
                               out token);
        if (!result)
        {
            var errorCode = Marshal.GetLastWin32Error();
            ErrorLogger.LogEvent(string.Format("Could not impersonate the elevated user.  LogonUser: {2}\\{1} returned error code: {0}.", errorCode, username, domain), System.Diagnostics.EventLogEntryType.Error, "");
            throw new Win32Exception("Logon for user " + username + " failed.");
        }
        return token;
    }


    #endregion

}
Community
  • 1
  • 1
H20rider
  • 2,162
  • 5
  • 29
  • 47
  • 1
    Personally I took my impersonation code from http://stackoverflow.com/questions/1168571/run-code-as-a-different-user-c and then just used standard process calls – BugFinder Jul 28 '16 at 13:07
  • @BugFinder Thanks. I had to do it this way for a reason. The code above is running under a windows service and I need to use the system account – H20rider Jul 28 '16 at 13:25
  • so is the service running as system, or you need a specific part of the service to run as system? – BugFinder Jul 28 '16 at 13:26
  • The service is running at system – H20rider Jul 28 '16 at 16:51
  • System has a lot less rights. Where are you trying to write to? Where does that errorlogger go? – BugFinder Jul 28 '16 at 23:24
  • The blog post correctly points out that the only reason to do this is to avoid having to supply a password. You are supplying a password. So don't do this and simply use the Process class. It supports redirection too. – Hans Passant Aug 02 '16 at 15:01
  • @BugFinder ErrorLogger is a wrapper using Log4Net Personally I would like to just grab the stream and grab the output. yes. System doesnt have that many rights. Therefore I am using the CreateProcessAsUser with credentials to do something that needs elevated rights.I cant use Process.Start since it is a windows services and the windows services is not allowing me to do this – H20rider Aug 03 '16 at 13:00
  • @HansPassant That was my first attempt, but since it was a windows service, I have security issues. – H20rider Aug 03 '16 at 13:01
  • should the process you're trying to run interact with desktop? if not, have you tried doing this http://stackoverflow.com/a/1369252/891715 and impersonating http://michiel.vanotegem.nl/2006/07/windowsimpersonationcontext-made-easy/ ? Also, this article may be of some use to you http://www.codeproject.com/Articles/170017/Solving-Problems-of-Monitoring-Standard-Output-and – Arie Aug 04 '16 at 12:39
  • @Arie I have tried just about everything to get the windows service to run as the System user, using impersonating to run any code that needs to be elevated. unfortunately nothing worked which is why I am using CreateProcessAsUser instead of the Process Class – H20rider Aug 04 '16 at 13:12
  • Show exactly what errors you are getting, including `GetLastError` values. – Barmak Shemirani Aug 07 '16 at 00:01

0 Answers0