从 Powershell 启动 Elevated CMD.exe

Launch Elevated CMD.exe from Powershell

我正在尝试从 PowerShell 启动提升的 CMD window,但我 运行 遇到了一些问题。下面是我现在的代码。机器上有一个管理员帐户,用户名为 "test",密码为 "test"

$username = "test"
$password = ConvertTo-SecureString "test" -AsPlainText -Force
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password
Start-Process "cmd.exe" -Credential $cred

这对于 运行 来自用户配置文件的应用程序来说一切正常,该脚本所在的没有管理员权限,但是在调用 cmd.exe 时,它会按预期启动并具有提升的权限但随后立即关闭。

我也试过用以下方式调用它:

Start-Process "cmd.exe" -Credential $cred -ArgumentList '/k'

这也行不通。
我通过传递如下参数来测试提升的权限,这工作正常。

Start-Process "cmd.exe" -Credential $cred -ArgumentList 'dir > dir.txt'

这将写出一个 dir.txt 文件到 C:\Windows\System32\WindowsPowerShell\v1.0 目录,该目录在用户帐户上被阻止,但管理员帐户测试不被阻止。

如能提供有关显示持久性 cmd window 的任何帮助,我们将不胜感激。

谢谢

最好的方法是先将您的 Start-Process-Credential 参数和您的管理员凭据加倍,然后在您的第二个 Start-Process 上使用 -Verb runas。之后,CMD.exe.

的引用变得有点复杂

总的来说,它应该是这样的。

Start-Process PowerShell -ArgumentList {-noexit -noprofile -Command "Start-Process powershell -argumentlist {-command cmd.exe -args \"/K #yourcommands# \"}" -verb runas} -Credential $Cred

所以从头开始。阅读 mklement0 关于为什么

的回答

总是有机会学习更多,我不知道将 ArgumentList 包裹在 ScriptBlock 中会阻止变量扩展。所以……不要那样做。

虽然方法保持不变。您仍然需要两次 Start-Process 调用,只是现在您必须正确引用。

#Both of these work
Start-Process powershell -Credential $cred -ArgumentList "-noprofile", "-command", "Start-Process cmd.exe -Verb RunAs -ArgumentList /k, echo, 'something'"
# $Something will expand into it's value rather than literally
Start-Process powershell -Credential $cred -ArgumentList "-noprofile", "-command", "Start-Process cmd.exe -Verb RunAs -ArgumentList /k, echo, '$something'"

注意:SomeShinyObject 在 中提出了方法的基础,但他的参数传递技术并不健壮(更新:自更正后)- 不要使用脚本块代替字符串 - 见底部。

  • -Verb RunAsStart-Process 启动进程提升的原因.

  • 但是-Verb RunAs不能和-Credential参数组合,所以不能直接控制下提升发生在哪个用户帐户 - 但通常没有必要:

    • 如果当前用户是管理员,提升总是发生在该用户的上下文中,GUI 提示仅要求确认.
    • 否则,会显示 GUI 对话框,询问管理员的用户名和密码(用户名字段为空)。

安全注意事项:

  • 将密码存储为纯文本通常存在安全风险。
  • 此外,如果您让非管理员用户使用存储的管理员凭据执行下面的代码,您实际上是在授予他们管理员权限。

如果您仍想按指定实施脚本,解决方法 需要 嵌套 2 Start-Process 来电:

  • 第一个 运行 在指定用户的上下文中是一个(总是)非提升的命令 不可见的 - 假设是一个管理用户 - 使用 -Credential.

    • 因为指定的用户是管理员,所以这不是问题,但如果 -Credential 以非管理员用户为目标,建议还指定一个 -WorkingDir 参数,指定的用户已知有访问权限-否则,调用可能会失败(保留当前位置,可能不允许目标用户访问)。
  • 第二个,嵌入第一个,然后使用-Verb RunAs到运行目标命令提升,然后发生在指定用户的上下文中

    • 注意:即使使用包含密码的凭据对象,您仍然会收到 yes/no UAC 提示以确认提升的意图 - 除非 UAC 已关闭(这是不可取的)。

    • 工作目录总是$env:SYSTEMROOT\System32-Verb RunAs 甚至忽略 -WorkingDirectory 值;如果要更改到特定目录,请在传递给 cmd.exe 的命令中嵌入一个 cd 命令; 的底部通过 powershell.exe / Set-Location 调用显示了此技术。

此命令完全符合您的要求 - 请注意安全警告:

# Construct the credentials object
$username = "jdoe"
# CAVEAT: Storing a password as plain text is a security risk in general.
#         Additionally, if you let non-administrative users execute this 
#         code with a stored password, you're effectively giving them
#         administrative rights.
$password = ConvertTo-SecureString "test" -AsPlainText -Force    
$cred = New-Object PSCredential -Args $username, $password

# Start an elevated Command Prompt (cmd) as user $username.
Start-Process powershell.exe -Credential $cred -WindowStyle Hidden `
 '-noprofile -command "Start-Process cmd.exe -Verb RunAs"'

请注意,嵌入的第二条命令作为 单个字符串 传递给(隐含的)-ArgumentList (a.k.a。-Args ) 参数。

在这个简单的例子中,只有 1 级嵌入引号 - '...' 字符串中的 " 实例 - 并且不需要扩展(字符串插值),传递单个字符串是可行的选项,但使用更复杂的命令引用变得棘手。

-ArgumentList 定义为 [string[]] 类型,即 array 字符串参数。 如果你传递多个,分隔的参数,PowerShell会为你合成命令行

  • 警告:A long-standing bug unfortunately requires that argument with embedded spaces be enclosed in embedded double-quoting - see 了解详情。

以下命令演示了此技术:它是一个变体,它传递 cmd.exe 要执行的命令,并在该命令中使用变量引用:

$msg = 'This is an elevated Command Prompt.'

Start-Process powershell.exe -Credential $cred -WindowStyle Hidden -Args `
 '-noprofile', '-command', "Start-Process cmd.exe -Verb RunAs -Args /k, echo, '$msg'"

最终执行(带提升)的cmd.exe命令为:
cmd /k echo This is an elevated Command Prompt.


可选阅读:为什么不建议使用 脚本块 代替字符串

tl;dr

  • 不要养成在需要字符串的地方使用脚本块的习惯。虽然方便,但它并不健壮,记住它何时会失败以及为什么会失败是很重要的。

乍一看,脚本块 ({ ... }) 似乎是一个方便的选择:

Start-Process cmd -ArgumentList { /k echo hi! }

以上代码在新控制台 window 中按预期执行 cmd /k echo hi!。 语法很方便,因为 { ... } 似乎提供了一个很容易引用的上下文:您可以自由使用 embedded "'构建命令行的实例。

然而,在幕后发生的是脚本块被转换为 字符串 ,因为这是 -ArgumentList 期望的参数类型,并且当脚本块转换为 字符串 时,其 文字 内容 - { 和 [=48 之间的所有内容=] - 使用.
这意味着不会发生字符串插值,因此您不能使用变量或子表达式。

尝试传递基于变量的命令:

 Start-Process cmd -ArgumentList { /k echo Honey, I`'m $HOME! }

这将执行的是:cmd /k echo Honey, I'm $HOME! - $HOME 未展开。

相比之下,传递一个内插字符串或单独的参数按预期工作:

# As a single string (argument list):
Start-Process cmd -ArgumentList "/k echo Honey, I'm $HOME!"

# As an array of arguments:
Start-Process cmd -ArgumentList /k, echo, "Honey, I'm $HOME!"

$HOME 在这两种情况下都被扩展(插值),并且像
cmd /k echo Honey, I'm C:\Users\jdoe 被执行。