Redirect output for CreateProcessAsUser
Asked Answered
E

0

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

}
Epistemic answered 28/7, 2016 at 13:6 Comment(11)
Personally I took my impersonation code from #1169071 and then just used standard process callsUwton
@Uwton 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 accountEpistemic
so is the service running as system, or you need a specific part of the service to run as system?Uwton
The service is running at systemEpistemic
System has a lot less rights. Where are you trying to write to? Where does that errorlogger go?Uwton
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.Pipit
@Uwton 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 thisEpistemic
@HansPassant That was my first attempt, but since it was a windows service, I have security issues.Epistemic
should the process you're trying to run interact with desktop? if not, have you tried doing this https://mcmap.net/q/302540/-how-to-run-console-application-from-windows-service and impersonating michiel.vanotegem.nl/2006/07/… ? Also, this article may be of some use to you codeproject.com/Articles/170017/…Mccarver
@Mccarver 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 ClassEpistemic
Show exactly what errors you are getting, including GetLastError values.Elisabetta

© 2022 - 2024 — McMap. All rights reserved.