PsExec GUI: Output auslesen

  • Hallo zusammen

    Ich bin gerade dabei ein GUI zu erstellen mit dem ich relativ einfach eigene und auch vordefinierte Befehle mit Hilfe von PsExec absetzen kann. Für diejenigen, die nich wissen was PsExec ist, hier eine Beschreibung. Kurz zusammen gefasst, ermöglicht PsExec es auf einem Remote-System einen Befehl abzusetzen.

    Ich bin nun soweit dass ich über das GUI einen Befehl absetzen kann. Das Problem welches ich hier allerdings noch habe, ist dass ich den Output nicht auslesen kann, welcher generiert wird. Ich denke das Problem liegt darin, dass PsExec eine Zeile schreibt, diese dann aber dann wieder löscht, und etwas neues hinein schreibt. Die Methode mit rausschreiben des Outputs in ein .txt File, möchte ich nicht, da gewisse Befehle eine lange Laufzeit haben, und ich gerne in "Echtzeit" sehen möchte was gerade läuft. Ich habe in den englischen AutoIt Foren über dieses Problem gelesen, bin aber nicht schlau geworden (ist mein erstes Mal mit diesen STDOUT usw).

    Mein Code sieht soweit wie folgt aus:

    Spoiler anzeigen
    [autoit]

    #include <GUIConstants.au3>
    #include <EditConstants.au3>
    #include <Process.au3>
    #include <Constants.au3>

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

    #Region ### START Koda GUI section ### Form=
    $Form = GUICreate("Form", 537, 728, 193, 125)
    $Host = GUICtrlCreateInput("Server01", 64, 16, 121, 21)
    $User = GUICtrlCreateInput("domain\admin", 64, 40, 121, 21)
    $Password = GUICtrlCreateInput("password", 64, 64, 121, 21, $ES_PASSWORD)
    $GetUser = GUICtrlCreateInput("Administrator", 64, 88, 121, 21)
    $GetUserTime = GUICtrlCreateButton("Loginzeit", 192, 16, 75, 25, 0)
    $Run = GUICtrlCreateButton("Run", 192, 48, 75, 25, 0)
    $Label1 = GUICtrlCreateLabel("Hostname", 8, 16, 52, 17)
    $Label2 = GUICtrlCreateLabel("Benutzer", 8, 40, 46, 17)
    $Label3 = GUICtrlCreateLabel("Passwort", 8, 64, 47, 17)
    $Label4 = GUICtrlCreateLabel("Loginuser", 8, 88, 50, 17)
    $Label5 = GUICtrlCreateLabel("Command", 8, 120, 51, 17)
    $Label6 = GUICtrlCreateLabel("Ausgabe", 8, 368, 46, 17)
    $Command = GUICtrlCreateEdit("", 8, 144, 521, 209, BitOR($ES_AUTOVSCROLL,$ES_AUTOHSCROLL,$ES_WANTRETURN))
    $Output = GUICtrlCreateEdit("", 8, 392, 521, 321, BitOR($ES_AUTOVSCROLL,$ES_WANTRETURN,$ES_READONLY))
    GUISetState(@SW_SHOW)
    #EndRegion ### END Koda GUI section ###

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

    While 1
    $nMsg = GUIGetMsg()

    Switch $nMsg
    Case $GUI_EVENT_CLOSE
    Exit

    Case $Run
    If GuiCtrlRead($Host) = "" Or GUICtrlRead($User) = "" Or GuiCtrlRead($Password) = "" Then
    MsgBox(64, "Fehlende Angaben", "Bitte fülle alle benötigten Felder aus")
    Else
    $Command = 'C:\Windows\si\PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ' & GUICtrlRead($Password) & ' ' & GUICtrlRead($Command)
    $CommandHidden = 'C:\Windows\si\PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ***** ' & GUICtrlRead($Command)

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

    $_Run = @ComSpec & ' /c ' & $Command
    GUICtrlSetData($Output, $_Run)
    $_Pid = Run ( $_Run, '', @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
    Dim $_StderrRead='', $_StdoutRead=''

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

    While ProcessExists ( $_Pid )
    Sleep(100)
    $_StderrRead = StderrRead ( $_Pid )
    If Not @error And $_StderrRead <> '' Then GUICtrlSetData($Output, GUICtrlRead($Output) & $_StderrRead & @Crlf )
    $_StdoutRead = StdoutRead ( $_Pid )
    If Not @error And $_StdoutRead <> '' Then GUICtrlSetData($Output, GUICtrlRead($Output) & $_StdoutRead & @Crlf )
    Wend
    EndIf

    Case $GetUserTime
    If GuiCtrlRead($Host) = "" Or GUICtrlRead($User) = "" Or GuiCtrlRead($Password) = "" Or GUICtrlRead($GetUser) = "" Then
    MsgBox(64, "Fehlende Angaben", "Bitte fülle alle benötigten Felder aus")
    Else
    $Command = 'C:\Windows\si\PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ' & GUICtrlRead($Password) & ' net user ' & GUICtrlRead($GetUser); & ' | findstr /B /C:"Letzte Anmeldung"'
    $CommandHidden = 'C:\Windows\si\PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ***** net user ' & GUICtrlRead($GetUser); & ' | findstr /B /C:"Letzte Anmeldung"'
    $_Run = @ComSpec & ' /c ' & $Command
    GUICtrlSetData($Output, $_Run)
    $_Pid = Run ( $_Run, '', @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
    Dim $_StderrRead='', $_StdoutRead=''

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

    While ProcessExists ( $_Pid )
    Sleep(100)
    $_StderrRead = StderrRead ( $_Pid )
    If Not @error And $_StderrRead <> '' Then GUICtrlSetData($Output, GUICtrlRead($Output) & $_StderrRead & @Crlf )
    $_StdoutRead = StdoutRead ( $_Pid )
    If Not @error And $_StdoutRead <> '' Then GUICtrlSetData($Output, GUICtrlRead($Output) & $_StdoutRead & @Crlf )
    Wend
    EndIf


    EndSwitch
    WEnd

    [/autoit]

    Falls mir jemand hierbei behilflich sein könnte, wäre ich ihm dankbar. Auch sonstige Script-Verbesserungen sind gerne erhört.

    Vielen Dank jetzt schonmal
    Ahatius

  • Eine Lösung dafür habe ich leider auch nicht ... eher das gleiche Problem ...

    Ich schicke über Netzwerk die PsExec-Befehle, ohne mir einen Erfolg oder Fehler zurückgeben zu lassen. Und das passt mir gar nicht ...

    Ich hatte mir erst überlegt eine .bat zu schreiben um ein .log von PsExec zu erzeugen und das über meine GUI wiederrum bei Fehler oder Erfolg darstellen zu lassen. Nur leider habe ich auch schon mehrere Stunden in verschiedenen Foren geschaut und erfahren, dass das Problem selber an den PsTools zu liegen scheint, da diese Informationen nicht gespeichert werden können.

    Also wenn jemand eine Idee hat wie´s funktionieren könnte würde auch ich mich freuen ;)

  • Es gibt Versionen von PsExec die liefern den Rückgabewert der remote ausgeführeten Anwendung zurück und die meisten (gerade die aktuellen) Version eben nicht. Im Anhang eine Version die den entfernten Rückgabewert übermittelt.

    Das zweite was ich sehe ist, das Du im Run() immer @ComSpec nutzt, obwohl die bei Standard-Exe-Dateien nicht notwendig ist, sondern nur bei "internen" Befehlen der Kommandozeilen-Shell wir z.B. DIR. Hier gibt es eine "DIR.EXE" nicht !

    Dein Run() sieht wie folgt aus: Run --> CMD.EXE --> PsExec.exe --> (Remotes Kommando)

    Nur ist die Frage was Du nun zurückbekommst.
    Wenn Du die Zeile 59 auskommentierst, funktioniert es weiterhin nur ist der Ablauf wie folgt:

    Dein Run() sieht wie folgt aus: Run --> PsExec.exe --> (Remotes Kommando)

    Und wenn nun PsExec die entfernte Rückgabe übermittelt, solltest Du sie auch auslesen können. Jedenfalls klappt es bei mir.

    Dateien

    Zur Nutzung dieses Forum's, ist ein Übersetzer für folgende Begriffe unerlässlich:

    "On-Bort, weier, verscheiden, schädliges, Butten steyling, näckstet, Parr, Porblem, scripe, Kompletenz, harken, manuel zu extramieren, geckukt, würglich, excell, acces oder Compilevorgeng"

  • Danke für die Antworten.

    Die Version welche du hochgeladen hast ist dem zip Namen nach Version 1.98. Diese habe ich bereits im Einsatz, daher können wir vom gleichen Stand ausgehen.

    Wie gesagt, ich habe auch bereits die englischen AutoIt Foren durchsucht, mehrere Themen dazu gefunden, allerdings ohne ein funktionierendes Ergebnis. Ich habe mir auch schonmal RemCom heruntergeladen in der Hoffnung damit arbeiten zu können. Das Problem hier: Im Gegensatz zu PsExec funktioniert dieses Tool bei exakt gleichen Bedingungen nicht. Und sollte das Problem hierfür Serverseitig sein, dann fällt dieses Tool sowieso weg.

    So wie es aussieht, muss ich wirklich fast eine temporäre Datei erstellen, in welchem die Ergebnisse gespeichert werden. Gibt es vielleicht eine Möglichkeit diese in Echtzeit auszulesen, ohne dass ich warten muss bis der komplette Befehl abgesetzt wurde?

    Edit: Es gibt Anwendungsbereiche, da ist der Output von einem Kommando weniger wichtig (z. B. wenn ich einen Dienst oder eine Applikation die einen Dienst zur Verfügung stellt starte). Wenn ich allerdings Informationen auslesen muss oder Einstellungen ändern will, dann wäre dass wichtig zu wissen, was für Ausgaben bestehen. Ich habe desweiteren auch ein mal WMIC angesehen, allerdings scheint dass alles sehr komplex, weswegen ich mich nicht noch näher damit beschäftigt habe.

    Edit2: Damit ich das ganze in ein temporäres File umleiten kann, bin ich gezwungen dass ganze mit dem Comspec am Anfang auszuführen :\

    Einmal editiert, zuletzt von ahatius (27. April 2011 um 15:27)

  • Guten Morgen

    Meine Lösung sieht im Moment wie folgt aus, und ermöglicht mir auch das auslesen der Ergebnisse in Echtzeit. Das einzige Problem dass ich habe ist, dass wenn ich einen ersten Befehl abgesetzt habe, ich keinen zweiten Befehl absetzen kann. Er schreibt den Output weder in das .txt File, noch wird es im GUI angezeigt. Hat irgendjemand eine Idee woran das liegen könnte?

    Edit: Er motzt beim 2. Befehl desweiteren auch, dass die Felder leer seien, obwohl überall etwas steht.

    Edit2: Ok, lag daran dass ich 2 Mal die gleiche Variable für 2 verschiedene Sachen gebraucht habe (das geht beim ersten Mal gut, beim zweiten Mal liest er was falsches aus)

    Spoiler anzeigen
    [autoit]


    #include <GUIConstants.au3>
    #include <EditConstants.au3>
    #include <Process.au3>
    #include <Constants.au3>

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

    #Region ### START Koda GUI section ### Form=
    $Form = GUICreate("Form", 537, 728, 193, 125)
    $Host = GUICtrlCreateInput("server01", 64, 16, 121, 21)
    $User = GUICtrlCreateInput("domain\admin", 64, 40, 121, 21)
    $Password = GUICtrlCreateInput("password", 64, 64, 121, 21, $ES_PASSWORD)
    $GetUser = GUICtrlCreateInput("Administrator", 64, 88, 121, 21)
    $GetUserTime = GUICtrlCreateButton("Loginzeit", 192, 16, 75, 25, 0)
    $Run = GUICtrlCreateButton("Run", 192, 48, 75, 25, 0)
    $Label1 = GUICtrlCreateLabel("Hostname", 8, 16, 52, 17)
    $Label2 = GUICtrlCreateLabel("Benutzer", 8, 40, 46, 17)
    $Label3 = GUICtrlCreateLabel("Passwort", 8, 64, 47, 17)
    $Label4 = GUICtrlCreateLabel("Loginuser", 8, 88, 50, 17)
    $Label5 = GUICtrlCreateLabel("Command", 8, 120, 51, 17)
    $Label6 = GUICtrlCreateLabel("Ausgabe", 8, 368, 100, 17)
    $Command = GUICtrlCreateEdit("", 8, 144, 521, 209, BitOR($ES_AUTOVSCROLL,$ES_AUTOHSCROLL,$ES_WANTRETURN))
    $Output = GUICtrlCreateEdit("", 8, 392, 521, 321, BitOR($ES_AUTOVSCROLL,$ES_WANTRETURN,$ES_READONLY))
    GUISetState(@SW_SHOW)
    #EndRegion ### END Koda GUI section ###

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

    While 1
    $nMsg = GUIGetMsg()

    Switch $nMsg
    Case $GUI_EVENT_CLOSE
    Exit

    Case $Run
    If GuiCtrlRead($Host) <> "" AND GUICtrlRead($User) <> "" AND GuiCtrlRead($Password) <> "" AND GuiCtrlRead($Command) <> "" Then
    GUICtrlSetData($Label6, "Ausgabe läuft...")
    $CommandRun = 'C:\Windows\si\PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ' & GUICtrlRead($Password) & ' ' & GUICtrlRead($Command) & ' > C:\Windows\Temp\PsExec.txt'
    $CommandHidden = 'C:\Windows\si\PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ***** ' & GUICtrlRead($Command)
    $Execute = @ComSpec & ' /c ' & $CommandRun
    $PID = Run($Execute, '', @SW_HIDE)
    While ProcessExists($PID)
    Sleep(100)
    GUICtrlSetData($Output, FileRead("C:\Windows\Temp\PsExec.txt"))
    WEnd
    GUICtrlSetData($Label6, "Ausgabe")
    Else
    MsgBox(64, "Fehlende Angaben", "Bitte fülle alle benötigten Felder aus")
    EndIf

    #cs
    Case $GetUserTime
    If GuiCtrlRead($Host) = "" Or GUICtrlRead($User) = "" Or GuiCtrlRead($Password) = "" Or GUICtrlRead($GetUser) = "" Then
    MsgBox(64, "Fehlende Angaben", "Bitte fülle alle benötigten Felder aus")
    Else
    $Command = 'C:\Windows\si\PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ' & GUICtrlRead($Password) & ' net user ' & GUICtrlRead($GetUser); & ' | findstr /B /C:"Letzte Anmeldung"'
    ;$CommandHidden = 'C:\Windows\si\PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ***** net user ' & GUICtrlRead($GetUser); & ' | findstr /B /C:"Letzte Anmeldung"'
    $_Run = @ComSpec & ' /c ' & $Command
    GUICtrlSetData($Output, $_Run)
    $_Pid = Run ( $_Run, '', @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
    Dim $_StderrRead='', $_StdoutRead=''

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

    While ProcessExists ( $_Pid )
    Sleep(100)
    $_StderrRead = StderrRead ( $_Pid )
    If Not @error And $_StderrRead <> '' Then GUICtrlSetData($Output, GUICtrlRead($Output) & $_StderrRead & @Crlf )
    $_StdoutRead = StdoutRead ( $_Pid )
    If Not @error And $_StdoutRead <> '' Then GUICtrlSetData($Output, GUICtrlRead($Output) & $_StdoutRead & @Crlf )
    Wend
    EndIf
    #ce
    EndSwitch
    WEnd

    [/autoit]

    Einmal editiert, zuletzt von ahatius (28. April 2011 um 08:19)

  • Mir ist gerade ein neues Problem aufgefallen. Wenn ich zum Beispiel einen Befehl wie ping 127.0.0.1 -t absetze, der ja unendlich lang pingt, dann hört der Prozess von alleine auch nicht auf. Jetzt würde ich es gerne fertig bringen, dass er reagiert, wenn auf Schliessen gedrückt wird oder auf den Button Abbruch, welchen ich vorher erstellt habe. Ich habe es versucht in dem ich in der While Schleife, welche das .txt laufend ausliest, If GuiGetMsg() = $GUI_EVENT_CLOSE Then Exit erstellt habe. Allerdings klappt das nicht. Da ich mich mit GUIs nicht sonderlich auskenne, wollte ich fragen ob mir jemand helfen könnte dieses Problem zu lösen?

    Code sieht wie folgt aus:

    Spoiler anzeigen
    [autoit]


    #include <GUIConstants.au3>
    #include <EditConstants.au3>
    #include <Process.au3>
    #include <Constants.au3>

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

    #Region ### START Koda GUI section ### Form=
    $Form = GUICreate("Form", 537, 728, 193, 125)
    $Host = GUICtrlCreateInput("server01", 64, 16, 121, 21)
    $User = GUICtrlCreateInput("domain\admin", 64, 40, 121, 21)
    $Password = GUICtrlCreateInput("password", 64, 64, 121, 21, $ES_PASSWORD)
    $GetUser = GUICtrlCreateInput("Administrator", 64, 88, 121, 21)
    $GetUserTime = GUICtrlCreateButton("Loginzeit", 192, 16, 75, 25, 0)
    $Run = GUICtrlCreateButton("Run", 192, 48, 75, 25, 0)
    $Label1 = GUICtrlCreateLabel("Hostname", 8, 16, 52, 17)
    $Label2 = GUICtrlCreateLabel("Benutzer", 8, 40, 46, 17)
    $Label3 = GUICtrlCreateLabel("Passwort", 8, 64, 47, 17)
    $Label4 = GUICtrlCreateLabel("Loginuser", 8, 88, 50, 17)
    $Label5 = GUICtrlCreateLabel("Command", 8, 120, 51, 17)
    $Label6 = GUICtrlCreateLabel("Ausgabe", 8, 268, 100, 17)
    $Command = GUICtrlCreateEdit("", 8, 144, 521, 100, BitOR($ES_AUTOVSCROLL,$ES_AUTOHSCROLL,$ES_WANTRETURN))
    $Output = GUICtrlCreateEdit("", 8, 292, 521, 426, BitOR($ES_AUTOVSCROLL,$ES_WANTRETURN,$ES_READONLY))
    $Cancel = GUICtrlCreateButton("Abbruch", 100, 260, 75, 25, 0)
    GUISetState(@SW_SHOW)
    #EndRegion ### END Koda GUI section ###

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

    While 1
    $nMsg = GUIGetMsg()

    Switch $nMsg
    Case $GUI_EVENT_CLOSE
    Exit

    Case $Run
    If GuiCtrlRead($Host) <> "" AND GUICtrlRead($User) <> "" AND GuiCtrlRead($Password) <> "" AND GuiCtrlRead($Command) <> "" Then
    GUICtrlSetData($Label6, "Ausgabe läuft...")
    $CommandRun = 'C:\Windows\si\PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ' & GUICtrlRead($Password) & ' ' & GUICtrlRead($Command) & ' > C:\Windows\Temp\PsExec.txt'
    $Execute = @ComSpec & ' /c ' & $CommandRun
    $PID = Run($Execute, '', @SW_HIDE)
    While ProcessExists($PID)
    If GuiGetMsg() = $GUI_EVENT_CLOSE Then Exit
    If GuiGetMsg() = $Cancel Then ExitLoop
    ;Sleep(100)
    GUICtrlSetData($Output, FileRead("C:\Windows\Temp\PsExec.txt"))
    WEnd
    ;If ProcessExists($PID) Then ProcessClose($PID)
    GUICtrlSetData($Label6, "Ausgabe")
    Else
    MsgBox(64, "Fehlende Angaben", "Bitte fülle alle benötigten Felder aus")
    EndIf

    Case $GetUserTime
    If GuiCtrlRead($Host) <> "" AND GUICtrlRead($User) <> "" AND GuiCtrlRead($Password) <> "" AND GuiCtrlRead($GetUser) <> "" Then
    GUICtrlSetData($Label6, "Ausgabe läuft...")
    $CommandRun = 'C:\Windows\si\PsExec.exe \\' & GUICtrlRead($Host) & ' -u ' & GUICtrlRead($User) & ' -p ' & GUICtrlRead($Password) & ' net user ' & GuiCtrlRead($GetUser) & ' > C:\Windows\Temp\PsExec.txt'
    $Execute = @ComSpec & ' /c ' & $CommandRun
    $PID = Run($Execute, '', @SW_HIDE)
    While ProcessExists($PID)
    Sleep(100)
    GUICtrlSetData($Output, FileRead("C:\Windows\Temp\PsExec.txt"))
    WEnd
    GUICtrlSetData($Label6, "Ausgabe")
    Else
    MsgBox(64, "Fehlende Angaben", "Bitte fülle alle benötigten Felder aus")
    EndIf

    EndSwitch
    WEnd

    [/autoit]
  • Ok, das Problem konnte ich mittlerweile auch lösen. Habe in die While Schleife einen weiteren Switch Case eingebaut, welcher GuiGetMsg abfragt.

    Mein neues Problem ist, dass ermitteln der PID der PsExec.exe welche durch das CMD gestartet wurde. Die PID die mein Script bereits ermittelt ist die vom cmd. Wenn ich den Prozess dieser PID kille, dann schliesst sich die cmd.exe, die PsExec läuft aber weiter.

    Bietet CMD irgendeine Funktion, um die PID der durch cmd gestarteten Applikation zu ermitteln?