从Windows服务获取以其他用户身份运行的应用程序

时间:2019-01-10 15:47:21

标签: c# windows-services impersonation advapi32

我已经做了大量研究,不确定为什么它不起作用,可能缺少一些简单的东西。该代码确实启动了该程序,但它以本地系统用户而不是预期用户的身份启动了该程序。

执行代码的服务部分:

APIProcess.PROCESS_INFORMATION PI = new APIProcess.PROCESS_INFORMATION();
if (!APIProcess.Launch(@"C:\Windows\System32\notepad.exe", ".",
                "admin", "test", out string MSG, out PI))
            {
                logger.Debug(MSG);
            }
            else
            {
                logger.Debug(MSG);
                logger.Debug(PI.dwProcessID);
            }

具有后台功能的部分代码:

public static bool Launch(string appCmdLine, string Domain, string Username, string Password,out string MSG, out PROCESS_INFORMATION pi)
    {
        MSG = "";
        pi = new PROCESS_INFORMATION();
        bool ret = false;

        IntPtr Token = IntPtr.Zero;

        if (LogonUserA(Username,Domain,Password,LogonType.LOGON_NEW_CREDENTIALS,LogonProvider.PROVIDER_WINNT50,ref Token))
        {
            if (Token != IntPtr.Zero)
            {
                IntPtr envBlock = GetEnvironmentBlock(Token);
                ret = LaunchProcessAsUser(appCmdLine, Token, envBlock, out MSG, out pi);
                MSG = string.Join(", ",Token.ToString(),envBlock.ToString());
                if (envBlock != IntPtr.Zero)
                    DestroyEnvironmentBlock(envBlock);


                CloseHandle(Token);
            }

        }
        else
        {
            MSG = "Failed To Logon User";
        }
        return ret;
    }

[DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LogonUserA(
        string Username,
        string Domain,
        string Password,
        LogonType LogonType,
        LogonProvider LogonProvider,
        ref IntPtr Token);

private static bool LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock, out string Error, out PROCESS_INFORMATION pi)
    {
        bool result = false;
        Error = "";

        pi = new PROCESS_INFORMATION();
        SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES();
        SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES();
        saProcess.nLength = (uint)Marshal.SizeOf(saProcess);
        saThread.nLength = (uint)Marshal.SizeOf(saThread);

        STARTUPINFO si = new STARTUPINFO();
        si.cb = (uint)Marshal.SizeOf(si);


        si.lpDesktop = @"WinSta0\Default"; //Modify as needed 
        si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
        si.wShowWindow = SW_SHOW;


        result = CreateProcessAsUser(
            token,
            null,
            cmdLine,
            ref saProcess,
            ref saThread,
            false,
            CREATE_UNICODE_ENVIRONMENT,
            envBlock,
            null,
            ref si,
            out pi);


        if (result == false)
        {
            int error = Marshal.GetLastWin32Error();
            string message = String.Format("CreateProcessAsUser Error: {0}", error);
            Error = message;
            //Debug.WriteLine(message);

        }

        return result;
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool CreateProcessAsUser(
       IntPtr hToken,
       string lpApplicationName,
       string lpCommandLine,
       ref SECURITY_ATTRIBUTES lpProcessAttributes,
       ref SECURITY_ATTRIBUTES lpThreadAttributes,
       bool bInheritHandles,
       uint dwCreationFlags,
       IntPtr lpEnvironment,
       string lpCurrentDirectory,
       ref STARTUPINFO lpStartupInfo,
       out PROCESS_INFORMATION lpProcessInformation);

我有一些日志记录,看起来用户令牌和环境令牌创建得很好。它也确实启动了预期的应用程序,但是它以系统用户而不是预期的模拟用户启动。如果该用户下已有程序正在运行,我可以使其工作。对于该示例,我将所需的程序更改为启动,还更改了域/用户名/密码。

1 个答案:

答案 0 :(得分:2)

问题在于在对LogonUser的调用中使用了LOGON_NEW_CREDENTIALS类型。来自the documentation

  

此登录类型允许调用方克隆其当前令牌并为出站连接指定新的凭据。 新的登录会话具有相同的本地标识符,但对其他网络连接使用不同的凭据。

尝试改用LOGON32_LOGON_BATCH。