WSH Shell : Afficher les URL des instances Internet Explorer en cours d'exécution sur une machine distante Une question d'un usager sur le Forum Scripting (Fr) était de savoir s'il était possible de déterminer par script les URL des instances Internet Explorer en cours d'exécution sur une machine distante. J'ai tout d'abord pensé, à tord, que le simple fait d'utiliser l'outil PSExec de Windows Sysinternals pour exécuter un script VBScript distant suffirait pour accomplir cette tâche en s'inspirant du précédent article WSH Shell : Afficher les URL. Et bien NON ! Pour la bonne et simple raison que l'outil PSExec n'est pas en mesure d'exécuter à distance un outil ou un script au sein d'une session utilisateur connecté sur une machine distante. Cela n'est pas grave car il y a quelques temps maintenant, j'avais developpé l'outil RunAsLoggedOnUser permettant de réaliser cette tâche (Vous pouvez retrouver l'article concernant cet outil en suivant ce lien). J'ai donc rapidement maquetté une solution, ce qui m'a conduit à mettre à jour l'outil RunAsLoggedOnUser pour, entre autre, ajouter le support de la redirection des flux puis à créer un module externe compatible avec la console WSH Shell. C'est cette solution que je vous présente ici et qui se compose de l'outillage et pré requis suivants : 1- Sur la machine d'administration : - De la classe VBScript _wshRemoteIExplore.inc (module externe WSH Shell)
Ce module est à déposer dans le sous-dossier Include de la console WSH Shell - De l'outil PSExec de Windows Sysinternals
- De l'outil RunAsLoggedOnUser de moi-même ;-)
Il est absolument nécessaire d'utiliser la version supérieure ou égale à la v1.0.0.3 - Du composant COM DynaWrap (version étendue obligatoire)
Ce composant a besoin d'être inscrit dans le registre via la commande regsvr32 sur la machine d'administration. Il est à noter que cette opération est automatiquement réalisée depuis la version v1.0.0.9 du setup WSH Shell si le composant DynaWrap - DynaCall Wrapper a été sélectionné dans la liste des composants additionnels à installer)
Note : Le composant COM DynaWrap est utilisé pour invoquer les API Win32 suivantes : - NetApiBufferFree
- NetWkstaGetInfo
- PathFindOnPathW
2- Sur la machine distante : - Du script VBScript IExplore.vbs (à installer par défaut à la racine de la partition C:\)
La propriété RemoteScriptFolder de la classe wshRemoteIExplore permet de modifier ce chemin par défaut. Ce script pourra, par exemple, être installé dans le dossier partagé netlogon d'un contrôleur de domaine pour les postes membres d'un domaine. Dans ce cas, il faudra utiliser la syntaxe d'expansion retardée des variables d'environnement compatible avec l'outil RunAsLoggedOnUser, c'est à dire : WSH D:\Test> oRemoteIE.RemoteScriptFolder="{logonserver}\netlogon" - Du composant COM DynaWrap (version étendue obligatoire)
Il est facilement possible de s'affranchir de ce pré requis si uniquement les URL doivent être affichées. Le script IExplore.vbs devra dans ce cas être adapté en conséquence
Note : Le composant COM Dynawrap est utilisé pour invoquer l'API Win32 suivante : Important : Comme le soulignait à juste titre Jacques Barathon [MS] dans ce fil de discussion, j'attire également votre attention sur le risque d'illégalité de l'utilisation de cette solution si les utilisateurs finaux ne sont pas prévenus que leur navigation Internet est susceptible d'être surveillée. Cette solution (scripts VBScript ainsi que les outils personnels développés qui l'accompagne) est destinée à un usage à but purement éducatif et non à un usage dans le cadre d'une utilisation illégale. Bon, le décor étant maintenant planté, il est temps de démarrer une console WSH Shell pour tester ce nouveau module externe ! Les commentaires se font au fil de l'eau en ligne de commandes dans la console. Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. Tous droits réservés.
_ _ _ ___ _ _ ___ _ _ _
| | | |/ __>| | | / __>| |_ ___ | || |
| | | |\__ \| | \__ \| . |/ ._>| || |
|__/_/ <___/|_|_| <___/|_|_|\___.|_||_|
Windows Script Host (WSH) Shell v1.0.0.9 starting ...
Registering components ...
Loading external modules ...
Loading _wshAdsi.inc ...
Loading _wshCalendar.inc ...
Loading _wshCodeSyntaxing.inc ...
Loading _wshComputerToolbox.inc ...
Loading _wshCultures.inc ...
Loading _wshFtp.inc ...
Loading _wshIExplore.inc ...
Loading _wshIni.inc ...
Loading _wshModuleManager.inc ...
Loading _wshRemoteIExplore.inc ...
Loading _wshTaskView.inc ...
Loading _wshWmi.inc ...
Loading profiles ...
Loading D:\Users\Dev\Copyright\WSH\Release\WSHShell_Profile.inc ...
Loading D:\Documents and Settings\Gilles\Mes documents\WSH Shell\WSHShell_P...
Welcome ...
It's 25/08/2008 18:46:48 and WSH Shell is up !
Ready.
WSH D:\Test> ' notre nouveau module est bien présent dans la liste des modules
WSH D:\Test> ' automatiquement chargés. Il ne reste plus, comme d'habitude, à
WSH D:\Test> ' créer une instance pour en bénéficier
WSH D:\Test> Set oRemoteIE=New wshRemoteIExplore
WSH D:\Test>
WSH D:\Test> ' détermination des membres (méthodes et propriétés)
WSH D:\Test> gm(oRemoteIE)
Category Name
-------- ----
Function CheckPrerequisites ()
Function GetRemoteIERunningInstances (strComputer)
Function IsUserLoggedIn (strComputer)
Property OptionalToolsFolder
Property RemoteScriptFolder
Property Verbose
Property Version
WSH D:\Test> ' la méthode "CheckPrerequisites ()" permet de vérifier que les
WSH D:\Test> ' pré requis sont bien présent dans un dossier référencé par la
WSH D:\Test> ' variable d'environnement Path ou alors dans le dossier pointé
WSH D:\Test> ' par la propriété OptionalToolsFolder
WSH D:\Test> ' les pré requis sont les deux outils suivants :
WSH D:\Test> ' - PsExec de Windows Sysinternals
WSH D:\Test> ' - RunAsLoggedOnUser >= 1.0.0.3 disponible sur mon site
WSH D:\Test>
WSH D:\Test> ' vérification des pré requis
WSH D:\Test> echo CStr (oRemoteIE.CheckPrerequisites ())
Checking prerequisites ...
PSExec.exe : Faux
RunAsLoggedOnUser.exe : Faux
Operation completed successfully
Faux
WSH D:\Test> ' les deux outils ne sont pas installés ou non présents dans l'un
WSH D:\Test> ' des dossiers référencé par la variable d'environnement Path
WSH D:\Test> ' en fait, j'ai installé ces deux outils dans le dossier "D:\Test"
WSH D:\Test> oRemoteIE.OptionalToolsFolder = "D:\Test"
Checking Tools Folder ...
Tools folder exists on this machine : D:\TEST
Operation completed successfully
WSH D:\Test> ' ok, le dossier spécifié existe bien sur ma machine locale
WSH D:\Test> ' nouvelle vérification des pré requis
WSH D:\Test> echo CStr (oRemoteIE.CheckPrerequisites ())
Checking prerequisites ...
PSExec.exe : Vrai
RunAsLoggedOnUser.exe : Vrai
Operation completed successfully
Vrai
WSH D:\Test> ' tout semble en ordre au niveau de l'outillage nécessaire
WSH D:\Test> ' une autre étape consiste à s'assurer qu'un utilisateur est bien
WSH D:\Test> ' connecté sur la machine distante
WSH D:\Test> ' pour cela il suffit d'utiliser la méthode "IsUserLoggedIn" en
WSH D:\Test> ' spécifiant comme paramètre le nom NetBIOS ou DNS de la machine
WSH D:\Test> ' distante
WSH D:\Test> echo CStr (oRemoteIE.IsUserLoggedIn ("\\homem500"))
Trying to connect to \\HOMEM500 ...
Number of logged on users (including service accounts) : 2
At least on user is logged in to \\HOMEM500
Operation completed successfully
Vrai
WSH D:\Test> ' au moins un utilisateur semble être connecté sur la machine
WSH D:\Test> ' distance HOMEM500
WSH D:\Test> ' Note: Les comptes de service sont également comptabilisés !
WSH D:\Test>
WSH D:\Test> ' le script VBScript distant doit être présent dans un dossier
WSH D:\Test> ' de la machine distante. Ce script VBScript porte le nom :
WSH D:\Test> ' IExplore.vbs. Par défaut, il doit être présent à la racine de
WSH D:\Test> ' la partition C:\. Ce dossier par défaut peut être modifié via
WSH D:\Test> ' la propriété "RemoteScriptFolder"
WSH D:\Test> ' affichage du dossier distant défini par défaut
WSH D:\Test> echo oRemoteIE.RemoteScriptFolder
C:\
WSH D:\Test> ' lecture des URL Internet Explorer (ici la version IE7)
WSH D:\Test> ' - deux onglets dans une instance IE7
WSH D:\Test> ' - un seul onglet dans une autre instance IE7
WSH D:\Test> arrURL=oRemoteIE.GetRemoteIERunningInstances ("\\homem500")
Checking prerequisites ...
PSExec.exe : Vrai
RunAsLoggedOnUser.exe : Vrai
Operation completed successfully
Creating temporary file ...
Running remote command ...
Checking result ...
Reading remote URL ...
Operation completed successfully
WSH D:\Test> ' Tout est "successfully" ;-)
WSH D:\Test> ' combien d'URL utilise l'utilisateur sur la machine distante ?
WSH D:\Test> echo UBound (arrURL)
3
WSH D:\Test> ' l'utilisateur connecté sur la machine distante HOMEM500 utilise
WSH D:\Test> ' actuellement trois URL
WSH D:\Test> ' mais quelles sont ces URL consultées ?
WSH D:\Test> ' affichage formaté des instances IE
WSH D:\Test> ft arrURL,"Id","","*"
Id Name URL
-- ---- ---
2568 iexplore.exe http://glsft.free.fr/
3512 iexplore.exe http://fr.msn.com/
3512 iexplore.exe http://www.google.fr/
WSH D:\Test> ' Enjoy !
Tip_1 : Fixer la propriété Verbose à False désactive le mode verbeux ;-) Vous trouverez ci-dessous le listing du module externe _wshRemoteIExplore.inc de la console WSH Shell ainsi que le listing du script VBScript IExplore.vbs à déposer celui-ci sur la machine distante. Listing 1 : _wshRemoteIExplore.inc | - '
- ' Windows Script Host (WSH) Shell
- ' (c) 2008 Gilles LAURENT
- ' wshRemoteIExplore Class v1.0.0.1
- '
- Option Explicit
- ' spécification des objets COM requis pour exécuter ce module externe
- ' un message d'erreur s'affichera lors du chargement du module externe
- ' si les pré requis ne sont pas vérifiés et la classe ne sera pas chargée
- ' en mémoire donc ne pourra être instanciée
- shell.Require "DynamicWrapper"
- Class WSHRemoteIExplore
- ' =========================
- ' == PRIVATE PROPERTIES
- ' =========================
- Private m_oFs, m_oRe, m_oDyn, m_oSh
- Private m_strToolsFolder, m_strScriptFolder, m_strRALUPath, m_strPSEPath
- Private m_dwToolsPtr
- Private m_bVerbose
- ' =========================
- ' == PUBLIC PROPERTIES
- ' =========================
- Public Property Get OptionalToolsFolder ()
- OptionalToolsFolder = m_strToolsFolder
- End Property
- Public Property Let OptionalToolsFolder (strToolsFolder)
- ' déclaration des variables
- Dim oFolder
- If Not IsNull (strToolsFolder) Then
- ' vérification de l'existance du dossier contenant les outils
- PrintMessage "Checking Tools Folder ..." & Chr (2)
- Set oFolder = m_oFs.GetFolder (strToolsFolder)
- ' le dossier existe
- ' lecture du chemin court
- m_strToolsFolder = oFolder.ShortPath
- PrintMessage "Tools folder exists on this machine : " & UCase (m_strToolsFolder) & Chr (2)
- ' libération de l'objet
- Set oFolder = Nothing
- Else
- ' le dossier n'est pas spécifié
- m_strToolsFolder = Null
- End If
-
- ' détermination de l'adresse de la chaine de caractères
- m_dwToolsPtr = m_oDyn.GetBSTRAddr (m_strToolsFolder)
- ' traitement achevé avec succès
- PrintMessage "Operation completed successfully" & Chr (2)
- End Property
- Public Property Get RemoteScriptFolder ()
- RemoteScriptFolder = m_strScriptFolder
- End Property
- Public Property Let RemoteScriptFolder (strScriptFolder)
- PrintMessage "Remote script folder set to " & UCase (strScriptFolder) & Chr (2)
- m_strScriptFolder = strScriptFolder
- ' traitement achevé avec succès
- PrintMessage "Operation completed successfully" & Chr (2)
- End Property
- Public Property Get Verbose ()
- Verbose = m_bVerbose
- End Property
- Public Property Let Verbose (bValue)
- m_bVerbose = CBool (bValue)
- End Property
-
- Public Property Get Version ()
- m_oRe.Pattern="\r\n\'.+?[vV]((\d+\.?){3}(\.\d+)?)\r\n"
- Version = m_oRe.Execute (m_oFs.OpenTextFile (shell.Path & "\Include\_" & TypeName (Me) & ".inc", 1, False).ReadAll ())(0).SubMatches (0)
- End Property
- ' =========================
- ' == PRIVATE MEMBERS
- ' =========================
- Private Sub Class_Initialize ()
- ' initialisation des objets
- Set m_oDyn = CreateObject ("DynamicWrapper")
- Set m_oFs = CreateObject ("Scripting.FileSystemObject")
- Set m_oSh = CreateObject ("WScript.Shell")
- Set m_oRe = New RegExp
- ' initialisation des variables
- m_bVerbose = True
- m_strToolsFolder = Null
- m_strScriptFolder = "C:\"
- m_dwToolsPtr = m_oDyn.GetBSTRAddr (m_strToolsFolder)
- ' déclaration des API Win32 (prototypes)
- m_oDyn.Register "shlwapi.dll", "PathFindOnPathW", "f=s", "r=b", "i=ll"
- m_oDyn.Register "netapi32.dll", "NetApiBufferFree", "f=s", "r=l", "i=l"
- m_oDyn.Register "netapi32.dll", "NetWkstaGetInfo", "f=s", "r=l","i=lll"
- ' définition des propriétés de l'expression régulière
- m_oRe.IgnoreCase = True
- m_oRe.Multiline = False
- m_oRe.Global = True
- End Sub
- Private Sub Class_Terminate ()
- ' libération des objets
- Set m_oRe = Nothing
- Set m_oSh = Nothing
- Set m_oFs = Nothing
- Set m_oDyn = Nothing
- End Sub
- Private Sub PrintMessage (str)
- ' le message s'affiche uniquement en mode verbeux
- If Verbose Then WScript.Echo str
- End Sub
- ' =========================
- ' == PUBLIC MEMBERS
- ' =========================
- Public Function CheckPrerequisites ()
- ' déclaration des variables
- Dim bRet(1)
- Dim strFileName
- ' vérification de la présence des pré requis
- PrintMessage "Checking prerequisites ..." & Chr (2)
- strFileName=Left("PSExec.exe" & String(260,0), 260)
- bRet(0) = m_oDyn.PathFindOnPathW (m_oDyn.GetBSTRAddr(strFileName), m_oDyn.GetVariantAddr (m_dwToolsPtr))
- PrintMessage "PSExec.exe : " & CStr (bRet(0)) & Chr (2)
- ' sauvegarde du chemin complet de l'outil PSExec.exe
- m_strPSEPath = Left(strFileName, InStr(strFileName, Chr(0)) - 1)
- strFileName=Left("RunAsLoggedOnUser.exe" & String(260,0), 260)
- bRet(1) = m_oDyn.PathFindOnPathW (m_oDyn.GetBSTRAddr(strFileName), m_oDyn.GetVariantAddr (m_dwToolsPtr))
- PrintMessage "RunAsLoggedOnUser.exe : " & CStr (bRet(1)) & Chr (2)
- ' sauvegarde du chemin complet de l'outil RunAsLoggedOnUser.exe
- m_strRALUPath = Left(strFileName, InStr(strFileName, Chr(0)) - 1)
- ' traitement achevé avec succès
- PrintMessage "Operation completed successfully" & Chr (2)
- ' retour du résultat de l'opération
- CheckPrerequisites = bRet(0) And bRet(1)
- End Function
- Public Function GetRemoteIERunningInstances (strComputer)
- ' déclaration des variables
- Dim strTempFileName, strCmd
- Dim arrContent
- ' vérification de la présence des pré requis
- If CheckPrerequisites () = True Then
- ' création d'un fichier temporaire
- PrintMessage "Creating temporary file ..." & Chr (2)
- strTempFileName = m_oFs.GetSpecialFolder (2) & "\" & m_oFs.GetTempName ()
- ' préparation de la commande
- strCmd = "%COMSPEC% /c " & m_strPSEPath & " " & strComputer & " -accepteula -s -c " & m_strRALUPath & " -hide -wait -cmd " & Chr(34) & "cscript.exe //nologo " & m_strScriptFolder & "\IExplore.vbs" & Chr(34) & "2>nul >" & strTempFileName
- ' exécution de la commande distante
- ' la méthode Exec ne semble pas fonctionner avec l'outil psexec :-(
- PrintMessage "Running remote command ..." & Chr (2)
- m_oSh.Run strCmd, 0, True
- ' vérification du résultat de l'opération
- PrintMessage "Checking result ..." & Chr(2)
- ' le fichier peut être vide sur la machine distante est inaccessible
- ' il est donc nécessaire de gérer les erreurs
- On Error Resume Next
- arrContent = Shell.GetFileContent (strTempFileName)
- On Error Goto 0
- ' le fichier ne doit pas être vide !
- If IsArray (arrContent) Then
- ' vérification de la présence du header
- If arrContent(0) = "Id,Name,URL" Then
- ' lecture des url
- PrintMessage "Reading remote URL ..." & Chr (2)
- GetRemoteIERunningInstances = Shell.StringToArray (Replace (Shell.ArrayToString (arrContent), ",", Shell.strTableFieldSep))
- ' traitement achevé avec succès
- PrintMessage "Operation completed successfully" & Chr (2)
- Else
- ' une erreur est survenue
- WScript.Echo ":: An error occurred" & Chr (1) & " :: Remote process failed to execute"
- End If
- Else
- ' une erreur est survenue
- WScript.Echo ":: An error occurred" & Chr (1) & " :: Temporary file is empty !"
- End If
- ' suppression du fichier temporaire
- If m_oFs.FileExists (strTempFileName) Then m_oFs.DeleteFile strTempFileName, True
- Else
- ' les pré requis ne sont pas vérifiés
- ' Impossible d'exécuter l'opération requise
- Err.Raise 17
- End If
- End Function
- Public Function IsUserLoggedIn (strComputer)
- ' déclaration des variables
- Dim bUserLoggedIn: bUserLoggedIn = False
- Dim lRet, lBufferAddr, lBufferPtr, lNbLoggedOnUsers
- ' récupération des informations de niveau 102
- ' la structure WKSTA_INFO_102 est allouée par le système
- PrintMessage "Trying to connect to " & UCase (strComputer) & " ..." & Chr (2)
- lRet = m_oDyn.NetWkstaGetInfo (m_oDyn.GetBSTRAddr (strComputer), 102, m_oDyn.GetVariantAddr (lBufferAddr))
- ' vérification du résultat de l'opération
- If lRet = 0 Then
- ' l'opération a réussi
- ' récupération du pointeur sur la structure WKSTA_INFO_102 (LPBYTE *)
- lBufferPtr = m_oDyn.GetMemInBSTRAddr(m_oDyn.GetVariantAddr (lBufferAddr), 0, 4)
- ' détermination du nombre d'utilisateur connecté sur la machine distante
- lNbLoggedOnUsers = m_oDyn.GetMemInBSTRAddr (lBufferPtr, 24, 4)
- PrintMessage "Number of logged on users (including service accounts) : " & lNbLoggedOnUsers & Chr (2)
- ' évaluation du nombre d'utilisateur connecté
- If lNbLoggedOnUsers > 0 Then
- ' au moins un utilisateur est connecté sur la machine distante
- PrintMessage "At least on user is logged in to " & UCase (strComputer) & Chr (2)
- bUserLoggedIn = True
- Else
- ' aucun utilisateur connecté sur la machine distante
- PrintMessage "No user is logged in to " & UCase (strComputerName)
- End If
- ' libération du buffer alloué par le système
- m_oDyn.NetApiBufferFree (lBufferAddr)
- Else
- ' une erreur est survenue
- WScript.Echo ":: An error occurred (" & lRet & ")" & Chr (1) & " :: " & m_oDyn.FormatMessage (lRet)
- End If
- ' traitement achevé avec succès
- PrintMessage "Operation completed successfully" & Chr (2)
- ' retour du résultat de l'opération
- IsUserLoggedIn = bUserLoggedIn
-
- End Function
- End Class
|
Listing 2 : IExplore.vbs | - Option Explicit
- ' déclaration des variables
- Dim oDyn, oApp, oWindow, oProc
- Dim dwPid: dwPid=CLng(0)
- ' initialisation des objets
- Set oDyn=CreateObject("DynamicWrapper")
- Set oApp=CreateObject("Shell.Application")
- ' déclaration des API Win32 (prototypes)
- oDyn.Register "User32.dll", "GetWindowThreadProcessId", "f=s", "r=l", "i=hl"
- ' écriture du header dans le flux stdout
- WScript.Echo "Id,Name,URL"
- ' énumération des instances Explorer et Internet Explorer
- For Each oWindow In oApp.windows
- ' détermination du pid du processus
- oDyn.GetWindowThreadProcessId oWindow.HWND, oDyn.GetVariantAddr(dwPid)
- ' détermination du nom du processus
- Set oProc = GetObject("winmgmts:/root/cimv2:Win32_Process.Handle='" & dwPid & "'")
- ' écriture des informations dans le flux stdout
- WScript.Echo oProc.ProcessId & "," & oProc.Name & "," & oWindow.LocationURL
- Next
|
|