Я пытаюсь реализовать функцию, предложенную
Моя цель - реализовать это как многоразовую пакетную подфункцию.
Но до этого я столкнулся с паттерном непоследовательного поведения
В основном в зависимости от того, как вызывается скрипт, где он находится и откуда вызывается.
Скрипт вообще работает если его вызывать из папки и он тоже в папке. Однако, если он вызывается из корня диска, может произойти сбой с ошибкой, касающейся каналов &&.
Для документирования поведения я сохранил скрипт в виде файлов
i:\Test privilege escalation powershell with arguments.bat
и i:\script\Test powershell повышения привилегий с arguments.bat
Точный сценарий
@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
Я пробовал вызывать эти скрипты из нескольких мест c:\d:\i:, из папки, из корня диска. Я даже пробовал несколько раз и не всегда получал одинаковые результаты.
Полный лог произошедшего можно посмотреть здесь https://pastebin.com/xCEJKE2f Я не буду копировать наиболее важные фрагменты здесь
В обычной консоли cmd.exe консоль не является привилегированной
C:\>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
C:\>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
C:\>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
All 3 fail to The token '&&' is not a valid statement separator in this version. https://i.imgur.com/5sUHPeB.png
C:\>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
Fails with The system cannot find the path specified. https://i.imgur.com/5sUHPeB.png
C:\Users\shodan>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
C:\Users\shodan>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
C:\Users\shodan>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
C:\Users\shodan>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
All 4 work https://i.imgur.com/DVGpJFI.png
d:\>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
d:\>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
d:\>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
all 3 don't work, "The token '&&' is not a valid statement separator in this version." https://i.imgur.com/60NHWLh.png
d:\>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
does not work The system cannot find the path specified.
Later attempt ??? Not the same result ? d:\ vs D:\ ?!?
D:\>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
D:\>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
D:\>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
D:\>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
All four fail with "The token '&&' is not a valid statement separator in this version." https://i.imgur.com/c9WcxCp.png
d:\share>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
d:\share>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
d:\share>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
all 3 work https://i.imgur.com/0be7DkQ.png
d:\share>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
The system cannot find the path specified.
Later attempt, not the same results ?!?! again d:\ vs D:\ ?!?
D:\share>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
D:\share>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
D:\share>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
D:\share>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
All four work and elevated properly ! https://i.imgur.com/EtfvRyb.png
I:\>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
I:\>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
I:\>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
I:\>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
all don't work, same error, "The token '&&' is not a valid statement separator in this version." https://i.imgur.com/HiJggQ4.png
This result tried twice, second time same result
I:\scripts>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
I:\scripts>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
I:\scripts>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
All 3 work
I:\scripts>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
does not work The system cannot find the path specified.
This result tried twice, second time same result
Double click on I:\Test privilege escalation powershell with arguments.bat
Does not work, console flashes briefly and disappear too fast to see what the error message was
It should pause, but doesn't !
Double clicking on I:\scripts\Test privilege escalation powershell with arguments.bat
works https://i.imgur.com/lFMD2MI.png
Drag and dropping a bunch of files onto I:\scripts\Test privilege escalation powershell with arguments.bat
works, dropped files are arguments https://i.imgur.com/nUXgWbA.png https://i.imgur.com/PtMT91d.png
Drag and dropping files onto
https://i.imgur.com/AMMPXzf.png
Does not work, console flashes briefly and disappear too fast to see what the error message was
It should pause, but doesn't !
In a PRIVILEGE console
C:\Windows\system32>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
C:\Windows\system32>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
C:\Windows\system32>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
C:\Windows\system32>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
All 4 work https://i.imgur.com/60owFpJ.png
C:\>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
C:\>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
C:\>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
C:\>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
All 4 work https://i.imgur.com/sgT3EUx.png
D:\>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
D:\>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
D:\>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
D:\>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
All 4 work https://i.imgur.com/TJyNGy4.png
I:\>"i:Test privilege escalation powershell with arguments.bat" 1 2 3
I:\>"i:\Test privilege escalation powershell with arguments.bat" 1 2 3
I:\>"i:\scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
I:\>"i:scripts\Test privilege escalation powershell with arguments.bat" 1 2 3
All 4 work https://i.imgur.com/S1mxGsF.png
Итак, мой вопрос: почему этот скрипт ведет себя по-разному в зависимости от того, откуда он вызывается, где он находится и как (i:\filename.bat против i:filename.bat). Тоже этот случай, когда вроде все было так же, а результат был не тот.
Наконец, моя цель — превратить этот скрипт в повторно используемую подфункцию, и она должна быть в следующем формате.
@echo off
Call :IsAdmin IsAdmin
if not %IsAdmin%==true Call :Elevate %*
if %IsAdmin%==true echo Admin privileges found
if %IsAdmin%==true echo First argument is "%~1"
if %IsAdmin%==true echo Second argument is "%~2"
if %IsAdmin%==true echo third argument is "%~3"
if %IsAdmin%==true echo fourth argument is "%~4"
if %IsAdmin%==true echo fifth argument is "%~5"
if %IsAdmin%==true echo sixth argument is "%~6"
if %IsAdmin%==true echo seventh argument is "%~7"
if %IsAdmin%==true echo eigth argument is "%~8"
if %IsAdmin%==true echo nineth argument is "%~9"
if %IsAdmin%==true pause
:END
GoTo :EOF
:IsAdmin
set %1=false
net session >nul 2>&1
if %ERRORLEVEL% == 0 set %1=true
GoTo :EOF
:Elevate
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
Проблема возникает, когда пакетный файл вызывается из корневого каталога диска, потому что %CD%
затем расширяется до значения, которое заканчивается на \
, что нарушает синтаксический анализ командной строки PowerShell, поскольку он мешает экранированному "
(\"
), который следует за ним. .
Прагматичный обходной путь — просто добавить пробел перед следующим \"
, что все еще работает, потому что cmd.exe
игнорирует конечные пробелы в путях файловой системы:
Другими словами: замените %CD%\"
на %CD% \"
Я также обновил связанный ответ соответственно.
Что касается вашего желания разбить код на модули с помощью подпрограмм, которые вы вызываете с помощью call
, включая размещение повышающего вызова powershell.exe
в подпрограмме Elevate
:
Не пытайтесь передавать аргументы через %*
в подпрограмму :Elevate
. Вместо этого запишите их в переменную на верхнем уровне командного файла, которую подпрограмма также увидит и сможет с ней работать.
Причина, по которой следует избегать передачи аргументов %*
через call
, заключается в том, что это неизбежно и необъяснимо заставляет cmd.exe
удваивать любые ^
символы в аргументах.
cmd.exe
и его особенности.Вот упрощенная версия вашего кода:
Обратите внимание на упрощение подпрограммы :IsAdmin
для использования кода выхода из команды net session
для сообщения об успехе (0
) и отказе (отличном от нуля), что позволяет оператору ||
(а также &&
) работать с этим кодом выхода.
Вызов без повышенных прав запускает воплощение с повышенными правами и ожидает его завершения, а затем завершает работу. Это означает, что любой код после строки, начинающейся с call :IsAdmin ...
, выполняется только в воплощении с повышенными правами.
@echo off & setlocal
:: If not already running elevated, self-elevate and exit.
:: Note: A helper variable must be used to capture %*,
:: because passing %* to a subroutine doubles ^ chars.
call :IsAdmin || set args=%* && (call :Elevate & exit /b)
:: Getting here means this is (now) an elevated session.
::Print the arguments received.
echo Now running elevated; arguments received:
echo. %*
pause
:: End of the main body.
goto :EOF
:: === Helper subroutines
:: Test if this session is elevated.
:: `net session` only succeeds and therefore reports exit code 0
:: in an elevated session.
:IsAdmin
net session >NUL 2>&1
goto :EOF
:: Perform self-elevation, passing all arguments through.
:Elevate
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:"=\"\"%"
:: Note:
:: * To not make the non-elevated incarnation wait for the
:: elevated one to complete, remove -Wait
:: * To keep the elevated session open until explicitly exited by the user,
:: use /k instead of /c
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
" Start-Process -Wait -Verb RunAs -FilePath cmd -ArgumentList \"/c \"\" cd /d \"\"%CD% \"\" ^&^& \"\"%~f0\"\" %args% \"\" \" "
goto :EOF
Как в сторону:
Обычно переменные среды наследуются дочерними процессами, и, учитывая, что все переменные cmd.exe
(созданные с помощью SET
) неизменно являются переменными среды, существует по крайней мере гипотетическая озабоченность по поводу «загрязнения» среды дочернего процесса вспомогательными переменными от вызывающей стороны (в в данном случае %args%
).
Хотя это можно было бы решить в целом, в данном случае в этом нет необходимости, потому что дочерние процессы с повышенными правами — из соображений безопасности — не наследуют среду своего (не повышенного) вызывающего объекта.
О, подождите, на самом деле set args=%args:^^^^=^^% не работает ... Ну, только частично, хммм. Я подумал, что было бы неплохо иметь только Call :Elevate %* в качестве одной команды. Я полагаю, что могу вызвать IsAdmin из функции :Elevate и даже больше не нуждаться в IF
Из чтения статьи о разборе cmd.exe я вижу, что для аргументов без кавычек и кавычек автоматически удваиваются только аргументы в кавычках. Таким образом, потребуется код, чтобы определить части %*, которые указаны в кавычках, и применить дедублирование каретки только к этим частям.
И в настоящее время, с set args=%args:^^^^=^^% , почему одна каретка удваивается, но не удваивается. Да, четыре каретки, превратиться в восемь кареток, а затем удвоиться до четырех. Почему set args=%args:^^^^=^^% несовместим?
@Shodan, я не думаю, что вы сможете отменить логику удвоения во всех случаях, и я предлагаю вообще обойти ее, используя aux. переменная. Пожалуйста, посмотрите мое обновление, которое теперь использует одну строку в основном теле и упрощает вашу подпрограмму :IsAdmin
.
О, вау, я люблю это! Я не знал, как напрямую использовать вывод подпрограммы, как вы сделали с IsAdmin, это будет очень полезно! Я думаю, что это в значительной степени идеально, единственное изменение, которое я собираюсь внести в свою приватную версию, это то, что я собираюсь вызвать args ElevatedArgs, чтобы сделать его уникальным именем переменной. Хотя это не должно иметь значения, как вы указали, локальные переменные теряются на высоте. Но таким образом вам даже не нужно спрашивать себя: "имеет ли значение, если я использовал аргументы в другом месте"
Да, я принимаю ответ, я также хочу убедиться, что каждый, кто ищет в Google «как поднять мой командный файл», найдет этот ответ, я искал повсюду, и я думаю, что это лучший, если вы пытаетесь сделать это чисто. Я также нашел другие методы повышения привилегий, такие как использование runas, а также создание vbscript, который повторно запускает пакетный файл с повышенным процессом. Я попытаюсь сформулировать вопрос, который сделает его номером 1 в Google, а также представлю все альтернативы.
Я запускал приведенный выше скрипт во всех перестановках, он работает pastebin.com/bqATQFkv Однако всегда ли он будет удваивать карреты? Потому что, если да, то как насчет добавления в функцию, если определенные аргументы установлены args=%args:^^^^=^^% Я только что проверил, и это также работает i.imgur.com/g09GIbv.png