Programm mit Parameterliste aufrufen


FSA

Erfahrenes Mitglied
#1
Hallo,

ich muss ein Programm von meinem Quellcode aus aufrufen und Parameter beim Aufruf übergeben.
Die Problematik besteht darin, dass die übergebenen Parameter bis zu 128.000 Zeichen lang sein können. Die üblichen Funktionen (CreateProcess, system) sind limitiert auf 32.768 Zeichen (system noch weniger). Aus Sicherheitsgründen darf ich die Parameterliste nicht in eine Datei schreiben.

Hat jemand eine Idee, wie ich so viele Zeichen als Parameter übergeben kann?

Grüße
 

cwriter

Erfahrenes Mitglied
#2
ich muss ein Programm von meinem Quellcode aus aufrufen und Parameter beim Aufruf übergeben.
Kannst du das aufgerufene Programm verändern?


Die Problematik besteht darin, dass die übergebenen Parameter bis zu 128.000 Zeichen lang sein können.
Das scheint mir sehr schlechtes Design zu sein, falls du per argc/argv übergeben willst.


Aus Sicherheitsgründen darf ich die Parameterliste nicht in eine Datei schreiben.
Irgendwas liegt im Argen bei sowas. Es ist zu geheim, um in Dateien zu schreiben, aber frei genug, dass man es zwischen Programmen durchreichen kann? (Es gibt bei Dateien auch Zugriffsrechte...)


Hat jemand eine Idee, wie ich so viele Zeichen als Parameter übergeben kann?
Ist zwar auch eine (Pseudo-)Datei, aber kannst du nicht per stdout pipen (bzw. die Windows Pipe API) benutzen?
Ansonsten gibt es die Möglichkeit, per WriteProcessMemory direkt in einen Speicherbereich zu schreiben. Oder du lädst per LoadLibrary direkt die main-Funktion aus dem anderen Programm und übergibst die Parameter dann direkt; dann fehlt aber die Process Isolation komplett.

Gruss
cwriter
 

Technipion

Erfahrenes Mitglied
#3
Okay, ich muss zugeben, dass mich das jetzt interessiert hat. Denn ich konnte in der Dokumentation kein Limit finden...
Allerdings sagt der C11-Standard auch, dass system implementierungsabhängig sein kann.
(siehe http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf unter 7.22.4.8 auf S.353)

Ich habe deshalb schnell dieses Programm runtergetippst:
C:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>


#ifndef DATASIZE
 #define DATASIZE 32768
#endif


int main()
{
    char some_data[DATASIZE]; // this will be echo-ed
    
    char pool[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    const size_t pool_len = strlen(pool);
    
    // filling some_data with ... some data
    for (size_t i = 0; i < (DATASIZE - 1); i++) {
        some_data[i] = pool[ (i * 13) % pool_len ];
    }
    some_data[DATASIZE - 1] = '\0'; // don't forget to terminate
    
    // quick and dirty:
    char command[DATASIZE + 1024];
    sprintf(command, "echo \"%s\" > test_%d.txt", some_data, DATASIZE);
    
    system(command); // execute command
    
    return 0;
}
Und mit diesem Bash-Skript einen Test laufen lassen:
Bash:
#!/bin/bash

echo "Testing C11 system command..."

for dsize in $(seq 1024 1024 1048576); do
  gcc -o main.bin -DDATASIZE=$dsize main.c
  ./main.bin
done

echo "Done. Listing created files:"

ls -lS

echo "Remember: You can remove all created files with \"rm test_*.txt\""
Bei mir hat der system call noch mit 130.048 Zeichen geklappt. Danach wird der Aufruf einfach geschluckt. Getestet auf einem Ubuntu 19.04 64Bit System. Ich frage mich, wie dieses Limit zustande kommt...

Irgendwas liegt im Argen bei sowas. Es ist zu geheim, um in Dateien zu schreiben, aber frei genug, dass man es zwischen Programmen durchreichen kann? (Es gibt bei Dateien auch Zugriffsrechte...)
Kann mich da allerdings nur anschließen. Bad design nennt man sowas. Vielleicht erklärst du uns wirklich mal, was du eigentlich tun willst. Klingt so als möchtest du Passwörter oder ähnliches per Argument übergeben. Davon kann man aber eigentlich echt nur abraten.

Gruß Technipion
 
Gefällt mir: FSA

cwriter

Erfahrenes Mitglied
#5
Denn ich konnte in der Dokumentation kein Limit finden...
<limits.h>
ARG_MAX scheint in diese Richtung zu gehen. Bei mir ist
Code:
getconf ARG_MAX
2097152 (2MB)
Du kommst auf recht viel weniger, aber die Environment-Daten müssen ja auch noch rein...
Schlussendlich kann aber theoretisch auch die Shell, das Terminal oder ein anderes Programm auf dem Weg zu exec() limitieren - auf jeden Fall ist argv nie für grosse Datenmengen gedacht gewesen, dafür gibt es Pipes.

Wir dürfen aber nicht vergessen, dass @FSA ganz offensichtlich auf Windows arbeitet. Dort sind die Limits in aller Regel noch etwas strikter, was Pfadlängen etc. angeht.

Gruss
cwriter
 

FSA

Erfahrenes Mitglied
#6
Danke für die vielen Antworten. Vorab: Ich habe keine Lösung zu meiner Frage gefunden, aber einen anderen Ansatz gewählt (Erklärung folgt).

Kannst du das aufgerufene Programm verändern?
Leider nicht.

Das scheint mir sehr schlechtes Design zu sein, falls du per argc/argv übergeben willst.
Da stimme ich dir absolut zu. Ich habe jedoch kein Einfluss auf das aufgerufenen Programm. Dieses akzeptiert die Daten nur als Parameter.

Ansonsten gibt es die Möglichkeit, per WriteProcessMemory direkt in einen Speicherbereich zu schreiben.
Das wäre auch mein Gedanke gewesen, hätte ich es anders nicht mehr lösen können. Ist allerdings nicht so angenehm für verschiedene Plattformen.

Okay, ich muss zugeben, dass mich das jetzt interessiert hat. Denn ich konnte in der Dokumentation kein Limit finden...
Ich habe auch nur die Angabe für die WinAPI gefunden. Für Unix/Linux gab es nichts. system() ist unter Windows noch ein Sonderfall. Da liegt das Limit bei ca 8100 Bytes, die genaue Zahl finde ich gerade nicht mehr.

Kann mich da allerdings nur anschließen. Bad design nennt man sowas. Vielleicht erklärst du uns wirklich mal, was du eigentlich tun willst. Klingt so als möchtest du Passwörter oder ähnliches per Argument übergeben. Davon kann man aber eigentlich echt nur abraten.
Ja wie schon geschrieben habe ich mich auch ziemlich über diese Art der Datenübergabe geärgert. Aber ohne Einfluss auf das aufzurufende Programm zu haben, blieb mir nicht viel übrig. Es handelt sich nicht um sensible Daten.

Bei mir hat der system call noch mit 130.048 Zeichen geklappt.
Hast du eine Theorie, wie so eine "ungerade" Zahl rauskommen kann? Wenn ich die Zeit finde, suche ich mal bisschen im Linux Kernel nach hinweisen.

Stimme zu! denn argv-Übergabe (command lines) ist überhaupt nicht geheim. Andere Prozesse können das mitlesen.
Wenn man sensible, unverschlüsselte Daten weitergeben muss, hat man meistens sowieso etwas falsch gemacht ;)

Du kommst auf recht viel weniger, aber die Environment-Daten müssen ja auch noch rein...
Ich kann mir nicht vorstellen, dass die ca 700kb groß sind. Auf jeden Fall ein interessantes Verhalten.

Wir dürfen aber nicht vergessen, dass @FSA ganz offensichtlich auf Windows arbeitet. Dort sind die Limits in aller Regel noch etwas strikter, was Pfadlängen etc. angeht.
Nein, unter Windows, macOS und GNU/Linux.

Nun zur Lösung:
Da ich das Programm nur als binary vorliegen habe, habe ich mir ein Disassembly gemacht und die für mich wichtigen Funktionen in meinem Quellcode geschrieben. Somit kann ich jetzt auf das externe Programm verzichten. Das hat zwar etwas gedauert, weil ich Assembler mit x86 Architektur nicht unbedingt flüssig lesen kann, aber letztendlich hat es gereicht, um die Logik des externen Programms nachzuvollziehen.
 

cwriter

Erfahrenes Mitglied
#7
für mich wichtigen Funktionen in meinem Quellcode geschrieben. Somit kann ich jetzt auf das externe Programm verzichten.
?
Wäre es nicht einfacher, dlopen() bzw. LoadLibrary zu verwenden? Genau für sowas ist das eigentlich gedacht.
Dein Ansatz ist grauenhaft zu warten, plattformabhängig und nicht im Sinne von "Ein Programm für einen Job".

Gruss
cwriter
 

FSA

Erfahrenes Mitglied
#8
Wäre es nicht einfacher, dlopen() bzw. LoadLibrary zu verwenden?
Das ist auch eine Möglichkeit, aber dann müsste ich die Daten wieder gesondert formatieren, um sie als Parameter zu übergeben. Da ich jetzt den benötigten Quellcode als Teil von meinem Programm habe, kann ich direkt den Zeiger auf die Daten verwenden.

Dein Ansatz ist grauenhaft zu warten, plattformabhängig und nicht im Sinne von "Ein Programm für einen Job".
Ich verstehe nicht genau, was du meinst?

Grüße
 

cwriter

Erfahrenes Mitglied
#9
Ich verstehe nicht genau, was du meinst?
Mal übertrieben angesehen: Jemand schreibt ein Programm, das ein anderes Programm benutzt. Z.B. ein Mailprogramm, das GPG für Verschlüsselung einsetzen will. Dann kann man die Operationen auf dem Dateisystem machen (Mail in Datei schreiben, GPG auf Datei ausführen, Ausgabe wieder einlesen), was sehr rein zwischen Mail und Verschlüsselung trennt, aber langsam ist.
Eine Alternative ist, eine dynamic library zu nutzen. Dann kann man die GPG-DLL/.so unabhängig vom Mailprogramm aktualisieren, um z.B. Sicherheitslücken zu schliessen, ohne, dass der Entwickler des Mailprogramms einen Finger rühren muss.
Die nächste Variante (falls vorhanden): Das Programm statisch in das eigene linken. Updates müssen durch den Autor des Mailprogramms verteilt werden, der aber nur eine neue Version kompilieren muss.
Deine Variante wäre: Du müsstest den neuen Code anschauen (oder Reverse-Engineeren), dann die Fixes anwenden, und neu kompilieren.
Eine saubere Trennung ist fast immer zu bevorzugen.

Bei deinem Fall ist es schwer abzuschätzen. Da du es per RE nachgebaut hast, kann es sich nicht um eine komplexe Funktion handeln, entsprechend ist auch das Fehlerpotential kleiner.


Das ist auch eine Möglichkeit, aber dann müsste ich die Daten wieder gesondert formatieren, um sie als Parameter zu übergeben. Da ich jetzt den benötigten Quellcode als Teil von meinem Programm habe, kann ich direkt den Zeiger auf die Daten verwenden.
Klar, wenn man das Programm nachprogrammiert, kann man eigene Shortcuts nehmen. Das geht aber wie gesagt nur bei einfachen/kleinen Programmen.

Also: Wenn du damit glücklich bist, ist ja alles gut (solange ich nie deinen Code warten muss ;) )
Aber im Allgemeinen (und das richtet sich vor allem an zukünftige Leser) holt man sich mehr Arbeit / Probleme ins Boot, wenn man solche Arten von Copy/Paste anwendet.

Gruss
cwriter
 

Technipion

Erfahrenes Mitglied
#10
Also: Wenn du damit glücklich bist, ist ja alles gut (solange ich nie deinen Code warten muss ;) )
Aber im Allgemeinen (und das richtet sich vor allem an zukünftige Leser) holt man sich mehr Arbeit / Probleme ins Boot, wenn man solche Arten von Copy/Paste anwendet.
Dem stimme ich auf jeden Fall zu. Allerdings scheint es so, als kann FSA nur bedingt etwas zu dem Stil, weil wohl schon das separate Programm schlecht designt war.

Off-topic:
Ich habe mein Bash-Skript dann noch auf verschiedenen anderen Systemen probiert und etwas damit herumgespielt. Nach etwas manpage-Lektüre bin ich dann in man execve auf folgendes gestoßen:
Code:
   Limits on size of arguments and environment
       Most  UNIX  implementations  impose some limit on the total size of the command-line argument (argv) and environment (envp) strings that may be
       passed to a new program.  POSIX.1 allows an implementation to advertise this limit using the ARG_MAX constant (either defined in <limits.h>  or
       available at run time using the call sysconf(_SC_ARG_MAX)).

       On  Linux  prior to kernel 2.6.23, the memory used to store the environment and argument strings was limited to 32 pages (defined by the kernel
       constant MAX_ARG_PAGES).  On architectures with a 4-kB page size, this yields a maximum size of 128 kB.

       On kernel 2.6.23 and later, most architectures support a size limit derived from the soft RLIMIT_STACK resource limit (see  getrlimit(2))  that
       is  in force at the time of the execve() call.  (Architectures with no memory management unit are excepted: they maintain the limit that was in
       effect before kernel 2.6.23.)  This change allows programs to have a much larger argument and/or environment list.   For  these  architectures,
       the total size is limited to 1/4 of the allowed stack size.  (Imposing the 1/4-limit ensures that the new program always has some stack space.)
       Since Linux 2.6.25, the kernel places a floor of 32 pages on this size limit, so that, even when RLIMIT_STACK is set very low, applications are
       guaranteed  to  have at least as much argument and environment space as was provided by Linux 2.6.23 and earlier.  (This guarantee was not pro‐
       vided in Linux 2.6.23 and 2.6.24.)  Additionally, the limit per string is 32 pages (the kernel constant MAX_ARG_STRLEN), and the maximum number
       of strings is 0x7FFFFFFF.

cwriter war also verdammt nahe dran. Es hängt alles von der Stackgröße des Systems ab.