使用DllImports以用户身份运行进程时隐藏控制台窗口

时间:2015-12-09 12:17:18

标签: c# winapi cmd console

更新

以下尝试的错误消息:

我看到来自TEST.EXE的此错误输出(在CMD行中以2>检索):

ERROR: Logon failure: unknown user name or bad password. 

但是没有看到来自调用EXE的进程的任何错误

根据以下建议(我认为)尝试创建一个调用我需要的流程的流程:

    public static void getProc()
    {
        SecureString ss = CreatePW();

        ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "\"/C cd C:\\users\\user.name\\desktop & TEST\"")
        {
            WindowStyle = ProcessWindowStyle.Hidden,
            UseShellExecute = false,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            CreateNoWindow = true,

            WorkingDirectory = @"C:\windows\system32",
            Verb = "runas",
            Domain = "DOMAIN",
            UserName = "zzkillcitrix",
            Password = ss
        };

        string asd = "";
        Process proc = Process.Start(startInfo);

        proc.OutputDataReceived += (x, y) => asd += y.Data;
        proc.BeginOutputReadLine();
        proc.WaitForExit();
    }

其中TEST.EXE是以下内容的构建(将参数发送给CMD):

    public static void getProc()
    {
        SecureString ss = CreatePW();

        ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "Command_which_requires_user_authentication_goes_here")
        {
            WindowStyle = ProcessWindowStyle.Hidden,
            UseShellExecute = false,
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            CreateNoWindow = true,
        };

        Process proc = Process.Start(startInfo);

        proc.OutputDataReceived += (x, y) => asd += y.Data;
        proc.BeginOutputReadLine();
        proc.WaitForExit();
    }

双击运行TEST.exe时,我仍然看到CMD窗口。 运行上面(顶部方法)时,TEST.exe似乎没有运行,因为我没有看到消息框窗口。

这可能是因为用户凭据未传递给EXE文件。

我有一个使用以下ProcessStartInfo详细信息调用的进程:

ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "/C tasklist /S " + server + " /FI \"SESSION eq " + sessID + "\" /FO CSV /NH")
{
    WindowStyle = ProcessWindowStyle.Minimized,
    UseShellExecute = false,
    RedirectStandardOutput = true,
    RedirectStandardError = true,
    CreateNoWindow = true,

    WorkingDirectory = @"C:\windows\system32",
    Verb = "runas",
    Domain = "DOMAIN",
    UserName = "zzkillcitrix",
    Password = ss,
}

MSDN我知道当以用户身份运行此类进程时,CreateNoWindow和WindowStyle属性不起作用。这包括设置WindowStyle = ProcessWindowStyle.Hidden。

因此,我试图通过声明以下内容来实现无控制台Process.Start:

[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

const int SW_HIDE = 0;
const int SW_SHOW = 5;

然后像这样调用proc:

Process proc = Process.Start(startInfo);

var handle = proc.MainWindowHandle;
ShowWindow(handle, SW_HIDE);

proc.OutputDataReceived += (x, y) => procList.Add(y.Data);
proc.BeginOutputReadLine();
proc.WaitForExit();

当进程运行时,控制台窗口仍会短暂闪烁。我是否试图隐藏一个不被识别为控制台窗口的窗口句柄?

我也尝试过调用ShowWindow方法,通常是在程序开头,但控制台窗口仍然出现。

对此有任何指导意见。

2 个答案:

答案 0 :(得分:1)

更改

WindowStyle = ProcessWindowStyle.Minimized

WindowStyle = ProcessWindowStyle.Hidden;

答案 1 :(得分:0)

执行此操作的唯一方法我可以发现实际工作是通过远程PowerShell会话以具有完全域访问权限的用户身份运行命令:

static class Ps
{
    private const string RemoteHost = "ADMINSERVER.DOMAIN1.co.uk";
    private static string _errors;

    /// <summary>
    /// Executes the given command over a remote powershell session
    /// </summary>
    /// <param name="args"></param>
    /// <returns></returns>
    public static Collection<PSObject> RemoteCommand(string args)
    {
        SupportMi.Trace = $"Remote Command({args}) {{";

        var script = BuildScript(args);
        var results = Execute(script);

        SupportMi.Trace = $"RES: {results[0]} ERR: {_errors} }}";
        return results;
    }

    /// <summary>
    /// Takes a complete script and executes it over a powershell Runspace. In this case it is being
    /// sent to the server, and the results of the execution are checked for any errors. 
    /// </summary>
    /// <param name="script"></param>
    /// <returns></returns>
    private static Collection<PSObject> Execute(string script)
    {
        var results = new Collection<PSObject>();

        // Using a runspace
        using (var runspace = RunspaceFactory.CreateRunspace())
        {
            runspace.Open();

            using (var pipeline = runspace.CreatePipeline())
            {
                pipeline.Commands.AddScript(script);

                try
                {
                    results = pipeline.Invoke();
                    var errors = pipeline.Error.Read(pipeline.Error.Count);
                    foreach (var error in errors)
                    {
                        _errors += error;
                    }
                }
                catch (Exception ex)
                {
                    results.Add(new PSObject(ex.Message));
                    SupportMi.Trace = ex.Message;
                }
            }
        }

        return results;
    }

    /// <summary>
    /// Takes a string argument to be sent to the remote powershell session and arranges it in the correct format,
    /// ready to be sent to the server.
    /// </summary>
    /// <param name="args"></param>
    /// <returns></returns>
    private static string BuildScript(string args)
    {
        // Build the script
        var script = Creds() + Environment.NewLine +
                    $"Invoke-Command -session $sessions -ScriptBlock {{ {args} /U DOMAIN1\\adminusername /P adminpassword }}" + Environment.NewLine +
                    "Remove-PSSession -Session $sessions" + Environment.NewLine +
                    "exit";

        return script;
    }

    /// <summary>
    /// Returns the credentials for a remote PowerShell session in the form 
    /// of a few lines of PowerShell script. 
    /// </summary>
    /// <returns></returns>
    private static string Creds()
    {
        return "$pw = convertto-securestring -AsPlainText -Force -String \"adminpassword\"" + Environment.NewLine +
               "$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"DOMAIN1\\adminusername\",$pw" + Environment.NewLine +
               $"$sessions = New-PSSession -ComputerName {RemoteHost} -credential $cred";
    }
}

因此,为了维护无控制台的操作,我们可以调用RemoteCommand方法并传入我们想要发送的命令。 RemoteCommand将首先使用BuildScriptCreds(针对管理员凭据)构建我们的查询/命令。

然后将其发送到Execute方法,该方法创建一个新的PowerShell RunSpace,可以运行该命令。

使用此方法的一些注意事项:

  • 该应用必须在&#34; admin&#34;所在的域上运行服务器存在
  • 必须存在一个管理员帐户,该帐户可以访问任何希望运行此代码的计算机以及服务器(在本例中为ADMINSERVER
  • 远程服务器上将存在任何错误,因此我们必须小心处理这些错误,以便将它们传回我们的应用程序