将带引号的参数从批处理文件传递到“ powershell start”-按需自我提升

时间:2019-02-12 20:37:33

标签: powershell batch-file escaping quotes elevated-privileges

我正在编写一个Windows批处理文件,只要用户在出现的“用户访问控制”对话框中单击“是”,它就会自动升级为管理权限。

我正在使用一种我学到的here技术来检测我们是否已经拥有管理员权限,以及从here中获得另一种权限来升级。在适当的情况下,以下脚本(称为foo.bat)通过Powershell介导的对runas的调用重新启动:

@echo off
net session >NUL 2>NUL
if %ERRORLEVEL% NEQ 0 (
powershell start -wait -verb runas "%~dpfx0" -ArgumentList '%*'
goto :eof
)

echo Now we are running with admin rights
echo First argument is "%~1"
echo Second argument is "%~2"
pause

我的问题是在-ArgumentList中转义了引号。如果我从命令提示符处调用foo.bat one two,但如果其中一个参数包含空格,例如在foo.bat one "two three"中(例如,第二个参数应为两个单词,“两个三个”。

如果我什至可以用静态参数替换%*时得到正确的行为:

powershell start -wait -verb runas "%~dpfx0" -ArgumentList 'one "two three"'

然后,我可以在foo.bat中添加一些行,这些行构成了%*的适当转义字符。但是,即使在该静态示例中,到目前为止,我尝试过的每个转义模式都失败了(我看到的是Second argument is "two"而不是Second argument is "two three")或导致了错误(通常为Start-Process: A positional parameter cannot be found that accepts argument 'two')。借助the docs for powershell's Start-Process,我尝试了各种形式的引号,脱字符,双引号和三引号,反引号和逗号的荒谬组合,但是批处理文件引号和powershell引号之间进行了一些不愉快的交互,但没有任何效果

这有可能吗?

3 个答案:

答案 0 :(得分:2)

这是我为此目的的批次:

{"ip_address":"x,y"}

示例输出:

::ElevateMe.cmd::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@echo off & setlocal EnableExtensions DisableDelayedExpansion
Set "Args=%*"
net file 1>nul 2>&1 || (powershell -ex unrestricted -Command ^
  Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c %~f0 %Args:"=\""%'
  goto :eof)
:: Put code here that needs elevation
Echo:%*
Echo:%1
Echo:%2
Pause

如果您希望升高的cmd保持打开状态,请使用one "two three" one "two three" Drücken Sie eine beliebige Taste . . .

答案 1 :(得分:2)

  • 您遇到了一次完美的风暴,用两个{em> 引用地狱(cmd PowerShell),上面装饰着一个PowerShell bug (从PowerShell Core 6.2.0开始)。

  • 要解决该错误,不能直接 重新调用该批处理文件,而必须通过cmd /c重新调用该批处理文件。

  • LotPings' helpful answer,考虑到这一点,通常有效,但在以下情况下

    • 如果批处理文件的完整路径包含空格(例如c:\path\to\my batch file.cmd
    • 如果参数恰好包含以下任何cmd元字符(甚至在"..."内部):& | < > ^;例如one "two & three"
    • 如果使用管理员特权重新调用批处理文件依赖于在最初从其调用的同一工作目录中执行。

以下解决方案解决了所有这些极端情况。虽然它并非无关紧要,但它应该可以重复使用:

@echo off
setlocal

:: Test whether this invocation is elevated (`net session` only works with elevation).
:: If already running elevated (as admin), continue below.
net session >NUL 2>NUL && goto :elevated

:: If not, reinvoke with elevation.
set args=%*
if defined args set args=%args:^=^^%
if defined args set args=%args:<=^<%
if defined args set args=%args:>=^>%
if defined args set args=%args:&=^&%
if defined args set args=%args:|=^|%
if defined args set "args=%args:"=\"\"%"
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
  " Start-Process -Wait -Verb RunAs -FilePath cmd -ArgumentList \"/c \"\" cd /d \"\"%CD%\"\" ^&^& \"\"%~f0\"\" %args% \"\" \" "
exit /b

:elevated
:: =====================================================
:: Now we are running elevated, in the same working dir., with args passed through.
:: YOUR CODE GOES HERE.

echo First argument is "%~1"
echo Second argument is "%~2"

pause

答案 2 :(得分:-1)

批准的唯一提升方式是使用清单。这模拟Unix的 SUDO.EXE

要运行命令并保持高位状态

RunAsAdminconsole <Command to run>

要提升当前的cmd窗口或创建一个新的提升的窗口

RunAsAdminconsole 

来自https://pastebin.com/KYUgEKQv


REM Three files follow
REM RunAsAdminConsole.bat
REM This file compiles RunAsAdminconsole.vb to RunAsAdminconsole.exe using the system VB.NET compiler.
REM Runs a command elevated using a manifest
C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc "%~dp0\RunAsAdminconsole.vb" /win32manifest:"%~dp0\RunAsAdmin.manifest" /out:"%~dp0\RunAsAdminConsole.exe" /target:exe
REM To use
rem RunAsAdminconsole <Command to run>
pause

RunAsAdmin.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="*"
    name="Color Management"
    type="win32"
/>
<description>Serenity's Editor</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> 
<security> 
    <requestedPrivileges> 
        <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/> 
    </requestedPrivileges> 
</security> 
</trustInfo> 
</assembly>

'RunAsAdminConsole.vb
'Change cmd /k to cmd /c to elevate and run command then exit elevation
imports System.Runtime.InteropServices 
Public Module MyApplication  

    Public Sub Main ()
        Dim wshshell as object
        WshShell = CreateObject("WScript.Shell")
        Shell("cmd /k " & Command())
    End Sub 

End Module 


----------------------------------------