从Powershell启动Elevated CMD.exe

时间:2017-04-07 00:19:19

标签: powershell powershell-v2.0 powershell-v3.0 powershell-v4.0

我正在尝试从PowerShell启动一个提升的CMD窗口,但我遇到了一些问题。以下是我现在的代码。机器上有一个管理员帐户,其用户名为" 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窗口以显示的任何帮助都将非常感激。

由于

2 个答案:

答案 0 :(得分:4)

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

  • -Verb RunAs Start-Process 启动流程提升的原因。

  • 但是, -Verb RunAs无法与-Credential参数结合使用,因此您无法直接控制高程发生的用户帐户 - 但是通常没有必要

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

安全警告

  • 将密码存储为纯文本通常会带来安全风险。
  • 此外,如果您让非管理用户使用存储的管理员凭据执行以下代码,您就可以有效地授予他们管理权限。

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

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

    • 由于指定的用户是管理员,因此此处不关注,但如果-Credential针对的是非管理员用户,建议您同时指定一个-WorkingDir参数已知指定用户有权访问 - 否则,调用可能会失败(保留当前位置,并且可能不允许目标用户访问它)。
  • 嵌入在1st中的第二个,然后使用-Verb RunAs运行目标命令提升,然后在指定用户的上下文中发生 < / em>的

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

    • 工作目录总是$env:SYSTEMROOT\Windows32; -Verb RunAs甚至忽略-WorkingDirectory值;如果要更改为特定目录,请在传递给cd的命令中嵌入cmd.exe命令。

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

# 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"'

请注意,embedded,2nd命令作为单字符串传递给(隐含的)-ArgumentList(a.k.a。-Args)参数。

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

-ArgumentList被定义为类型[string[]] ,即字符串参数的数组如果您传递多个, - 分隔的参数,那么PowerShell会为您合成命令行,这通常可以更容易地获得正确的引用,特别是涉及变量引用时:

以下命令演示了这种技术:它是一种变体,它传递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;博士

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

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

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

上面按照预期在新控制台窗口中执行cmd /k echo hi!。 语法很方便,因为{ ... }似乎提供了一个简单引用的上下文:您可以自由使用嵌入式 "'个实例构建命令行。

然而,幕后发生的事情是脚本块被转换为字符串,因为它是-ArgumentList期望的参数类型,并且< strong>当脚本块转换为字符串时,其文字内容 - 使用{}之间的所有内容 。
这意味着不会发生字符串插值,因此您无法使用变量或子表达式。

尝试根据变量传递命令:

 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已执行。

答案 1 :(得分:2)

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

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

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

所以抓住这个。阅读mklement0&#39>关于

的答案

总是有机会了解更多内容,而且我并不知道在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'"