PsExec GUI: Befehle Remote ausführen

  • 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
    [autoit]


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

    [/autoit] [autoit][/autoit] [autoit]

    #Region ### START Koda GUI section ### Form=
    ;Erstelle GUI
    $GUI = GUICreate("PsExec GUI", 601, 757, 193, 125)

    [/autoit] [autoit][/autoit] [autoit]

    ;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

    [/autoit] [autoit][/autoit] [autoit]

    ;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)

    [/autoit] [autoit][/autoit] [autoit]

    ;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)

    [/autoit] [autoit][/autoit] [autoit]

    ;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))

    [/autoit] [autoit][/autoit] [autoit]

    ;Dropdowns
    $ProfilesDropdown = GUICtrlCreateCombo("", 192, 8, 161, 25)

    [/autoit] [autoit][/autoit] [autoit]

    ;Menüs laden
    LoadProfileNames()
    LoadProfile("Default")

    [/autoit] [autoit][/autoit] [autoit]

    ;Initialisiere GUI
    GUISetState(@SW_SHOW)
    #EndRegion ### END Koda GUI section ###

    [/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    ;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

    [/autoit] [autoit][/autoit] [autoit]

    Func RemoveTemp($Random)
    DirRemove("C:\Windows\Temp\" & $Random, 1)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    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

    [/autoit] [autoit][/autoit] [autoit]

    Func DeleteProfile()
    $SelectedProfile = GuiCtrlRead($ProfilesDropdown)
    IniDelete("Profiles.ini", $SelectedProfile)
    GUICtrlSetData($ProfilesDropdown, "")
    LoadProfileNames()
    LoadProfile("Default")
    EndFunc

    [/autoit]

    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