5
votes

passer les arguments entre guillemets du fichier de commandes au `PowerShell start` - auto-élévation à la demande

J'écris un fichier de commandes Windows qui se transforme automatiquement en autorisations administratives, à condition que l'utilisateur clique sur "Oui" dans la boîte de dialogue Contrôle d'accès utilisateur qui apparaît.

J'utilise une technique que j'ai apprise ici pour détecter si nous avons déjà des droits d'administrateur et un autre sur ici a > pour escalader. Le cas échéant, le script suivant, appelons-le foo.bat , se relance via un appel par PowerShell à runas :

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


5 commentaires

Votre code fonctionne pour moi sur Windows 10 à partir d'une invite de commande. Quelle version du système d'exploitation ou du shell de commande utilisez-vous?


@AdminOfThings Mais je suppose que vous voyez Le deuxième argument est "deux" plutôt que le Le deuxième argument est "deux trois"


Lors de l'exécution sur Windows 7 avec UAC activé, le code ouvre une fenêtre d'invite de commande avec le deuxième argument est «deux». Dans ma console d'invite de commande d'origine, il affiche "deux trois" après avoir fermé la console pop-up. Sur Windows 10, je ne reçois que le deuxième argument est "deux trois" sans fenêtre de commande supplémentaire. Il ne s'exécute probablement pas comme il se doit sur mon système Windows 10.


@AdminOfThings si vous voyez quelque chose imprimé dans la console d'origine, votre goto: eof doit être manquant ou il est en quelque sorte ignoré. Oups, il manque dans ma liste dans la question, c'est pourquoi - modifié. Ce qui compte, ce sont les arguments de l'instance élevée du fichier de commandes, et ils doivent être les mêmes que dans la version non élévée.


Qu'est-ce que % ~ dpfx0 ? Je suppose que vous voulez le chemin complet, donc % ~ dpnx0 dans la forme longue ou % ~ f0 dans une forme plus compacte ...


3 Réponses :


2
votes

Voici mon lot à cet effet:

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

Exemple de sortie:

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

Si vous voulez que la cmd élevée reste ouverte, utilisez -ArgumentList '/ k% ~ f0% Args: "= \" "%


1 commentaires

Super merci. Il convient de noter que votre solution se généralise également au cas où % * est vide (mon original ne le fait pas, car vous ne pouvez pas passer un -ArgumentList vide)



-1
votes

La seule façon approuvée d'élever est d'utiliser un manifeste. Cela émule le SUDO.EXE d'Unix.

Pour exécuter une commande et rester élevé

'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 


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

Pour élever la fenêtre cmd actuelle ou en créer une nouvelle

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>

De 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

RunAsAdminconsole 

RunAsAdminconsole <Command to run>


20 commentaires

Il devrait être % ~ dp0RunAsAdmin ... car % ~ dp0 se développe déjà avec une barre oblique inverse de fin!


Cela ne fait aucune différence. VBC ne craint pas les doubles contre-obliques. Un jour, je vais lancer une expérience avec trois barres obliques inverses pour tester mon hypothèse selon laquelle elle le prend comme une barre oblique inverse échappée. Cela rend les chemins des fichiers de commandes plus évidents.


Je n'ai pas dit que cela faisait une différence, mais ce n'est pas une raison suffisante dans ce cas pour le laisser comme incorrect. Je ne vois pas le reste de votre code, en incluant simplement des barres obliques inverses supplémentaires inutiles!


C'est un modèle pour de nombreux programmes. J'utilise des noms de programmes longs. Cela facilite la modification de noms plus courts.


Le point de @ Compo est valable, en passant, même s'il ne fait aucune différence pratique. En réponse à votre revendication de la seule méthode approuvée : Start-Process -Verb RunAs devrait fonctionner pour l'élévation à la demande et généralement < / i>, mais il y a un bogue concernant le passage d'arguments entre guillemets . Exiger une compilation à la demande avec deux fichiers d'assistance - en utilisant des chemins codés en dur, rien de moins - n'est pas une alternative pratique.


Il y a aussi le problème que cela ne fonctionne que si l'utilisateur n'a pas modifié cette clé de registre. Par conséquent, il ne peut y avoir aucune garantie que cela fonctionnera. Encore une fois, c'est l'état d'esprit d'un utilisateur et non d'un programmeur. Les programmeurs ne prétendent pas être des utilisateurs.


@ mklement0 Ce chemin fonctionnera sur toutes les versions de Windows de XP à Windows 10. Il se trouve dans un emplacement connu. Toutes les applications compatibles UAC doivent avoir un niveau d'exécution demandé ajouté au manifeste de l'application. donc le script ne peut pas faire cela. Il n'y a pas de moyen infaillible de contourner l'UAC, sauf de faire les choses à la manière de Windows. Et vous le construisez UNE FOIS et le réutilisez.


@ mklement0 Pas même un bogue, juste un cas d'extrême maladresse syntaxique, comme le montre le fait que la réponse de LotPings y navigue avec succès.


@Noodles, je serais intéressé d'en savoir plus sur ce problème: lorsque vous dites " cela ne fonctionne que si l'utilisateur n'a pas modifié ce registre clé "pouvez-vous élaborer? De quelle solution parlez-vous et de quelle clé de registre?


@jez HKEY_LOCAL_MACHINE \ SOFTWARE \ Classes \ exefile \ shell \ runas ajoute le verbe Exécuter en tant qu'administrateur aux fichiers EXE. Et voici quelques personnes avec le problème answers.microsoft.com/en-us/windows/forum/all/... et neowin.net/forum/topic/...


Donc, vous dites que si cette clé de registre a été modifiée (ou corrompue comme dans ce thread), alors l'approche powershell -verb runas échouera, alors que votre approche vbc + manifest fonctionnera toujours ?


Et les virus le font aussi bleepingcomputer.com/ forums / t / 483782 /… et


@jez Oui, c'est ce que je dis. Et un virus, sans avoir besoin d'autorisations d'administration, peut remplacer ce paramètre en créant HKEY_CURRENT_USER \ SOFTWARE \ Classes \ exefile \ shell \ runas (car les clés utilisateur remplacent les clés système). Et je répète mon point - il n’existe qu’UNE seule manière correcte d’élever .


Cela explique comment cela fonctionne docs.microsoft.com/en-us/windows/security/identity-protectio‌ n /…


Il n'y a également AUCUN moyen correct de désélever. Certaines personnes essaient de simuler une élévation en supprimant les privilèges du jeton, mais cela donne des résultats bizarres lorsqu'un utilisateur appelle un administrateur pour entrer le nom et le mot de passe de l'administrateur. C'est donc quelque chose que vous n'essayez pas - vous structurez votre programme pour qu'il fonctionne comme Windows le souhaite.


Bon à savoir. J'ai essayé votre code. Il s'est bien compilé, mais en appelant RunAsAdminConsole.exe n'importe quoi.bat j'ai eu une pause de deux secondes, aucune invite de consentement et un «accès refusé» sur la sortie de la console.


Vous n'y passez pas un chemin. Une fois élevé, le répertoire actuel devient C: \ windows \ system32. C'est la décision de Microsoft de le faire.


@jez: Il est un bogue, spécifique à la combinaison de -Verb RunAs avec des arguments intégrés entre guillemets dans un seul argument -ArgumentList - voir < a href = "https://github.com/PowerShell/PowerShell/issues/8898" rel = "nofollow noreferrer"> github.com/PowerShell/PowerShell/issues/8898 . En effet, la réponse de LotPings contourne le bogue d'une manière qui fonctionne typiquement (comme depuis implicitement reconnu par votre acceptation de cette réponse); cependant, il y a des cas extrêmes qu'il ne gère pas, ce que ma solution (espérons-le) fait.


@Noodles: Il est bon de savoir que le chemin statique fonctionne de W7 à W10 (sera-t-il présent dans les versions futures?), Mais le plus important est qu'il existe un moyen de résoudre le problème de jez en utilisant < i> fonctionnalité intégrée , sans avoir à recourir à la création d'un exécutable d'aide, ce qui est (a) loin d'être simple et vous oblige à (b) intégrer le code pour créer cet exécutable à la demande dans votre script ( encore plus loin du simple) ou (c) pour déployer à l'avance cet exécutable sur les machines cibles.


Windows est fourni avec les versions 2, 3.5 et 4 des compilateurs x32 et x64.



6
votes
  • Vous avez rencontré une tempête parfaite de deux enfers citant ( cmd et PowerShell), garnis d'un Bogue PowerShell (à partir de PowerShell Core 6.2.0).

  • Pour contourner le bogue, le fichier batch ne peut pas être réinvoqué directement et doit à la place être réinvoqué via cmd /c.

  • Réponse utile de LotPings , qui en tient compte, généralement fonctionne , mais pas dans les cas extrêmes suivants:

    • Si le chemin complet du fichier de commandes contient des espaces (par exemple, c: \ chemin \ vers \ mon fichier de commandes.cmd )
    • Si les arguments contiennent l'un des cmd métacaractères suivants (même à l'intérieur de "..." ): & | ^ ; par exemple, un "deux et trois"
    • Si le fichier de commandes réinvoqué avec les privilèges d'administrateur repose sur l'exécution dans le même répertoire de travail à partir duquel il a été appelé à l'origine.
  • La solution suivante résout tous ces cas extrêmes. Bien qu'il soit loin d'être trivial, il devrait être réutilisable tel quel:

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

J'aimerais pouvoir voter pour ce 10x! C'est la première fois que je vois un extrait qui fonctionne réellement pour les caractères spéciaux et les espaces.


Je suis heureux d'entendre que la réponse est utile, @NJones.