Как запросить доступ администратора внутри командного файла

Я пытаюсь написать командный файл, чтобы мои пользователи могли запускать их с компьютеров Vista с UAC. Файл перезаписывает свой файл hosts, поэтому его нужно запускать с разрешениями администратора. Мне нужно иметь возможность отправить им электронное письмо со ссылкой на файл.bat. Желаемое поведение состоит в том, что когда они щелкают правой кнопкой мыши по файлу и говорят "Открыть", они получают одно из тех диалогов UAC, которое заставляет экран потемнеть и вынуждает их ответить, хотят ли они дать приложению разрешение на запуск от имени администратора. Вместо этого они просто видят "Отказано в доступе" в окне командной строки.

Возможно ли это сделать по-другому?

11 ответов

Решение

Этот скрипт делает свое дело! Просто вставьте его в верхнюю часть вашего файла летучей мыши. Если вы хотите просмотреть вывод вашего скрипта, добавьте команду "пауза" внизу вашего пакетного файла.

ОБНОВЛЕНИЕ: Этот скрипт теперь немного отредактирован для поддержки аргументов командной строки и 64-битной ОС.

Спасибо Eneerge @ https://sites.google.com/site/eneerge/scripts/batchgotadmin

@echo off

:: BatchGotAdmin
:-------------------------------------
REM  --> Check for permissions
    IF "%PROCESSOR_ARCHITECTURE%" EQU "amd64" (
>nul 2>&1 "%SYSTEMROOT%\SysWOW64\cacls.exe" "%SYSTEMROOT%\SysWOW64\config\system"
) ELSE (
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
)

REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
    echo Requesting administrative privileges...
    goto UACPrompt
) else ( goto gotAdmin )

:UACPrompt
    echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
    set params= %*
    echo UAC.ShellExecute "cmd.exe", "/c ""%~s0"" %params:"=""%", "", "runas", 1 >> "%temp%\getadmin.vbs"

    "%temp%\getadmin.vbs"
    del "%temp%\getadmin.vbs"
    exit /B

:gotAdmin
    pushd "%CD%"
    CD /D "%~dp0"
:--------------------------------------    
    <YOUR BATCH SCRIPT HERE>

Вот одна строка, которую я использовал:

@echo off
if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b)

echo main code here
pause

Заметки:

  • Проверено только на Windows 7 и 10, возможно, вам придется возиться с цитированием
  • Пока не поддерживает передачу аргументов

Вот мой код! Это выглядит большим, но это в основном строки комментариев (строки начинающиеся с:).

Особенности:

  • Полная переадресация аргументов
  • Не меняет рабочую папку
  • Обработка ошибок
  • Принимает пути с круглыми скобками (кроме папки%TEMP%)
  • Поддерживает UNC-пути
  • Проверка подключенной папки (предупреждает вас, если администратор не может получить доступ к подключенному диску)

  • Может использоваться как внешняя библиотека (проверьте мой пост в этой теме: /questions/10528178/biblioteka-funktsij-windows-batcmd-v-sobstvennom-fajle/10528194#10528194)

  • Может быть вызвано, когда / если нужно где-нибудь в вашем коде

Просто прикрепите его к концу вашего пакетного файла или сохраните как библиотеку (см. Выше)

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:RequestAdminElevation FilePath %* || goto:eof
:: 
:: By:   Cyberponk,     v1.5 - 10/06/2016 - Changed the admin rights test method from cacls to fltmc
::          v1.4 - 17/05/2016 - Added instructions for arguments with ! char
::          v1.3 - 01/08/2015 - Fixed not returning to original folder after elevation successful
::          v1.2 - 30/07/2015 - Added error message when running from mapped drive
::          v1.1 - 01/06/2015
:: 
:: Func: opens an admin elevation prompt. If elevated, runs everything after the function call, with elevated rights.
:: Returns: -1 if elevation was requested
::           0 if elevation was successful
::           1 if an error occured
:: 
:: USAGE:
:: If function is copied to a batch file:
::     call :RequestAdminElevation "%~dpf0" %* || goto:eof
::
:: If called as an external library (from a separate batch file):
::     set "_DeleteOnExit=0" on Options
::     (call :RequestAdminElevation "%~dpf0" %* || goto:eof) && CD /D %CD%
::
:: If called from inside another CALL, you must set "_ThisFile=%~dpf0" at the beginning of the file
::     call :RequestAdminElevation "%_ThisFile%" %* || goto:eof
::
:: If you need to use the ! char in the arguments, the calling must be done like this, and afterwards you must use %args% to get the correct arguments:
::      set "args=%* "
::      call :RequestAdminElevation .....   use one of the above but replace the %* with %args:!={a)%
::      set "args=%args:{a)=!%" 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
setlocal ENABLEDELAYEDEXPANSION & set "_FilePath=%~1"
  if NOT EXIST "!_FilePath!" (echo/Read RequestAdminElevation usage information)
  :: UAC.ShellExecute only works with 8.3 filename, so use %~s1
  set "_FN=_%~ns1" & echo/%TEMP%| findstr /C:"(" >nul && (echo/ERROR: %%TEMP%% path can not contain parenthesis &pause &endlocal &fc;: 2>nul & goto:eof)
  :: Remove parenthesis from the temp filename
  set _FN=%_FN:(=%
  set _vbspath="%temp:~%\%_FN:)=%.vbs" & set "_batpath=%temp:~%\%_FN:)=%.bat"

  :: Test if we gave admin rights
  fltmc >nul 2>&1 || goto :_getElevation

  :: Elevation successful
  (if exist %_vbspath% ( del %_vbspath% )) & (if exist %_batpath% ( del %_batpath% )) 
  :: Set ERRORLEVEL 0, set original folder and exit
  endlocal & CD /D "%~dp1" & ver >nul & goto:eof

  :_getElevation
  echo/Requesting elevation...
  :: Try to create %_vbspath% file. If failed, exit with ERRORLEVEL 1
  echo/Set UAC = CreateObject^("Shell.Application"^) > %_vbspath% || (echo/&echo/Unable to create %_vbspath% & endlocal &md; 2>nul &goto:eof) 
  echo/UAC.ShellExecute "%_batpath%", "", "", "runas", 1 >> %_vbspath% & echo/wscript.Quit(1)>> %_vbspath%
  :: Try to create %_batpath% file. If failed, exit with ERRORLEVEL 1
  echo/@%* > "%_batpath%" || (echo/&echo/Unable to create %_batpath% & endlocal &md; 2>nul &goto:eof)
  echo/@if %%errorlevel%%==9009 (echo/^&echo/Admin user could not read the batch file. If running from a mapped drive or UNC path, check if Admin user can read it.)^&echo/^& @if %%errorlevel%% NEQ 0 pause >> "%_batpath%"

  :: Run %_vbspath%, that calls %_batpath%, that calls the original file
  %_vbspath% && (echo/&echo/Failed to run VBscript %_vbspath% &endlocal &md; 2>nul & goto:eof)

  :: Vbscript has been run, exit with ERRORLEVEL -1
  echo/&echo/Elevation was requested on a new CMD window &endlocal &fc;: 2>nul & goto:eof
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Пример того, как его использовать

:EXAMPLE
@echo off

 :: Run this script with elevation
 call :RequestAdminElevation "%~dpfs0" %* || goto:eof

  echo/I now have Admin rights!
  echo/
  echo/Arguments using %%args%%:    %args%
  echo/Arguments using %%*: %*
  echo/%%1= %~1
  echo/%%2= %~2
  echo/%%3= %~3

  echo/
  echo/Current Directory: %CD%
  echo/
  echo/This file: %0
  echo/

pause &goto:eof

[here you paste the RequestAdminElevation function code]

Другой подход заключается в

  • создайте ярлык локально и установите его для вызова прав администратора [Свойства, Дополнительно, Запуск от имени администратора]

а потом

  • отправьте своим пользователям ярлык [или ссылку на ярлык, а не ссылку на сам пакетный файл].

Денис

[Добавлено позже - Да, я не заметил дату этой темы.]

Решение Бена Грипки вызывает бесконечные петли. Его партия работает следующим образом (псевдокод):

IF "no admin privileges?"
    "write a VBS that calls this batch with admin privileges"
ELSE
    "execute actual commands that require admin privileges"

Как видите, это вызывает бесконечный цикл, если VBS не может запросить права администратора.

Однако может возникнуть бесконечный цикл, хотя права администратора были успешно запрошены.

Проверка в командном файле Бена Грипки просто подвержена ошибкам. Я поиграл с пакетом и заметил, что права администратора доступны, хотя проверка не удалась. Интересно, что проверка работала, как и ожидалось, если я запустил пакетный файл из проводника Windows, но это не так, когда я запустил его из моей IDE.

Поэтому я предлагаю использовать два отдельных пакетных файла. Первый генерирует VBS, который вызывает второй пакетный файл:

@echo off

echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
set params = %*:"=""
echo UAC.ShellExecute "cmd.exe", "/c ""%~dp0\my_commands.bat"" %params%", "", "runas", 1 >> "%temp%\getadmin.vbs"

"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"

Второй, названный my_commands.bat и расположенный в том же каталоге, что и первый, содержит ваши действительные команды:

pushd "%CD%"
CD /D "%~dp0"
REM Your commands which require admin privileges here

Это не вызывает бесконечных циклов, а также удаляет подверженную ошибкам проверку привилегий администратора.

Другое решение PowerShell...

Речь идет не о запуске пакетного сценария от имени администратора, а о том, как поднять другую программу из пакета...

У меня есть батник "обертка" под exe. У них то же самое "имя корневого файла", но другие расширения. Я могу запустить exe как администратор и установить рабочий каталог на тот, который содержит скрипт, со следующей одной строкой вызова powershell:

@powershell "Start-Process -FilePath '%~n0.exe' -WorkingDirectory '%~dp0' -Verb RunAs"

Больше информации

Есть целый ряд дополнительных Start-Processварианты, которые вы можете применить! Проверьте: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-6

Обратите внимание, что я использую @префикс. Это эквивалентно@echo offдля одной строки. я использую%~n0 здесь, чтобы получить "корневое имя" пакетного сценария, затем я объединяю .exeчтобы указать на него дополнительный двоичный файл. Использование%~dp0предоставляет полный путь к каталогу, в котором находится пакет. И, конечно же,-Verb RunAsпараметр обеспечивает высоту.

Я знаю, что это не решение для OP, но так как я уверен, что здесь есть много других вариантов использования, я решил поделиться.

У меня были проблемы со всеми примерами кода в этих ответах, но потом я нашел: http://www.robotronic.de/runasspcEn.html

Он не только позволяет вам работать от имени администратора, он проверяет файл, чтобы убедиться, что он не был подделан, и надежно сохраняет необходимую информацию. Я признаю, что это не самый очевидный инструмент, чтобы понять, как его использовать, но для тех из нас, кто пишет код, он должен быть достаточно простым.

@echo off 
Net session >nul 2>&1 || (PowerShell start -verb runas '%~0' &exit /b)
Echo Administrative privileges have been got. & pause

Вышеуказанное работает на моей Windows 10 версии 1903

@echo off а также title может прийти перед этим кодом:

net session>nul 2>&1
if %errorlevel%==0 goto main
echo CreateObject("Shell.Application").ShellExecute "%~f0", "", "", "runas">"%temp%/elevate.vbs"
"%temp%/elevate.vbs"
del "%temp%/elevate.vbs"
exit

:main
    <code goes here>
exit

Многие другие ответы излишни, если вам не нужно беспокоиться о следующем:

  • параметры
  • Рабочий каталог (cd %~dp0 изменится на каталог, содержащий пакетный файл)

Также есть запрос FSUTIL из этого поста, который также связан с ss64.com и имеет следующий код:

@Echo Off
Setlocal
:: First check if we are running As Admin/Elevated
FSUTIL dirty query %SystemDrive% >nul
if %errorlevel% EQU 0 goto START

::Create and run a temporary VBScript to elevate this batch file
   Set _batchFile=%~f0
   Set _Args=%*
   :: double up any quotes
   Set _batchFile=""%_batchFile:"=%""
   Set _Args=%_Args:"=""%

   Echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\~ElevateMe.vbs"
   Echo UAC.ShellExecute "cmd", "/c ""%_batchFile% %_Args%""", "", "runas", 1 >> "%temp%\~ElevateMe.vbs"

   cscript "%temp%\~ElevateMe.vbs" 
   Exit /B

:START
:: set the current directory to the batch file location
cd /d %~dp0
:: Place the code which requires Admin/elevation below
Echo We are now running as admin [%1] [%2]
pause

Пока существует ФСУТИЛ, это надежная альтернатива.

Основываясь на сообщениях Toster-CX и других интересных постах на этой странице, я получил представление о том, как настроить и решить мою проблему. У меня была похожая проблема, когда я хотел, чтобы утилита очистки диска запускалась каждую неделю дважды в понедельник и четверг в обеденное время (скажем, в 2 часа дня). Однако это требовало повышенных прав.

Совместное использование командного файла, который может помочь другим новичкам, как я -

@echo off
echo  Welcome to scheduling 'PC Maintenance Activity'
ping localhost -n 3 >nul
echo -- Step - 1 of 3 : Please give 'Admin' rights on next screen
ping localhost -n 5 >nul
if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit)
cls
echo -- Step - 2 of 3 : In next screen, select temp areas for cleaning 
during routine scheduled activity
ping localhost -n 3 >nul
C:\Windows\System32\cleanmgr.exe /sageset:112
cls
echo    Now scheduling maintenance activity...
SchTasks /Create /SC WEEKLY /D MON,THU /TN PC_Cleanup /TR 
"C:\Windows\System32\cleanmgr.exe "/sagerun:112 /ST 14:00

cls

echo                         -- Thanks for your co-operation --
echo                    -- Maintenance activity is scheduled for --
echo                       -- Every Monday and Thursday at 2 pm --

ping localhost -n 10 >nul

Большое спасибо за этот форум и Rems POST здесь [ https://www.petri.com/forums/forum/windows-scripting/general-scripting/32313-schtasks-exe-need-to-pass-parameters-to-script][1]

Его сообщение помогло настроить необязательный аргумент при планировании задачи.

Так как у меня возникают проблемы с этим сценарием, который выдает новую командную строку с повторным запуском в бесконечном цикле (с использованием Win 7 Pro), я предлагаю вам попробовать другой подход: как я могу автоматически поднять мой пакетный файл, чтобы он запрашивал у Права администратора UAC, если требуется?

Будьте осторожны, вы должны добавить это в конце скрипта, как указано в правке, чтобы вернуться в каталог скриптов после повышения привилегий: cd /d %~dp0

Использовать mshta для запроса прав администратора:

@echo off
net session >nul 2>&1 && goto :admintasks
MSHTA "javascript: var shell = new ActiveXObject('shell.application'); shell.ShellExecute('%~nx0', '', '', 'runas', 1);close();"
exit /b
:admintasks
rem ADMIN TASKS HERE

Или, используя PowerShell:

powershell -c Start-Process "%~nx0" -Verb runas

Я использовал несколько примеров, чтобы исправить этот рабочий лайнер.

Это откроет ваш пакетный скрипт как ADMIN + Maximized Window

Просто добавьте один из следующих кодов в начало вашего пакетного скрипта. Оба способа работают, просто разные способы кодирования.

Я считаю, что первый пример дает самый быстрый ответ из-за /d переключатель, отключающий мои команды doskey, которые я включил..

EXAMPLE ONE

@ECHO OFF
IF NOT "%1"=="MAX" (powershell -WindowStyle Hidden -NoProfile -Command {Start-Process CMD -ArgumentList '/D,/C' -Verb RunAs} & START /MAX CMD /D /C %0 MAX & EXIT /B)
:--------------------------------------------------------------------------------------------------------------------------------------------------------------------

:: Your original batch code here:

:--------------------------------------------------------------------------------------------------------------------------------------------------------------------

EXAMPLE TWO

@ECHO OFF
IF NOT "%1"=="MAX" (powershell -WindowStyle Hidden -NoProfile -Command "Start-Process CMD -ArgumentList '/C' -Verb RunAs" & START /MAX CMD /C "%0" MAX & EXIT /B)
:--------------------------------------------------------------------------------------------------------------------------------------------------------------------

:: Your original batch code here:

:--------------------------------------------------------------------------------------------------------------------------------------------------------------------

См. Ниже рекомендации при использовании исходного кода партии.

Поместите исходный код партии in it's entirety

Просто потому, что первая строка кода на самом верху @ECHO OFFне означает, что вы не должны включать его снова, если он также есть в исходном скрипте.

Это гарантирует, что при перезапуске скрипта в новом окне, которое теперь работает в режиме администратора, вы не потеряете намеченные параметры / атрибуты скрипта... Такие как текущий рабочий каталог, ваши локальные переменные и т. Д.

Вы можете начать со следующих команд, чтобы избежать некоторых из этих проблем.

:: Make sure to use @ECHO OFF if your original code had it
@ECHO OFF
:: Avoid clashing with other active windows variables with SETLOCAL
SETLOCAL
:: Nice color to work with using 0A
COLOR 0A
:: Give your script a name
TITLE NAME IT!

:: Ensure your working directory is set where you want it to be
:: the following code sets the working directory to the script directory folder
PUSHD "%~dp0"

THE REST OF YOUR SCRIPT HERE...

:: Signal the script is finished in the title bar
ECHO.
TITLE Done! NAME IT!
PAUSE
EXIT

Вы не можете запрашивать права администратора у пакетного файла, но вы можете написать сценарий хоста сценариев Windows в%temp% и запустить его (и который, в свою очередь, выполнит ваш пакет как администратор). Вы хотите вызвать метод ShellExecute в командной консоли. Объект приложения с "runas" в качестве глагола

Используйте команду runas. Но я не думаю, что вы можете легко отправить файл.bat по электронной почте.

Другие вопросы по тегам