【问题标题】:Converting C# Application that runs CMD commands to one that runs Powershell commands with administrative privileges将运行 CMD 命令的 C# 应用程序转换为运行具有管理权限的 Powershell 命令的应用程序
【发布时间】:2019-07-14 12:45:25
【问题描述】:

我有一个 Visual Studio 应用程序,它使用以下函数运行一系列 Cmd 命令

public static void AdminEx(string command) //Runs an Administrative Windows Command
    {
        var proc = new System.Diagnostics.ProcessStartInfo();
        proc.UseShellExecute = true;
        proc.WorkingDirectory = @"C:\Windows\System32";
        proc.FileName = @"C:\Windows\System32\cmd.exe";
        proc.Verb = "runas";
        proc.Arguments = "/c " + command;
        proc.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
        var p = System.Diagnostics.Process.Start(proc);
        p.WaitForExit();
    }

我们最近更新了代码并转换为 Powershell。我将如何更改此功能以适应新代码。这仍然是最有效的方法吗???

【问题讨论】:

    标签: c# powershell cmd elevated-privileges


    【解决方案1】:

    Otávio Caldonazo's helpful answer 提供了一个有效的解决方案,但有几点值得解释:

    • 除非存在安全问题(有人将非标准的恶意cmd.exe / powershell.exe 可执行文件放在%PATH% / $env:PATH 环境变量中列出的文件夹中,以抢占标准可执行文件),它是单独使用可执行文件文件名更简单,即只使用cmd.exe / powershell.exe - 这也避免了根目录为not C:\Windows.

      • 另外,鉴于您通过.FileName 指定完整路径.WorkingDirectory 属性实际上被忽略.Verb 设置为runas,因为 .WorkingDirectory 实际上并没有设置工作目录,而是指定在哪里可以找到可执行文件,如果指定了仅限名称 - 但是,仍会在 %PATH% / $env:PATH 中进行查找。
    • 虽然 /c 参数将命令传递给调用的 shell 恰好也适用于 PowerShell 的 CLI,但 PowerShell 通常使用 sigil - 为参数名称添加前缀 ;因此,
      -c-Command 的缩写)是更好的选择。

      • 此外,考虑到 PowerShell默认加载其 $PROFILE 初始化文件(即使使用 -Command-File 调用,即使这通常是不可取的),它是 最好的改为使用-NoProfile -Command ...
    • 一般 - 如果您不需要需要海拔(以管理员身份运行) - 作为通过powershell.exe 创建子进程 的替代方法,考虑使用PowerShell SDK (API),它可以更快在-process 执行 同时还支持更细粒度地捕获输出流(您似乎对您的特定场景;虽然System.Diagnostics.ProcessStartInfo 允许您通过.RedirectStandardOutput.RedirectStandardError 属性捕获stdout 和stderr 输出,但请注意PowerShell 具有additional output streams)。

    基于以上,我建议做以下事情:

    var proc = new System.Diagnostics.ProcessStartInfo();
    proc.UseShellExecute = true;
    proc.Verb = "runas";
    proc.FileName = @"powershell.exe";
    proc.Arguments = "-NoProfile -Command " + command;
    proc.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
    var p = System.Diagnostics.Process.Start(proc);
    p.WaitForExit();
    

    或者,更简洁:

    using System;
    using System.Diagnostics;
    
    var proc = new ProcessStartInfo()
    {
      UseShellExecute = true,
      Verb = "runas",
      WindowStyle = ProcessWindowStyle.Hidden,
      FileName = "powershell.exe",
      Arguments = "-NoProfile -Command " + command
    };
    Process.Start(proc).WaitForExit();
    

    【讨论】:

    • 嘿,小问题:我想使用上述方法更改注册表项?我该怎么办?
    • @securityghost:通用的*-Item* cmdlet 允许您使用注册表,例如this answer。如果您需要进一步的帮助,请创建一个问题;完成后,请随时在此处 ping 我。
    【解决方案2】:

    只需换行:

    proc.FileName = @"C:\Windows\System32\cmd.exe";
    

    收件人:

    proc.FileName = @"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";
    

    这将使您的软件打开 powershell.exe 而不是 cmd.exe,我不知道这是否是解决此问题的最佳方法,但我在这里尝试过,它对我有用。

    【讨论】:

    • 我不推荐这种方法,你应该引用 System.Management.Automation 命名空间。
    • @persistent13 System.Management.Automation namespace 是什么?你能解释一下它的作用以及我如何使用它吗? - 非常感谢
    • 请查看 elgonzo 在您的问题下方的评论中发布的链接。 System.Management.Automation 是用于 C# 的 PowerShell。您无需调用外部进程即可对 PowerShell 实例进行完全编程控制。
    • @Persistent13:虽然推荐 PowerShell SDK (API) 通常是一个很好的指针,但这里还有一个额外的要求:需要以 作为管理员(提升)运行,其中, AFAIK,总是涉及一个外部进程,因此通过Powershell CLI使用securityghost的child-process方法(使用@毕竟,987654324@ 设置为true.Verb 设置为RunAs) 可能是最好的(唯一的?)方法。
    猜你喜欢
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-08
    相关资源
    最近更新 更多