Hallo zusammen
Ich habe mir ein kleines Script gebastelt, um Befehle mit Hilfe von PsExec über ein GUI auf einem Remote-PC auszuführen. Hauptgrund für mich so etwas zu schreiben war, dass ich zu teilweise zu faul war, gewisse Anwendungen zu starten, in dem ich mich über RemoteDesktop am PC einlogge.
Mir stellten sich da gewisse Probleme, vor allem das Auslesen des Outputs welcher von PsExec erzeugt wird, gestaltete sich als schwierig (musste schlussendlich auf temporäre txt Dateien ausweichen). Auch das ermitteln der PID eines Child Prozesses von CMD gelang mir (noch) nicht, weshalb ich hier mit Random Prozessnamen arbeite.
Das Tool bietet im Moment 2 Hauptfunktionen. Eine, um einen eigenen Befehl ausführen zu können, und eine weiterer vordefinierter Befehl welcher die Letzte Loginzeit eines Benutzers abfragt. Hierfür muss im Feld Parameter der abzufragende Username angegeben werden. Die Buttons PMS und JD stehen für den Playstation Media Server respektive JDownloader, enthalten aber im Moment noch keinen Code.
Des Weiteren kann man Profile erstellen, um die Angaben für verschiedene PCs zur Hand zu haben. Das Passwort kann zwar verschlüsselt gespeichert werden, aber ich denke dass es empfehlenswert wäre dies nicht zu tun, da der Schlüssel schlussendlich im Sourcecode steht. Weitere Speicherangaben sind der Remote-PC Name, der user mit dem angemeldet werden soll und das Feld für den Parameter. Profile können über die Buttons angelegt, gelöscht, geladen und auch editiert werden.
Da ich wie gesagt, die Prozess ID des CMD Child-Prozesses nicht rausfinden konnte, musste ich schlussendlich die Original exe kopieren, und mit einer Random Nummer versehen, um sie eindeutig identifizieren zu können, damit der richtige Prozess geschlossen wird (für den Fall dass mehrere Prozesse laufen).
Die PsExec.exe ist eine Kommandozeilenanwendung, welche von Sysinternals bzw. Microsoft zur Verfügung gestellt wird. Zu finden hier. Bei mir liegt das Script, zusammen mit einem Haufen anderer Tools von Sysinternals im Verzeichnis C:\Windows\si.
Folgend das soweit lauffähige Script.
Spoiler anzeigen
#include <GUIConstants.au3>
#include <EditConstants.au3>
#include <Process.au3>
#include <Constants.au3>
#include <WindowsConstants.au3>
#Include <GuiEdit.au3>
#Include <String.au3>
;Key zur Verschlüsselung der Passwörter, falls gespeichert
$EncryptKey = "Key angeben"
#Region ### START Koda GUI section ### Form=
;Erstelle GUI
$GUI = GUICreate("PsExec GUI", 601, 757, 193, 125)
;Buttons
$Load = GUICtrlCreateButton("Laden", 360, 0, 75, 25, 0)
$Save = GUICtrlCreateButton("Speichern", 440, 0, 75, 25, 0)
$Delete = GUICtrlCreateButton("Entfernen", 520, 0, 75, 25, 0)
$Run = GUICtrlCreateButton("Run", 192, 32, 75, 25, 0)
$LastLogon = GUICtrlCreateButton("Loginzeit", 272, 32, 75, 25, 0)
$PMS = GUICtrlCreateButton("PMS", 352, 32, 75, 25, 0)
$JD = GUICtrlCreateButton("JD", 432, 32, 75, 25, 0)
$Cancel = GUICtrlCreateButton("Abbrechen", 104, 232, 75, 25, 0)
GUICtrlSetState($Cancel,$GUI_DISABLE) ;Abbruch Button deaktivieren
;Labels
$OutputLabel = GUICtrlCreateLabel("Ausgabe", 8, 240, 94, 17)
GUICtrlCreateLabel("Hostname", 8, 8, 52, 17)
GUICtrlCreateLabel("Benutzer", 8, 32, 46, 17)
GUICtrlCreateLabel("Passwort", 8, 56, 47, 17)
GUICtrlCreateLabel("Parameter", 8, 80, 52, 17)
GUICtrlCreateLabel("Befehl", 8, 112, 34, 17)
;Inputs
$Host = GUICtrlCreateInput("", 64, 8, 121, 21)
$User = GUICtrlCreateInput("", 64, 32, 121, 21)
$Password = GUICtrlCreateInput("", 64, 56, 121, 21, $ES_PASSWORD)
$Parameter = GUICtrlCreateInput("", 64, 80, 121, 21)
;Edits
$Command = GUICtrlCreateEdit("", 8, 136, 585, 89, BitOR($ES_AUTOVSCROLL,$ES_AUTOHSCROLL,$ES_WANTRETURN))
$Output = GUICtrlCreateEdit("", 8, 264, 585, 481, BitOR($ES_AUTOVSCROLL,$ES_AUTOHSCROLL,$ES_READONLY,$ES_WANTRETURN,$WS_VSCROLL))
;Dropdowns
$ProfilesDropdown = GUICtrlCreateCombo("", 192, 8, 161, 25)
;Menüs laden
LoadProfileNames()
LoadProfile("Default")
;Initialisiere GUI
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###
While 1
$MsgMain = GUIGetMsg()
;Status abfragen
Switch $MsgMain
Case $GUI_EVENT_CLOSE
Exit
;Profil laden
Case $Load
LoadProfile(GuiCtrlRead($ProfilesDropdown))
;Profil speichern
Case $Save
SaveProfile()
;Profil löschen
Case $Delete
DeleteProfile()
;Eigenen Befehl ausführen
Case $Run
If GuiCtrlRead($Host) <> "" AND GUICtrlRead($User) <> "" AND GuiCtrlRead($Password) <> "" AND GuiCtrlRead($Command) <> "" Then ;Alle Angaben vorhanden?
GUICtrlSetState($Cancel,$GUI_ENABLE) ;Abbruch Button aktivieren
GUICtrlSetData($OutputLabel, "Ausgabe läuft...") ;Label ändern
$Random = Random(1, 99999, 1) ;Zufallszahl bestimmen, für Prozesszuweisung
DirCreate("C:\Windows\Temp\" & $Random) ;Zufalls-Verzeichnis für diese Instanz erstellen
FileCopy("C:\Windows\si\PsExec.exe", "C:\Windows\Temp\" & $Random & "\" & $Random & "PsExec.exe") ;PsExec mit neuem Zufallsnamen kopieren
$CommandRun = 'C:\Windows\Temp\' & $Random & '\' & $Random & 'PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ' & GUICtrlRead($Password) & ' ' & GUICtrlRead($Command) & ' > C:\Windows\Temp\' & $Random & '\' & $Random & 'PsExec.txt' ;Befehl zusammen setzen
$Execute = @ComSpec & ' /c ' & $CommandRun ;Endgültigen Befehl zusammen setzen
$PID = Run($Execute, '', @SW_HIDE) ;Befehl ausführen
While ProcessExists($PID) ;Hauptschleife während Ausführung
Sleep(100)
$MsgWhile = GUIGetMsg()
;Status abfragen
Switch $MsgWhile
Case $GUI_EVENT_CLOSE
ProcessClose($PID) ;CMD Prozess schliessen
ProcessClose($Random & "PsExec.exe") ;PsExec Prozess schliessen
RemoveTemp($Random) ;Temporäres Verzeichnis für Instanz entfernen
Exit ;Programm beenden
Case $Cancel
ProcessClose($PID) ;CMD Prozess schliessen
ProcessClose($Random & "PsExec.exe") ;PsExec Prozess schliessen
RemoveTemp($Random) ;Temporäres Verzeichnis für Instanz entfernen
ExitLoop ;Abbruch des aktuellen Vorgangs
EndSwitch
GUICtrlSetData($Output, FileRead('C:\Windows\Temp\' & $Random & '\' & $Random & 'PsExec.txt')) ;Output des Befehls ins Fenster schreiben
_GUICtrlEdit_Scroll($Output, 4) ;Automatisch mitscrollen
WEnd
GUICtrlSetData($OutputLabel, "Ausgabe") ;Label zurücksetzen
RemoveTemp($Random) ;Temporäres Verzeichnis entfernen
GUICtrlSetState($Cancel,$GUI_DISABLE) ;Abbruch Button deaktivieren
Else
MsgBox(64, "Fehlende Angaben", "Bitte fülle alle benötigten Felder aus") ;Fehlermeldung falls Angaben fehlen
EndIf
Case $LastLogon
If GuiCtrlRead($Host) <> "" AND GUICtrlRead($User) <> "" AND GuiCtrlRead($Password) <> "" AND GuiCtrlRead($Parameter) <> "" Then
GUICtrlSetState($Cancel,$GUI_ENABLE)
GUICtrlSetData($OutputLabel, "Ausgabe läuft...")
$Random = Random(1, 99999, 1)
DirCreate("C:\Windows\Temp\" & $Random)
FileCopy("C:\Windows\si\PsExec.exe", "C:\Windows\Temp\" & $Random & "\" & $Random & "PsExec.exe")
$CommandRun = 'C:\Windows\Temp\' & $Random & '\' & $Random & 'PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ' & GUICtrlRead($Password) & ' net user ' & GUICtrlRead($Parameter) & ' > C:\Windows\Temp\' & $Random & '\' & $Random & 'PsExec.txt'
$Execute = @ComSpec & ' /c ' & $CommandRun
$PID = Run($Execute, '', @SW_HIDE)
While ProcessExists($PID)
Sleep(100)
$MsgWhile = GUIGetMsg()
Switch $MsgWhile
Case $GUI_EVENT_CLOSE
ProcessClose($PID)
ProcessClose($Random & "PsExec.exe")
RemoveTemp($Random)
Exit
Case $Cancel
ProcessClose($PID)
ProcessClose($Random & "PsExec.exe")
RemoveTemp($Random)
ExitLoop
EndSwitch
GUICtrlSetData($Output, FileRead('C:\Windows\Temp\' & $Random & '\' & $Random & 'PsExec.txt'))
_GUICtrlEdit_Scroll($Output, 4)
WEnd
;Zeit rausfiltern
$Time = FileRead('C:\Windows\Temp\' & $Random & '\' & $Random & 'PsExec.txt')
$TimeDE = _StringBetween($Time, "Letzte Anmeldung", @CRLF)
$TimeEN = _StringBetween($Time, "Last Logon", @CRLF)
;Erkennung der Sprache
If IsArray($TimeDE) Then
GUICtrlSetData($Output, $TimeDE[0])
ElseIf IsArray($TimeEN) Then
GUICtrlSetData($Output, $TimeEN[0])
EndIf
GUICtrlSetData($OutputLabel, "Ausgabe")
RemoveTemp($Random)
GUICtrlSetState($Cancel,$GUI_DISABLE)
Else
MsgBox(64, "Fehlende Angaben", "Bitte fülle alle benötigten Felder aus")
EndIf
EndSwitch
WEnd
Func RemoveTemp($Random)
DirRemove("C:\Windows\Temp\" & $Random, 1)
EndFunc
Func LoadProfileNames()
$Profiles = IniReadSectionNames("Profiles.ini")
For $i = 1 To $Profiles[0]
If $i = 1 Then
$ProfileNames = $Profiles[$i]
Else
$ProfileNames = $ProfileNames & "|" & $Profiles[$i]
EndIf
Next
GUICtrlSetData(-1, $ProfileNames)
EndFunc
Func LoadProfile($SelectedProfile)
GUICtrlSetData($Host, IniRead("Profiles.ini", $SelectedProfile, "Hostname", ""))
GUICtrlSetData($User, IniRead("Profiles.ini", $SelectedProfile, "User", ""))
GUICtrlSetData($Parameter, IniRead("Profiles.ini", $SelectedProfile, "Parameter", ""))
GUICtrlSetData($Password, _StringEncrypt(0, IniRead("Profiles.ini", $SelectedProfile, "Password", ""), $EncryptKey))
If IniRead("Profiles.ini", $SelectedProfile, "Password", "") = "" Then
GUICtrlSetData($Password, IniRead("Profiles.ini", $SelectedProfile, "Password", ""))
Else
GUICtrlSetData($Password, _StringEncrypt(0, IniRead("Profiles.ini", $SelectedProfile, "Password", ""), $EncryptKey))
EndIf
GUICtrlSetData($ProfilesDropdown, $SelectedProfile)
EndFunc
Func SaveProfile()
$ProfileName = InputBox("Profil erstellen", "Bitte gib einen Namen für das Profil ein", GuiCtrlRead($ProfilesDropdown))
If $ProfileName = "" Then
MsgBox(64, "Profil speichern", "Profil wurde nicht gespeichert!")
Else
If GUICtrlRead($Password) = "" Then
$SetPassword = ""
Else
$SetPassword = _StringEncrypt(1, GUICtrlRead($Password), $EncryptKey)
EndIf
IniWriteSection("Profiles.ini", $ProfileName, "Hostname=" & GuiCtrlRead($Host) & @LF & "User=" & GuiCtrlRead($User) & @LF & "Password=" & $SetPassword & @LF & "Parameter=" & GUICtrlRead($Parameter))
EndIf
EndFunc
Func DeleteProfile()
$SelectedProfile = GuiCtrlRead($ProfilesDropdown)
IniDelete("Profiles.ini", $SelectedProfile)
GUICtrlSetData($ProfilesDropdown, "")
LoadProfileNames()
LoadProfile("Default")
EndFunc
Die Profile werden in eine Profiles.ini gespeichert, welche folgenden Aufbau hat:
Spoiler anzeigen
Würde mich über Rückmeldungen und Verbesserungsvorschläge freuen
Ahatius