передача аргументов в кавычках из командного файла в `powershell start` - само-повышение по требованию - PullRequest
0 голосов
/ 12 февраля 2019

Я пишу пакетный файл Windows, который автоматически переходит в административные полномочия при условии, что пользователь нажимает «Да» в открывшемся диалоговом окне «Контроль доступа пользователей».

Я использую методику, которую я выучил здесь , чтобы определить, есть ли у нас уже права администратора, и еще одно из , здесь для повышения.Когда это уместно, следующий сценарий, назовем его 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, которые составляют соответственно экранированныйзаменить %*.Однако даже в этом статическом примере каждый шаблон escape, который я пробовал до сих пор, либо не выполнялся (я вижу Second argument is "two", а не Second argument is "two three"), либо вызывал ошибку (обычно Start-Process: A positional parameter cannot be found that accepts argument 'two').Опираясь на документы для Start-Process PowerShell Я испробовал всевозможные смешные комбинации кавычек, кареток, двойных и тройных кавычек, обратных кавычек и запятых, но между цитированием пакетного файла происходит какое-то нечестивое взаимодействиеи цитирование powershell, и ничего не сработало.

Возможно ли это вообще?

Ответы [ 3 ]

0 голосов
/ 14 февраля 2019

Единственный одобренный способ поднять - это использовать манифест.Это эмулирует 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 


----------------------------------------
0 голосов
/ 15 февраля 2019
  • Вы столкнулись с идеальным штормом из двух цитирующих адов (cmd и PowerShell), украшенных ошибкой PowerShell (по состоянию на PowerShell Core 6.2.0).

  • Чтобы обойти эту ошибку, пакетный файл не может быть повторно вызван напрямую , и вместо этого должен быть повторно вызван через cmd /c.

  • Полезный ответ LotPings , который учитывает это, обычно работает, но не вследующие крайние случаи:

    • Если полный путь к пакетному файлу содержит пробелы (например, 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
0 голосов
/ 13 февраля 2019

Это моя партия для этой цели:

::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

Пример вывода:

one "two three"
one
"two three"
Drücken Sie eine beliebige Taste . . .

Если вы хотите, чтобы повышенный cmd оставался открытым, используйте -ArgumentList '/k %~f0 %Args:"=\""%

...