Como solicitar acesso de administrador dentro de um arquivo em lotes

179

Estou tentando escrever um arquivo em lotes para meus usuários executarem suas máquinas Vista com o UAC. O arquivo está reescrevendo o arquivo de hosts, portanto, ele precisa ser executado com permissões de administrador. Preciso enviar a eles um email com um link para o arquivo .bat. O comportamento desejado é que, quando clicam com o botão direito do mouse no arquivo e dizem Abrir, eles recebem uma daquelas caixas de diálogo do UAC que tornam a tela escura e os obriga a responder se desejam conceder ao aplicativo permissão para executar como administrador. Em vez disso, eles estão apenas vendo "Acesso negado" na janela da linha de comando.

Isso é possível de maneira diferente?

skb
fonte
2
Se você se deparou com isso e, como eu, está feliz em usar o PowerShell, não perca o one-liner do @ toster-cx. Perfeito!
Michael Repucci

Respostas:

352

Este script faz o truque! Basta colá-lo na parte superior do seu arquivo bat. Se você deseja revisar a saída do seu script, adicione um comando "pause" na parte inferior do arquivo em lotes.

ATUALIZAÇÃO: Este script agora está ligeiramente editado para suportar argumentos de linha de comando e um sistema operacional de 64 bits.

Obrigado 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>
Ben Gripka
fonte
22
Eu odeio ter que fazer essa besteira imunda dos lotes, mas às vezes você é forçado e isso funciona muito bem. Felicidades!
Matt queima
4
Este método não encaminha argumentos. Você sabe como isso pode ser feito? Basicamente, o que observo é que na primeira linha% 1 tem algum valor e na última linha% 1 é nula. Eu preciso encaminhar os argumentos.
pinos
3
Assim como um FYI este é testado como trabalhar em Windows 8 Incorporado
Robert Snyder
6
Isso lança minha máquina em uma espiral de janelas de comando, abrindo e fechando diagonalmente na tela. A única maneira de pará-lo é excluir o arquivo em lotes original. Ele está executando repetidamente meu arquivo em lotes e gravando o arquivo vbs. A primeira vez que solicitou autorização, mas depois disso, apenas repetiu.
TomDestry
2
Eu encontrei exatamente o mesmo problema do TomDestry com o loop infinito e o código de retorno 2. Isso foi no Windows 8.1. Eu sei que funcionou no Windows 8.0 e não posso dizer com certeza se foi a atualização 8.1 ou outra coisa que causou o isssue. A solução que funcionou para mim foi a de não utilizar cacls.exe (ou icacls), em vez: net session> nul 2> & 1 IF ERRORLEVEL 1 Goto UACPrompt ...
CRIG
55

Aqui está uma frase que eu tenho usado:

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

echo main code here
pause

Notas:

  • Testado apenas no Windows 7 e 10, você pode ter que mexer nas citações
  • Não suporta transmitir argumentos por enquanto
toster-cx
fonte
1
Se você sabe quantas params poderia haver, você pode passar parâmetros, incluindo-os depois de am_admin if not "%1"=="am_admin" (powershell start -verb runas '%0' 'am_admin "%~1" "%~2"' & exit)os parâmetros será uma acima de onde eles estavam
Tony Brix
Também é possível usar 'am_admin %*'para passar tudo, não funciona bem com aspas e espaços: / Você pode usar shiftem lote para ativar o primeiro argumento, corrigindo todos os argumentos, exceto %0.
toster-cx
Essa é uma boa resposta, mas não deve ser aceita, porque NÃO verifica se, na PRIMEIRA execução, foi executada com privilégio ADMIN ou não.
T.Todua
Para manter o diretório de trabalho, adicione cd /D %~dp0depoisif not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b)
Pedro Duarte
Boa resposta. Simplesmente funciona enquanto o script VB cria um loop infinito, mesmo com permissão de gravação para% temp%.
Twonky
19

Aqui está o meu código! Parece grande, mas são principalmente linhas de comentário (as linhas que começam com: :).

Recursos:

  • Encaminhamento de argumento completo
  • Não altera a pasta de trabalho
  • Manipulação de erros
  • Aceita caminhos com parênteses (exceto para a pasta% TEMP%)
  • Suporta caminhos UNC
  • Verificação de pasta mapeada (avisa se o administrador não pode acessar a unidade mapeada)

  • Pode ser usado como uma biblioteca externa (verifique minha postagem neste tópico: https://stackoverflow.com/a/30417025/4932683 )

  • Pode ser chamado quando / se necessário em qualquer lugar do seu código

Basta anexá-lo ao final do seu arquivo em lotes ou salvá-lo como uma biblioteca (veja acima)

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

Exemplo de como usá-lo

: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]
cyberponk
fonte
Funciona muito bem, mas eu tive que mudar uma linha para fazê-la funcionar. O &fc;: 2>nulin set "_FN=_%~ns1" & echo/%TEMP%| findstr /C:"(" >nul && (echo/ERROR: %%TEMP%% path can not contain parenthesis &pause &endlocal &fc;: 2>nul & goto:eof)estava definindo o nível de erro como 1. Eu removi esse bit e funcionou perfeitamente. Estou usando o Windows 10 Home.
precisa saber é
Sua pasta% temp% tem parênteses no caminho? Somente nesse caso, o nível de erro 1 deve estar definido.
22616 cyberponk
Não. Ele precisa do seguinte fc;:: eof logo depois? Também não sei por que esse problema causou o problema, pois ele deveria estar sendo executado.
Knyri
"fc ;: 2> nul" existe intencionalmente para definir ERRORLEVEL 1 antes de sair, para sinalizar um erro. Você poderia remover o @echo e executar uma corrida e me enviar a saída em uma mensagem privada? Obrigado!
cyberponk
Enfim, os echo/%TEMP%| findstr /C:"(" >nultestes testam se há um (caractere na sua %temp%variável de ambiente e só devem executar a parte após &&positiva. É estranho o motivo pelo qual seu (teste está retornando positivo.
22416 cyberponk
7

Outra abordagem é

  • crie um atalho localmente e defina-o para solicitar permissão de administrador [Propriedades, Avançado, Executar como administrador]

e depois

  • envie aos usuários o atalho [ou um link para o atalho em vez de um para o próprio arquivo em lotes].

Denis

[Adicionado posteriormente - Sim, não observei a data deste tópico.]

Tryx3
fonte
6

A solução de Ben Gripka causa loops infinitos. Seu lote funciona assim (pseudo-código):

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

Como você pode ver, isso causa um loop infinito, se o VBS falhar ao solicitar privilégios de administrador.

No entanto, o loop infinito pode ocorrer, embora os privilégios de administrador tenham sido solicitados com êxito.

A verificação no arquivo em lotes de Ben Gripka é propensa a erros. Eu brinquei com o lote e observei que os privilégios de administrador estão disponíveis, embora a verificação falhe. Curiosamente, a verificação funcionou como esperado, se eu iniciasse o arquivo em lotes no Windows Explorer, mas não funcionou quando iniciei no IDE.

Então, sugiro usar dois arquivos em lotes separados. O primeiro gera o VBS que chama o segundo arquivo em lote:

@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"

O segundo, chamado "my_commands.bat" e localizado no mesmo diretório que o primeiro, contém seus comandos reais:

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

Isso não causa loops infinitos e também remove a verificação de privilégios de administrador propensa a erros.

espinha de peixe
fonte
Trabalhou para você? Infelizmente para mim, este e todos os outros aqui e do outro segmento falham devido ao mesmo problema subjacente. O "runas" parm (ou comando) falha sempre que o Objeto Shell é criado indiretamente de dentro de outro programa. Tópico de interesse aqui .
Laurie Stearn
Trabalhou para mim :)
Sritam Jagadev
6

Outra solução do PowerShell ...

Não se trata de executar um script em lote conforme o administrador, mas como elevar outro programa do lote ...

Eu tenho um arquivo em lotes "wrapper" para um exe. Eles têm o mesmo "nome de arquivo raiz", mas extensões alternativas. Eu sou capaz de iniciar o exe como administrador e definir o diretório de trabalho para o que contém o script, com a seguinte chamada de PowerShell de uma linha :

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

Mais informações

Há várias Start-Processopções adicionais que você pode aplicar! Confira: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-6

Note que eu uso o @prefixo. Isso é equivalente @echo offa uma linha. Eu uso %~n0aqui para obter o "nome raiz" do script em lote e concatenar o .exepara apontar o binário adjuvante. O uso de %~dp0fornece o caminho completo para o diretório em que o lote reside. E, é claro, o -Verb RunAsparâmetro fornece a elevação .

BuvinJ
fonte
4

Sei que essa não é uma solução para OP, mas como tenho certeza de que existem muitos outros casos de uso aqui, pensei em compartilhar.

Eu tive problemas com todos os exemplos de código nestas respostas, mas então encontrei: http://www.robotronic.de/runasspcEn.html

Ele não apenas permite que você execute como administrador, mas também verifica o arquivo para garantir que não foi adulterado e armazena as informações necessárias com segurança. Admito que não é a ferramenta mais óbvia para descobrir como usar, mas para aqueles que escrevem código, deve ser bastante simples.

trex005
fonte
3

@echo offe titlepode vir antes deste código:

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

Muitas das outras respostas são exageradas se você não precisar se preocupar com o seguinte:

  • Parâmetros
  • Diretório de trabalho ( cd %~dp0mudará para o diretório que contém o arquivo em lotes)
FluorescenteVerde5
fonte
3
@echo off 
Net session >nul 2>&1 || (PowerShell start -verb runas '%~0' &exit /b)
Echo Administrative privileges have been got. & pause

O acima funciona no meu Windows 10 versão 1903

Matthew Wai
fonte
1

Como tenho problemas com esse script, exibindo um novo prompt de comando, ele próprio é executado novamente, em loop infinito (usando o Win 7 Pro), sugiro que tente outra abordagem: Como posso elevar automaticamente meu arquivo em lotes, para que ele solicite Direitos de administrador do UAC, se necessário?

Cuidado, você deve adicionar isso no final do script, conforme declarado em uma edição, para voltar ao diretório de scripts após os privilégios serem elevados: cd / d% ~ dp0

barbara.post
fonte
Verifique minha resposta a esta pergunta. Ele lida com todos esses problemas.
22616 cyberponk
1

Baseado no post de toster-cx e em outros posts interessantes nesta página, obtive informações sobre como configurar e resolver meu problema. Eu tive um problema semelhante no qual desejava que o utilitário Limpeza de Disco fosse executado semanalmente duas vezes na segunda e quinta-feira durante o horário de almoço (por exemplo, 14h). No entanto, isso exigia direitos elevados.

Compartilhando arquivo em lote que pode ajudar outros iniciantes como eu -

@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

Muito obrigado por este fórum e pelo Rems POST aqui [ https://www.petri.com/forums/forum/windows-scripting/general-scripting/32313-schtasks-exe-need-to-pass-parameters-to-script ] [1]

Sua postagem ajudou a configurar argumentos opcionais ao agendar a tarefa.

Anand
fonte
1

Há também a consulta FSUTIL desta postagem, que também está vinculada no ss64.com com o seguinte código:

@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

Enquanto o FSUTIL estiver presente, é uma alternativa confiável.

Laurie Stearn
fonte
0

Você não pode solicitar direitos de administrador de um arquivo em lotes, mas pode escrever um script de host de scripts do Windows em% temp% e executá-lo (que por sua vez executa seu lote como administrador). Você deseja chamar o método ShellExecute no Shell. Objeto de aplicativo com "runas" como verbo

Anders
fonte
0

Use mshtapara solicitar direitos de administrador:

@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

Ou, usando o PowerShell:

powershell -c Start-Process "%~nx0" -Verb runas
HackingAddict1337
fonte
-5

use o comando runas. Mas acho que você não pode enviar um arquivo .bat com facilidade.

Josh
fonte
6
Esta resposta está incorreta. O runascomando não pode ser usado para provocar elevação.
Bill_Stewart