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

本文关键字:隐藏 控制台 窗口 进程 运行 DllImports 用户 身份 使用 | 更新日期: 2023-09-27 18:26:16

更新

以下尝试的错误消息:

我看到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窗口。当运行上面的(top方法)时,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.

因此,我试图实现一个无控制台的过程。首先声明以下内容:

[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方法,但控制台窗口仍然会出现。

如果有任何指导意见,我将不胜感激。

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

更改

WindowStyle = ProcessWindowStyle.Minimized

WindowStyle = ProcessWindowStyle.Hidden;

我能找到的唯一有效的方法是作为具有完全域访问权限的用户通过远程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,该方法可以运行该命令。

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

  • 应用程序必须在"admin"服务器所在的域上运行
  • 必须存在一个管理员帐户,该帐户可以访问任何希望运行此代码的机器以及服务器(在本例中为ADMINSERVER
  • 任何错误都将存在于远程服务器上,因此我们必须注意正确处理这些错误,以便将它们传递回我们的应用程序