Weiterleiten von Argumenten an anderes Programm


#1

Folgendes Problem:
Es gibt ja so manchen Cross-Compiler, bei dem Entwickler von Programmen, die damit kompiliert wurden, auf make- und ninja-Skripte setzen.
Will man das Programm oder ein anderen Programm selber kompilieren, dann funktioniert jedoch leider gar nichts, weil man die ganzen Compiler- und Linker-Optionen nicht richtig gesetzt hat.

Daher habe ich nun kleine Hilfsprogramme geschrieben, die leider nicht alle funktionieren (und ich weiß nicht warum).

Als Hilfsprogramm ist hier erst einmal hallo.c genannt, das auf Linux zu “hallo” kompiliert wird.

#include <stdio.h>

int main() {
    printf("Hallo zusammen!\n");
    return 0;
}

Um mir die Übergabe auf der Kommandozeile anzusehen, habe ich folgendes kleines Programm printit.c

#include <stdio.h>

int main(int argc, char *argv[]) {
  int i=0;
  printf("\n[->] %s", argv[0]);

  for (i=1; i<argc; i++) {
     printf(" %s", argv**);
  }

  printf("\n");
  return 0;
}

Ein Test mit der Eingabe von
$ ./printit.orig egreg wgrgh wrgh
gibt auch
[->] ./printit egreg wgrgh wrgh
aus.
So weit so gut.
Das heißt, wenn ich den gcc durch das Programm ersetze, dann werden mir die Kommandozeileoptionen zurückgegeben, die während des Kompilierens übergeben wurden.

Nun will ich jedoch nicht einfach nur die Compiler durch solche Binaries ersetzen, denn der Compiler soll auch gleichzeitig seine Aufgabe erfüllen.
Daher möchte ich, daß das eigentliche Programm ebenfalls aufgerufen wird.

Hier mein Programm printit4.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>

int main(int argc, char *argv[]) {
   char *string = argv[0];
   int ret;
   int i=0;
   printf("\n\n[->] %s", argv[0]);

   for (i=1; i<argc; i++) {
     printf(" %s", argv**);
   }

   printf("\n\n");

   strncat(string, ".orig",5);
   execvp(string, argv);

   return -1;
}

Der erste Test ist nun das Binary “hallo” in “hallo.org” umzubenennen und “printit4” zu “hallo” zu kopieren.
Ausführen von “hallo” liefert dann

./hallo

[->] ./hallo

Hallo zusammen!

Wuderbar. Nun benenne ich “printit” in “printit.org” um und “printit4” kopiere ich zu “printit”.
Aufruf von “printit” mit Argumenten:

 ./printit dew wegewg 4 646 jm   rtg4 4t4

[->] ./printit dew wegewg 4 646 jm rtg4 4t4

[->] ./printit.orig orig  4 646 jm rtg4 4t4

Man sieht, daß an das ursprüngliche “printit4”-Programm alles richtig übergeben wurde. Nur hat dieses Programm dann die Argumente nicht richtig an das Original “printit” übergeben. Warum?

Grüße
theuserbl


#2

Das ist einer dieser Fälle, wo es sein könnte, dass eine weniger ausführliche Beschreibung leichter verständlich wäre :stuck_out_tongue_winking_eye: printit, printit4, printit.org oder orig, und die einzige Frage, die ich mir die ganze Zeit stelle: Warum printit4? Warum nicht printit13?

Aber mal im Ernst: Ich bin aus C schon ein bißchen raus, aber wenn mit den Argumenten irgendwas nicht stimmt, dann könnte das daran liegen, das strncat als erstes Argument einen Array erwartet, wo alles rein passt, was rein soll - und argv[0] ist sicher kein solcher Array (d.h. ich vermute du überschreibst da irgendwelchen Speicher). Ob das wirklich die Ursache ist? Nunja… *handflächen nach oben halt und mehrmals hin- und her-dreh* Undefined Behavior …

Mit GROB!!! sowas wie

char newString[1000];
...
strcpy (newString, string);
strncat(newString, ".orig",5);
execvp(newString, argv);

könnte es richtiger sein (aber noch lange nicht richtig… ich habe execvp IIRC nie verwendet…)


#3

Moin,

ja - du hast einfach nur einen Buffer-Overflow.

Du weist gar nicht wieviel Speicher die in argv[0] zur Verfügung steht. Im Gegenteil, das bekommst Du geliefert und hast es nicht anzufassen. Du musst in dem Fall davon ausgehen, das am aktuellen Ende von argv[0] kein Platz mehr ist. Wenn Du also argv[0] ändern willst, musst Du den gesamten Vektor ändern und dir mehr Platz verschaffen.

Der Vorschlag von Marco sieht erstmal sinnvoll aus. Wenn argv[0] und newString identisch sein müssen, sollte ein einfaches argv[0] = newString reichen.

hand, mogel


#4

Zweimal Aufruf von strncat beisst sich. Den Rest habn meine zwei Vorredner schon gesagt (Speicherplatz reservieren…):

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>

int main(int argc, char *argv[]) {
   char * firstArg = malloc( sizeof(char)*((sizeof(argv[0])/sizeof(argv[0][0]))+1) );
   int i;
   
   printf("\n[->] %s", argv[0]);
   for (i=1; i<argc; i++) {
     printf(" %s", argv**);
   }
   printf("\n");
   
   firstArg[0] = '\0';
   strcpy(firstArg, argv[0] );
   strncat(firstArg, "1", 2);
   printf("First: |%s|\n", firstArg);
   argv[0] = firstArg;
   if (strcmp("./hallo1111", firstArg) != 0) {
       return execvp(firstArg, argv);
   }

   return EXIT_SUCCESS;
}

( sizeof(argv[0]) + 1 hätte auch genügt, aber wir wollen richtig sein )

Es gibt die drei Dateien: hallo1.c , hallo11.c und hallo111.c …

Mit einem Make-File: make hallo1 hallo11 hallo111 (gcc, ANSI-C, “übertrieben genau”)

Aufruf: Benutzername:~/workspace $ ./hallo1 eins zwei drei vier

Und die Ausgabe wäre:


[->] ./hallo1 eins zwei drei vier
First: |./hallo11|

[->] ./hallo11 eins zwei drei vier
First: |./hallo111|

[->] ./hallo111 eins zwei drei vier
First: |./hallo1111|

So etwas auch vorstellbar:

       if (execvp(firstArg, argv) < 0) {
           return EXIT_SUCCESS;
       }

   return EXIT_SUCCESS;

@Marco13 : Das: `` hebt sich nicht genug ab. :thinking:


#5

öhm - nein, sowie nie und niemals

weil Du immer mit einem EXIT_SUCCESS beendets, auch wenn execvp schief gelaufen ist


#6

Nunja, einfach nur return EXIT_SUCCESS; hätte blöd ausgesehen, und normalerweise prüft man mit < 0 auf Fehler hin.


#7

“Blöd aussehen”. Das klingt ja technisch.

Ich kenne execvp nicht, und die Doku unter https://linux.die.net/man/3/execvp klang so kompliziert, dass ich es auf die Schnelle nicht in aller Tiefe nachvollzogen habe. Aber allgemein: Im Fehlerfall kann man entweder EXIT_FAILURE oder einen Fehlercode zurückgeben. NICHT EXIT_SUCCESS.