Anzeige:
Ergebnis 1 bis 15 von 15

Thema: C: Argument vs. String, wo istder Unterschied?

  1. #1
    Registrierter Benutzer
    Registriert seit
    30.07.2001
    Ort
    München
    Beiträge
    40

    C: Argument vs. String, wo istder Unterschied?

    Hallo zusammen,
    ich habe hier die rijndael AES Schlüssel Beispielprogramme und verzweifel dabei diese bei mir in mein Programm zu integrieren.

    Das ist mein Problem:

    Das C Programm encrypt verschlüsselt einen Text folgendermaßen:
    echo "Hallo Welt" > text.txt
    cat text.txt | encrypt hugo text.bin
    Der Passphrase ist also Hugo und der verschlüsselte Text wird nach text.bin geschrieben.

    Das C Programm decrypt entschlüsselt den Text wieder wie folgt und gibt den entschlüsselten Text nach stdout aus:
    decrypt hugo text.bin
    Hallo Welt
    Jetzt will ich stufenweise die Funktion in mein C Programm integrieren (ohne popen oder so).
    Nur scheitere ich schon am einfachsten Problem.

    auszug encrypt.c:

    int main(int argc, char **argv)
    {
    unsigned long rk[RKLENGTH(KEYBITS)];
    unsigned char key[KEYLENGTH(KEYBITS)];
    int i;
    int nrounds;
    char *password;
    FILE *output;

    password = argv[1];
    for (i = 0; i < sizeof(key); i++)
    key[i] = password != 0 ? *password++ : 0;
    ....
    Ich habe mir gedacht ich tausche das in
    password = "hugo";
    kompiliere und teste, ob das noch geht. Ergebnis: ich kann des Text nicht mehr entschlüsseln, es handelt sich wohl um das falsche Passwort.

    Nun meine Frage, wo ist der Unterschied in einem Argument und einem Text String?
    adios

    Los_Andros

  2. #2
    Registrierter Benutzer
    Registriert seit
    08.07.2002
    Beiträge
    377
    nur so ne Ahnung, versuchs aber mal mit
    Code:
    password = "hugo\0";
    Amilo D - 2,8 Ghz - ATI Radeon 9000
    Debian GNU/Linux 3.1 (Sarge)

  3. #3
    Registrierter Benutzer
    Registriert seit
    30.07.2001
    Ort
    München
    Beiträge
    40
    leider schon in allen Varianten probiert:

    password = "hugo\0";
    password = "hugo\n\0";
    password = "hugo\0\n";
    password = "hugo\n";

    Alles ohne Erfolg ..... :-(
    adios

    Los_Andros

  4. #4
    Registrierter Benutzer Avatar von lokicall
    Registriert seit
    17.12.2005
    Beiträge
    53
    Code:
    password = argv[1];
    muss da nicht ein strcpy stehen, weil das ja Zeichenketten sind?

  5. #5
    Registrierter Benutzer Avatar von sommerfee
    Registriert seit
    02.07.2006
    Beiträge
    1.603
    Zitat Zitat von Los_Andros Beitrag anzeigen
    Nun meine Frage, wo ist der Unterschied in einem Argument und einem Text String?
    Nirgendwo. (Ok, stimmt nicht ganz, die Unterschiede sind aber in der Regel irrelevant.)

    Code:
    password = argv[1];
    und
    Code:
    password = "hugo";
    sind identisch, wenn das erste Argument "hugo" ist.

    Vielleicht liegt der Fehler woanders, wird noch argc abgefragt etc.?

    Liebe Grüße,
    Axel

    Nachtrag: Hast du eine Bezugsquelle für dein encrypt.c, welches du verwendest, für uns?

    P.S. @lokicall: Nein, kein strcpy, da "password" ein uninitialisierter Zeiger ist würde dies zum Absturz führen.

  6. #6
    Registrierter Benutzer
    Registriert seit
    30.07.2001
    Ort
    München
    Beiträge
    40
    also ich kopiers einfach mal rein, den Link hab ich grad nicht parat:

    Als Argument übergebe ich beim aufruf
    hugo

    deshalb sollte das doch identisch sein mit
    password = "hugo";

    #include <stdio.h>
    #include "rijndael.h"

    #define KEYBITS 256

    int main(int argc, char **argv)
    {
    unsigned long rk[RKLENGTH(KEYBITS)];
    unsigned char key[KEYLENGTH(KEYBITS)];
    int i;
    int nrounds;
    char *password;
    FILE *output;

    password = argv[1];
    for (i = 0; i < sizeof(key); i++)
    key[i] = password != 0 ? *password++ : 0;
    output = fopen(argv[2], "wb");
    if (output == NULL)
    {
    fputs("File write error", stderr);
    return 1;
    }
    nrounds = rijndaelSetupEncrypt(rk, key, 256);
    while (!feof(stdin))
    {
    unsigned char plaintext[16];
    unsigned char ciphertext[16];
    int j;
    for (j = 0; j < sizeof(plaintext); j++)
    {
    int c = getchar();
    if (c == EOF)
    break;
    plaintext[j] = c;
    }
    if (j == 0)
    break;
    for (; j < sizeof(plaintext); j++)
    plaintext[j] = ' ';
    rijndaelEncrypt(rk, nrounds, plaintext, ciphertext);
    if (fwrite(ciphertext, sizeof(ciphertext), 1, output) != 1)
    {
    fclose(output);
    fputs("File write error", stderr);
    return 1;
    }
    }
    fclose(output);
    }
    adios

    Los_Andros

  7. #7
    Registrierter Benutzer
    Registriert seit
    30.07.2001
    Ort
    München
    Beiträge
    40
    adios

    Los_Andros

  8. #8
    Registrierter Benutzer Avatar von sommerfee
    Registriert seit
    02.07.2006
    Beiträge
    1.603

    Red face

    Mache mal folgendes:

    Code:
    decrypt hugo text.bin
    copy text.bin aaa.bin
    decrypt hugo aaa.bin
    Daran sieht man sehr schön, daß der Originalcode schon murks ist.

    Das Problem liegt hier:
    Code:
      for (i = 0; i < sizeof(key); i++)
        key[i] = password != 0 ? *password++ : 0;
    Er kopiert hier gnadenlos 8 Bytes, egal wie lang das Passwort ist oder nicht. Was aber genau hinter dem "hugo" (und dem abschließenden 0-Byte) steht, weiß aber nur der Wind, bzw. genauer gesagt der Startup-Code des Compilers. In der Regel werden hier ein paar 0-Bytes und "text.bin" (also das nächste Argument) stehen, daher klappt es in der Regel, solange der Dateiname ebenfalls gleich ist und für beide Programme der gleiche Compiler verwendet wurde.

    Ändere das mal in encrypt.c und decrypt.c nach:
    Code:
      for (i = 0; i < sizeof(key); i++)
        key[i] = *password != 0 ? *password++ : 0;
    Und viola, jetzt klappt es auch, wenn man text.bin nach aaa.bin kopiert und es dort heraus dekodiert. Und jetzt mache mal weiter und ersetze

    Code:
    password = argv[1];
    durch

    Code:
    passwort = "hugo";
    und auch das wird dann auf wundersame Weise funktionieren...

    Liebe Grüße,
    Axel
    Geändert von sommerfee (15-05-2007 um 15:17 Uhr)

  9. #9
    Registrierter Benutzer
    Registriert seit
    30.07.2001
    Ort
    München
    Beiträge
    40
    wow ..... danke.
    Ich gebe zu, ich verstehe es nicht ganz, aber du hast recht, funktioniert einwandfrei.

    Der Unterschied ist einmal password, dan andere Mal *password.

    Und genau diese Zeile habe ich nicht richtig verstanden, kein wunder, dass es nicht geht. Habe vorher immer probiert sogar in Hex den string auszugeben, um Unterschiede festzustellen.
    adios

    Los_Andros

  10. #10
    Registrierter Benutzer Avatar von sommerfee
    Registriert seit
    02.07.2006
    Beiträge
    1.603
    Zitat Zitat von Los_Andros Beitrag anzeigen
    Ich gebe zu, ich verstehe es nicht ganz, aber du hast recht, funktioniert einwandfrei.

    Der Unterschied ist einmal password, dan andere Mal *password.
    Ich versuche es mal genauer zu erklären:

    Code:
    for (i = 0; i < sizeof(key); i++)
        key[i] = password != 0 ? *password++ : 0;
    Dieser Code versucht sich die ersten 8 (=sizeof(key)) Zeichen des Passwortes zu kopieren. Ist das Passwort kürzer, soll key[] mit passenden 0-Bytes aufgefüllt werden, deswegen die Fallunterscheidung bei der Zuweisung.

    Daß der * vor "password != 0" fehlt, ist schlicht und einfach ein Bug. Die Abfrage "password != 0" (identisch mit "password != NULL") ergibt keinen Sinn, da argv[1] und damit auch password niemals ein NULL-Pointer ist. Stattdessen muß das aktuelle Zeichen (also *password) auf das Ende des Wortes abgetestet werden.

    Also ist der "soll"-Zustand:

    key[0] = 'h';
    key[1] = 'u';
    key[2] = 'g';
    key[3] = 'o';
    key[4] = '\0';
    key[5] = '\0';
    key[6] = '\0';
    key[7] = '\0';

    Den Code dafür könnte man übrigens auch viel einfacher und verständlicher kodieren:

    Code:
    strncpy( key, password, sizeof( key ) );
    Kopiert max. sizeof( key ) Zeichen und füllt ggf. mit Leerzeichen auf, also genau das, was man haben möchte.

    Zurück zum Bug: Durch den Bug ist aber z.B. folgender "ist"-Zustand daraus geworden:

    key[0] = 'h';
    key[1] = 'u';
    key[2] = 'g';
    key[3] = 'o';
    key[4] = '\0';
    key[5] = 't'; /* Vermutlich steht der Anfang von "text.bin" hinter "hugo"!?*/
    key[6] = 'e';
    key[7] = 'x';

    Auch diesen Code könnte man einfacher haben, wenn man denn wollte:

    Code:
    memcpy( key, password, sizeof( key ) );
    Was genau daraus geworden ist, weiß nur der Startup-Code des Compilers, aber auf jeden Fall waren die letzten 3 Bytes von key[] Murks und dieser Murks hing eben davon ab, was *hinter* dem Passwort stand. Wenn du also "password = "hugo"" geschrieben hattest, stand hinter "hugo" was anderes als wenn dort "password = argv[1]" stehen würde. Und deswegen kam ein anderes Ergebnis dabei heraus.

    Stelle dir vor, du schreibst:

    Code:
    const char string[] = "abc";
    und greifst anschließend auf string[4] zu, das wäre der gleiche Murks, weil nur string[0] bis string[3] ein wohldefiniertes Ergebnis haben.

    Wenn noch Fragen offen sind: Immer her damit.

    Liebe Grüße,
    Axel
    Geändert von sommerfee (15-05-2007 um 16:12 Uhr) Grund: Ergänzt

  11. #11
    Registrierter Benutzer
    Registriert seit
    30.07.2001
    Ort
    München
    Beiträge
    40
    Ahhhh
    Katsching, verstanden, danke für die ausführliche Erklärung.
    Hätte ich also zufällig als Passwort "12345678" genommen, hätte ich den Bug nicht bemerkt.
    Was passiert in dem Fall, dass
    password
    größer 8 Zeichen ist?

    Schneidet er hier ab oder nimmt er 8 Byte Blöcke ....?
    adios

    Los_Andros

  12. #12
    Registrierter Benutzer Avatar von sommerfee
    Registriert seit
    02.07.2006
    Beiträge
    1.603
    Zitat Zitat von Los_Andros Beitrag anzeigen
    Hätte ich also zufällig als Passwort "12345678" genommen, hätte ich den Bug nicht bemerkt.
    Wenn das mit den 8 Zeichen stimmen würde, dann ja. Da es aber nicht stimmt (siehe unten), hätte man erst ab Passwörtern mit mindestens 32 Zeichen den Fehler nicht bemerkt.

    Was passiert in dem Fall, dass password größer 8 Zeichen ist?

    Schneidet er hier ab oder nimmt er 8 Byte Blöcke ....?
    Er schneidet einfach ab.

    Allerdings nicht nach 8 Zeichen, sondern nach KEYBITS/8 = 256/8 = 32 Zeichen, da war ich wohl zu blöde zum Rechnen, sorry.

    Liebe Grüße,
    Axel

  13. #13
    Registrierter Benutzer
    Registriert seit
    30.07.2001
    Ort
    München
    Beiträge
    40
    gracie.

    danke für die Hilfe
    adios

    Los_Andros

  14. #14
    Registrierter Benutzer
    Registriert seit
    07.05.2007
    Beiträge
    656
    Zitat Zitat von sommerfee Beitrag anzeigen
    ...
    Code:
    strncpy( key, password, sizeof( key ) );
    ...
    Code:
    memcpy( key, password, sizeof( key ) );
    Vorsicht! Das mag dann gut gehen, wenn alle Routinen, die key benutzen, explizit nur die Bytes key[0] bis key[sizeof(key)-1] auslesen. Jede String-Funktion ([s|f]printf, strcat, strcpy, ...) kann gegen die Wand fahren, wenn password>=sizeof(key) ist. Es fehlt nämlich das in C für char* verbindliche \0 am Ende, da in diesem Fall jedes Byte von key mit Werten aus password belegt wird. Ein typischer Fall für Stack-Overflow- oder Buffer-Overflow-Angriffe.

    Sicherer ist es, die Größe des char-Array von vornherein um ein Byte größer als die gewünschte Anzahl zu deklarieren und bei Copy-Funktionen _immer_ sizeof(key)-1 anzugeben - vorher initialisieren ist auch nicht schlecht, nicht alle Compiler machen das bei der Deklaration automatisch.

    Das wirkt sich besonders fatal dann aus, wenn key als char* an Funktionen übergeben wird, die kennen nämlich die key-Deklaration nicht und wissen also nicht, wie lang er sein soll.

    Beispiel:
    Code:
    jan@jack:~/tmp> cat buf_ovfl.c
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char **argv) {
      char ziel[32];
      char *quelle = "1234567890123456789012345678901234567890";
      memcpy(ziel,quelle,sizeof(ziel));
      printf("%s\n", ziel);
      return(0);
    }
    jan@jack:~/tmp> make buf_ovfl
    cc     buf_ovfl.c   -o buf_ovfl
    jan@jack:~/tmp> ./buf_ovfl
    12345678901234567890123456789012�@�
    Besser:
    Code:
    jan@jack:~/tmp> cat buf_ovfl.c
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char **argv) {
      char ziel[33];
      char *quelle = "1234567890123456789012345678901234567890";
      memset(ziel, 0, sizeof(ziel));
      memcpy(ziel,quelle,sizeof(ziel)-1);
      printf("%s\n", ziel);
      return(0);
    }
    jan@jack:~/tmp> make buf_ovfl
    cc     buf_ovfl.c   -o buf_ovfl
    jan@jack:~/tmp> ./buf_ovfl
    12345678901234567890123456789012
    Jan

  15. #15
    Registrierter Benutzer
    Registriert seit
    30.07.2001
    Ort
    München
    Beiträge
    40
    Irgendwie komme ich nicht so richtig auf nen richtigen Weg.
    Also Passwort einbauen hat super funktioniert, jetzt habe ich aber noch das folgende Problem:

    Ich erzeuge in meinem C Programm einen String mit ner Menge Systeminformationen und nem Zeitstempel.

    Der Zeichenstring sieht beispeilsweise wie folgt aus:
    guest ! system ! 2.6.18.8-0.3-default ! 1180405715 ! 08323584 ! ....


    Bei dem ursprünglichen "encrypt" wurde das so gemacht:
    cat datei.txt | encrypt binäres_output_file.bin
    Entschlüsselt mit:
    decrypt binäres_output_file.bin


    Das hat prima funkitoniert, aber man sieht deutlich, das mit dem cat ist Blödsinn, ich will im C Programm direkt verschlüsseln, ohne externen Aufruf.
    Also habe ich meinen encrypt wie folgt modifiziert:

    Code:
    ...
    char *license_key = "12345-hugo";
    unsigned long rk[RKLENGTH(KEYBITS)];
    unsigned char key[KEYLENGTH(KEYBITS)];
    ...
            FILE *cryptfile;
            for (i = 0; i < sizeof(key); i++)
            key[i] = *license_key != 0 ? *license_key++ : 0;
            cryptfile = fopen("encrypt.bin", "wb");
            if (cryptfile == NULL) {
                    fputs("File write error", stderr);
                    return 1;
            }
            nrounds = rijndaelSetupEncrypt(rk, key, 256);
                    char plaintext[sizeof(textstring)] = "";
                    unsigned char ciphertext[256];
                    strcat(plaintext,textstring);
                    printf("verschluesselnder Text: %s\n",plaintext);
                    rijndaelEncrypt(rk, nrounds, plaintext, ciphertext);
                    if (fwrite(ciphertext, sizeof(ciphertext), 1, cryptfile) != 1) {
                            fclose(cryptfile);
                            fputs("File write error", stderr);
                            return 1;
                    }
    soweit so gut, (das mit dem strcat(plaintext,textstring) war nur ein Test, habs auch direkt probiert)
    Jetzt mache ich meinen "decrypt" und erhalte folgendes Ergebnis, hier mal soll und ist:

    IST:
    guest ! systemxy9¦û·mç·°öú·

    SOLL
    guest ! systemxyz ! 2.6.18.8-0.3-default ! 1180405715 ! 08323584 ! ....



    Entschlüsselnt scheint also teilweise zu klappen, aber eben nur teilweise ...
    adios

    Los_Andros

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •