Remote Shell



  • Ivo schrieb:

    Dumm war nur, ich hatte niemals irgendwelche Rueckmeldungen (Fehlermeldungen usw) bekommen.

    Deswegen hab ich ja geschrieben, er soll Pipes einsetzen.



  • Abend 😉

    ja, da sag ich gleich mal vielen herzlichen Dank für die guten Tips. Werde mir besagte msdn Seiten zu den Pipes durchlesen. Mit FTP habt ihr natürlich recht. Kleiner Denkfehler meinerseits... Naja finde Netzwerkprogrammierung eben unheimlich spannend und bin mich grad noch im Reinfuchsen, obwohl die Programmierung mit Winsock schon ganz gut hinhaut, obwohl ich jetzt noch nicht soo viel Erfahrung in C/C++ habe. Mein Problem ist, dass ich C und Cpp Code gerne mische. Das ganze soll eben ein kleines (großes ;)) Projekt werden und Netzwerke interessieren mich eben am meisten.

    Also nochmal vielen Dank für die Tips und ich werde euch bestimmt mal wieder nerven 😉 Sorry schonmal dafür.

    Beste Grüße,
    brownie



  • hi!

    Hab mich jetzt bisschen informiert und so wie ich das Verstanden habe, kann eine unbenannte Pipe entweder nur Lesen oder Schreiben. Aber ich brauche doch beides. Der Server soll ja die Info die vom Client kommt an die cmd weitergeben und dann die Antwort von cmd wieder auslesen. Also brauch ich doch wohl eine benannte Pipe oder? Oder heißt das nur, dass man das nicht gleichzeitig machen kann (Read und Write).

    Grüße



  • Wenn es Dir gelingt die Standardausgabe des Servers auf einen string umzuleiten, sollte es gehen. Dann hast Du alle Meldungen der cmd auf Deinen Remoterechner.
    Musst du nur richtig implementieren.

    Ivo



  • hmmm. habe jetzt die msdn seiten ein bisschen überflogen. ich denke ein gutes tutorial bezüglich dem Einsatz von Pipes wäre nicht schlecht 😉

    Wenn ihr was habt, lasst es mich bitte wissen...

    Bisher habe ich es so verstanden, dass man die der cmd die Befehle nicht direkt übergibt (bräucht ich übrigens auch infos, wie man eigentlich die Befehle an die CMD bekommt, also wie man sie "öffnet"), sondern die Pipe mit der CMD verknüpft und die Befehle im Programm an die Pipe weitergibt. die Pipe macht dann das eigentliche weiterleiten. Aber wie man Die CMD jetzt mit der Pipe verknüpft? Keine Ahnung, und konnte auch nicht die Lösung aus der msdn hilfe entnehmen...



  • Die einfachste und schlechteste Moeglichkeit ist sicher

    system(string.c_str());
    

    Ivo



  • Ok, das ist also ein Befehl direkt an die cmd. Das heißt ja aber, dass ich den direkten Weg gehe und nicht über Pipes. D.h. ich habe keine Fehlermeldungen...

    Pipes währen da schon eleganter denke ich. Aber hab leider immer noch kein gutes Tut zu den anonymen gefunden.

    Grüße



  • Sodala. Hab es jedenfalls schonmal geschafft die cmd zu starten und eine Pipe zu erstellen. Aber wie verknüpfe ich Pipe mit CMD und root.exe(mein Server)??

    Hier mein Code:

    #include <windows.h> 
    #include <tchar.h>
    #include <stdio.h> 
    
    int main(void)
    {
    	//PIPE
    	HANDLE hReadPipe;
    	HANDLE hWritePipe;
    	SECURITY_ATTRIBUTES sa;
    	//Sicherheitshinweise der Pipe
        sa.nLength= sizeof(SECURITY_ATTRIBUTES); //Wie groß die Struktur SEC_ATR ist
        sa.lpSecurityDescriptor = NULL;			 //Zugriffsrechte für jeden
        sa.bInheritHandle = TRUE;				 
    
    	//PROCESS
    	STARTUPINFO si;				//Infos über Größe,Ort usw der Child App.
        PROCESS_INFORMATION pi;		//Gibt Infos über Process ID, Handle, Thread usw. Für Child App.
    	ZeroMemory( &si, sizeof(si) );
        si.cb = sizeof(si);			//Größe der Struktur in Bytes wird hier ermittelt.
        ZeroMemory( &pi, sizeof(pi) );
    
    	//PROZESS ERSTELLEN
    	if(!CreateProcess(NULL,
    		"C:\\WINDOWS\\system32\\cmd.exe",
    		NULL,
    		NULL,
    		FALSE,
    		0,
    		NULL,
    		NULL,
    		&si,
    		&pi))
    	{
    		printf("CreateProcess failed (%d)\n", GetLastError());
    		return 0;
    	}
    	else printf("CreateProcess started...\n");
    
    	//PIPE ERSTELLEN
    	if(!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0))
    		printf("Problem :%d",GetLastError);
    	else printf("Pipe started...\n");
    
    	//Warten bis die cmd geschlossen wird
    	WaitForSingleObject( pi.hProcess, INFINITE );
    
    	//Die Shell Wieder aufräumen
    	CloseHandle( pi.hProcess );
        CloseHandle( pi.hThread );
    
    	return 1;
    }
    

    Grüße 😉



  • Mit dem system uebergibst Du einfach eine Zeichenkette an die root.exe.
    Oder besser gesagt, die Daten die root.exe ueber TCP/IP empfaengt uebergibst Du so an die CMD.
    Nun muss es Dir nur noch gelingen die Standardausgabe der CMD (auf der Serverseite) in einen String oder aehnliches zu packen. Wenn du das geschaft hast, schickt Du die auf die Reise. Oder sieh dir einfach mal den Code von Telnet bzw. OpenSSH an. So geht es richtig und es ist kein Gefrickel. Gefrickel ist immer etwas "mai dai"

    Ivo



  • du meinst ich soll meine Befehle gar nicht per Pipe an die Cmd weiterleiten? Und CreateProcess brauch ich auch nicht? Aber wie kann ich die Ausgabe der Cmd mit meinem server Programm auslesen? Da brauch ich doch eine Pipe oder gibt es eine einfache andere Möglichkeit?

    Grüße



  • Ja, diese Ausgabe der cmd bekomme ich ja nur über eine Pipe... Ich versteh noch nicht wirklich wie ich die Pipe mit dem Prozess in Verbindung bekomme.
    Jedenfalls muss ich doch den Output von meinem Prozess, also von der cmd auf den Input meiner Pipe "routen" d.h. auf hReadPipe.

    Wenn das Funktioniert müsste ich ja theoretisch nur noch das Programm starten und prallel dazu eine cmd aufmachen und da zb "DIR" eingeben. Dann müsste doch bei meinem eigenen Programm das Ergebnis ausgespuckt werden oder nicht?

    Grüße



  • Du hast das schon richtig erkannt: Du brauchst eine Pipe zum Schreiben an den aufgerufenen Prozess und eine zum Lesen dessen, was der aufgerufene Prozess in seine Standardausgabe schreibt.
    Das Schreibende der einen Pipe behält der Aufrufer, das Leseende vererbt er an STDIN des aufgerufenen Prozesses.
    Von der zweiten Pipe behält der Aufrufer das Leseende und vererbt das Schreibende an STDOUT des aufgerufenen Prozesses.
    Der Aufrufer hat also zwei Pipes, von denen er jeweils nur ein Ende benutzt.

    Hier ist ein ganz gutes Beispiel:
    http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx



  • Tja mein Denkansatz ist vielleicht richtig, aber irgendwie steig ich nicht so dahinter. Vielleicht ist das Thema auch einfach noch zu komplex für mich. Hab mir das etwas einfacher vorgestellt. Allerdings hab ich schon verstanden wie die Pipe geroutet werden muss. Habe mal folgendes gemacht, aber irgendwie gibt das Programm nur die anzahl meines Feldes in kryptischer Form aus:

    #include <windows.h>
    #include <tchar.h>
    #include <stdio.h>
    
    int main(void)
    {
        //PIPE
        HANDLE hReadPipe;
        HANDLE hWritePipe;
        SECURITY_ATTRIBUTES sa;
        //Sicherheitshinweise der Pipe
        sa.nLength= sizeof(SECURITY_ATTRIBUTES); //Wie groß die Struktur SEC_ATR ist
        sa.lpSecurityDescriptor = NULL;             //Zugriffsrechte für jeden
        sa.bInheritHandle = TRUE;   
    
        //PIPE ERSTELLEN
        if(!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0))
            printf("Problem :%d",GetLastError);
        else printf("Pipe started...\n");
    
        //PROCESS
        STARTUPINFO si;                //Infos über Größe,Ort usw der Child App.
        PROCESS_INFORMATION pi;        //Gibt Infos über Process ID, Handle, Thread usw. Für Child App.    
    	ZeroMemory( &si, sizeof(si) );
        si.cb = sizeof(si);            //Größe der Struktur in Bytes wird hier ermittelt.
        ZeroMemory( &pi, sizeof(pi) );
    	si.hStdInput=NULL;
    	si.hStdOutput=hWritePipe;
    	si.hStdError=hWritePipe;
    	si.dwFlags=STARTF_USESTDHANDLES;
    	CloseHandle(hWritePipe);
    
        //PROZESS ERSTELLEN
        if(!CreateProcess(NULL,
            "C:\\WINDOWS\\system32\\cmd.exe",
            NULL,
            NULL,
            NULL,
            0,
            NULL,
            NULL,
            &si,
            &pi))
        {
            printf("CreateProcess failed (%d)\n", GetLastError());
            return 0;
        }
        else printf("CreateProcess started...\n");
    
    	char buf[100];
    	DWORD reDword;
    	ReadFile(hReadPipe,buf,99,&reDword,0);
    	printf(buf);
    
        //Warten bis die cmd geschlossen wird
        WaitForSingleObject( pi.hProcess, INFINITE );
    
        //Die Shell Wieder aufräumen
        CloseHandle( pi.hProcess );
        CloseHandle( pi.hThread );
    
        return 1;
    }
    


  • Erstens bin ich mir nicht sicher, ob Du das Pipe-Handle, daß Du weitergibst, beim Aufrufer erst NACH dem CreateProcess schließen solltest, zweitens mußt Du beim CreateProcess als fünften Parameter true übergeben.



  • Jo dann funktioniert es auch. Wenn ich das Programm starte bekomme ich die ersten Zeilen von der cmd... Cool 😉 Aber ich glaube irgendwie es ist nicht so optimal direkt den buf in printf zu übergeben.....

    Wie dem auch sei. Wie kann ich jetzt "lauschen", also warten ob die cmd was zu sagen hat?



  • Hab es so gelöst:

    [cpp]
    while(true)
    {
    if(ReadFile(hReadPipe,buf,99,&reDword,0)!=0)
    printf(buf,"\n");
    }[code]

    Allerdings nicht sehr performant... Wenn ich das Fenster rumziehe, dann zieht es schlieren nach sich. Gibts da evtl. was besseres, als so eine Endloschleife?

    Achja, und wenn ich ne andre CMD starte und befehle eingebe, dann kommt nichts an bei meinem Programm. Liegt das daran, dass die cmd die ich öffne ein ganz neues Handle bekommt und keinen Einfluss haben kann auf mein per CreateProcess geöffnetes cmd?



  • Ganz kurz noch (ich muß langsam schlafen gehen):
    Du kannst Dich nicht darauf verlassen, daß Dein ReadFile auch wirklich 99 Byte bekommt, ergo solltest Du beim printf nicht blind den ganzen buf ausgeben.
    Selbstverständlich bekommt eine weitere cmd, die Du zB per Hand öffnest, eigene Bereiche für STDIN und STDOUT.
    Die von Deinem Programm geöffnete bekommt ja dafür eigens die Enden der Pipes. Das ist natürlich nicht der Fall, wenn Du eine Konsole ohne entsprechende Vorbereitung öffnest.
    Endlosschleife muß ja nicht sein, da ReadFile nur zurückkehrt, wenn es etwas gelesen hat, oder ein Fehler aufgetreten ist.

    Also so:

    while(ReadFile((HANDLE)pipe,buffer,255,&x,NULL) && GetLastError() != ERROR_BROKEN_PIPE)
    {
       ...
    

    Damit Du die Kontrolle über Dein Programm behältst, solltest Du das eventuell in einen eigenen Thread verlagern.
    Du könntest dann im Hauptthread über eine Pipe (die Du bisher in Deinem Beispiel noch nicht hast), Befehle an die Konsole senden (dir, type xxx.dat, wasweißichnochwas), und über die bereits vorhandene Pipe die Ausgabe abholen(was Du ja auch schon tust).
    Ich hab da in grauer Vorzeit schon mal ein prima Beispiel (sogar mit deutschen Kommentaren im Netz gefunden), das poste ich hier einfach mal so, wie ich es entdeckt habe:

    #include <stdio.h>
    #include <windows.h>
    #include <stdlib.h>
    
    // ReadData
    // Diese Funktion wird anschliessen als eigener Thread gestarted und liest Daten aus
    // der mit stdout und stderr verbundenen Pipeline und gibt sie einfach auf der
    // Konsole aus.
    unsigned long __stdcall ReadData(void *pipe)
    {
      DWORD x;           // Anzahl der gelesenen Bytes
      char buffer[255];  // Puffer
    
      // Solange lesen, bis die Pipeline von der anderen Seite geschlossen wird
    	while(ReadFile((HANDLE)pipe,buffer,255,&x,NULL) && GetLastError() != ERROR_BROKEN_PIPE)
    		fwrite(buffer,x,1,stdout);
    
      fflush(stdout);
    
      // eigenes Handle schliessen
      CloseHandle((HANDLE)pipe);
    
      return 0;
    }
    
    // WriteData
    // Diese Funktion wird anschliessend als eigener Thread gestarted und schreibt ein
    // paar Befehle auf stdin des Kommandointerpreters cmd. Diese werden in einer Art
    // Batchbetrieb abgearbeitet.
    unsigned long __stdcall WriteData(void *pipe)
    {
      DWORD ignore;
    
      // Ein paar Daten schreiben (es ist sicherlich ein wenig overkill, diese paar
      // Zeichen durch einen eigenen Thread schreiben zu lassen - ich moechte
      // hier nur das Prinzip demonstrieren)
      WriteFile((HANDLE)pipe,"dir /s *.exe\n",13,&ignore,NULL);
      WriteFile((HANDLE)pipe,"cd /d C:\\\n",10,&ignore,NULL);
      WriteFile((HANDLE)pipe,"type autoexec.bat\n",18,&ignore,NULL);
    
      // Pipeline schliessen - damit wird der anderen Seite EOF signalisiert
      CloseHandle((HANDLE)pipe);
    
      return 0;
    }
    
    int main()
    {
      // Handles fuer die beiden Pipelines
      HANDLE hClientOut_rd,hClientOut_wr; 
      HANDLE hClientIn_rd,hClientIn_wr;
    
      // Erzeugen der beiden Pipelines - ich gebe NULL fuer den Zeiger auf
      // die SECURITY_ATTRIBUTES an. Dadurch werden beide Handles als nicht
      // vererbbar gekennzeichnet.
      if(!CreatePipe(&hClientOut_rd,&hClientOut_wr,NULL,0)) {
        printf("Can not create PIPE!\n");
        exit(5);
      }
    
      if(!CreatePipe(&hClientIn_rd,&hClientIn_wr,NULL,0)) {
        printf("Can not create PIPE!\n");
        exit(5);
      }
    
      // Die Handles, die der Kindprozess benoetigt werden als vererbbar 
      // gekennzeichnet. Dies kann entweder unter NT ueber SetHandleInformation
      // erfolgen oder unter Windows 95 ueber DuplicateHandle
      #ifdef NT
        SetHandleInformation(hClientOut_wr,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
        SetHandleInformation(hClientIn_rd,HANDLE_FLAG_INHERIT,HANDLE_FLAG_INHERIT);
      #else
        DuplicateHandle(GetCurrentProcess(),/*source*/hClientOut_wr,
          GetCurrentProcess(),/*dest*/&hClientOut_wr,0,/*bInheritHandle*/TRUE,
          DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
        DuplicateHandle(GetCurrentProcess(),hClientIn_rd,GetCurrentProcess(),
          &hClientIn_rd,0,/*bInheritHandle*/TRUE,
          DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
      #endif
    
      STARTUPINFO si;
    
      memset(&si,0,sizeof(STARTUPINFO));
    
      // stdin, stdout und stderr des Kindes in STARTUPINFO eintragen
      si.cb         = sizeof(STARTUPINFO);
      si.dwFlags    = STARTF_USESTDHANDLES;
      si.hStdInput  = hClientIn_rd;
      si.hStdOutput = hClientOut_wr;
      si.hStdError  = hClientOut_wr;
    
      PROCESS_INFORMATION pi;
    
      // cmd als Kindprozess ohne eigene Konsole starten
      if(!CreateProcess(NULL,"cmd",NULL,NULL,TRUE,NORMAL_PRIORITY_CLASS | DETACHED_PROCESS,
        NULL,NULL,&si,&pi)) {
        printf("Kein Process??\n");
        exit(5);
      }
    
      // um die Systemresourcen zu schonen koennen wir das nicht benotigte
      // Thread Handle gleich wieder schliessen
      CloseHandle(pi.hThread);
    
      // Schliessen der nicht mehr benoetigten Handles, die ich an den Prozess
      // uebergeben habe. Nur so kann ich eine 'broken pipe' feststellen, wenn
      // der Kind-Prozess beendet wird.
      CloseHandle(hClientIn_rd);
      CloseHandle(hClientOut_wr);
    
      HANDLE threads[3];
      unsigned long ignore;
    
      // Starten des Lese- und Schreibthreads
      threads[0] = CreateThread(NULL,0,WriteData,(void *)hClientIn_wr,0,&ignore);
      threads[1] = CreateThread(NULL,0,ReadData,(void *)hClientOut_rd,0,&ignore);
    
      // Prozess ebenfalls in Warteliste eintragen
      threads[2] = pi.hProcess;
    
      // Warten bis alle fertig sind
      WaitForMultipleObjects(3,threads,TRUE,INFINITE);
    
      // und tschuess...
      exit(0);
    }
    


  • verstehe. Ich brauche also eine zweite Pipe um Signale an die cmd zu senden. Werde mir das ganze nochmal genauer Ansehen 😉 Aber im Großen und Ganzen hab ichs verstanden (denke ich ;)). Danke für das coole Beispiel!!!

    PS: Da ich noch am C/C++ lernen bin, meint ihr, dass der C++ Primer eine gute Wahl ist (wird da auch Netwerkkram beschrieben und etwas Windoof Programmierung?

    Grüße. brownie



  • Genau. Weil man bei einer anonymous Pipe an einem Ende nur lesen und am anderen Ende nur schreiben kann. Es gibt daneben noch named Pipes, dort kann man an beiden Enden lesen und schreiben, ist aber für Deine Zwecke untauglich.
    Das von Dir erwähnte Buch kenne ich nicht, wird aber von vielen empfohlen.
    Für die reine Windows-Programmierung empfehle ich Windows-Programmierung von Charles Petzold, das ist zwar schon ziemlich betagt, aber imho noch immer sehr gut, wenn es darum geht, das Message-System und die Programmierung von Windows-Elementen zu verstehen.
    Ich halte es aber für besser, zuerst mal C++ unabhängig von einem OS zu lernen, wenn man sich da einigermaßen bewegen kann, ist es noch Zeit genug, sich um ein bestimmtes OS zu kümmern.

    Übrigens: Mit den Pipes bist Du schon mittendrin in der Windowsprogrammierung!


Anmelden zum Antworten