IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

printf et ses bizarreries


Sujet :

Langage C++

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2008
    Messages : 12
    Points : 7
    Points
    7
    Par défaut printf et ses bizarreries
    Bonjour tout le monde!

    Je suis en train d'étudier un cours de sécurité et je vois du printf partout .

    Alors j'ai commencé à m'interesser à cette fonction!

    Ma première question concernant printf est :

    Pourquoi ceci est accepté par printf:

    Le compilateur va bien produire une erreur qui dit que printf a trop peu d'arguments mais ça passe et ça s'exécute bien sur.

    Donc la question est pourquoi les développeurs de printf ont décidé qu'on pouvait utiliser plus de formateurs dans formats string que d'arguments donné à printf?

    Ma deuxième question porte sur l'implémentation, je vois pas comment printf peut accéder à des arguments qui n'existent pas...

    Une petite dernière question sur printf .

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
        char x[10];
        x[0] = '\x00';
        x[1] = '\x01';
        x[2] = '\x02';
        x[3] = '\x03';
        printf("%d\n", x[0]);
        printf("%d\n", x[1]);
        printf("%d\n", x[2]);
        printf("%d\n", x[3]);
    Je ne comprends pas mon propre code .

    Du moins je suis surpris de l'output des printf .

    Ca me sort

    0
    1
    2
    3

    Ce que je ne comprends pas. Pour moi ça devrait juste sortir un truc farfelu c'est à dire pour le premier quand je printf x[0] il me sort 0 et pour moi il devrait prendre les 32 bits en mémoires que j'ai écrit, c'est à dire

    '\x00''\x01''\x02''\x03' ce qui devrait être égal selon l'endianess

    0000 0001 0010 0011
    ou
    1100 0100 1000 0000


    ce qui devrait valoir en décimale

    291
    ou
    50304

    Je ne suis pas tout à fait sur de ça mais bon certainement pas 0.



    Voilà si quelqu'un pouvait me donner quelques informations sur toutes ces petites questions ça me ferait vraiment plaisir!


    Bonne soirée à vous .

  2. #2
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    %x fait ce qu'il est supposé faire :
    afficher les 4 prochains bytes présent sur la stack en hexadécimal. Donc tu te retrouves à afficher tout et n'importe quoi.

    Je sais pas quel cours de sécu tu lis mais les attaques par format string ça passe plus (enfin... quasiment plus) de nos jours avec l'introduction de FORTIFY_SOURCE dans la glibc.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2008
    Messages : 12
    Points : 7
    Points
    7
    Par défaut
    Je n'ai pas dit que toutes les attaques étaient à jour, mais disons qu'on apprend les principes ce qui est toujours interessant.

    Comment c'est implémenter ça en c++, retourner les 4 prochains bytes sur le stack?

    Et soi dit en passant le comportant est soi disant indéfini dans le cas où les arguments sont pas fournis, ce que je comprends pas c'est comment on arrive à ce comportement d'afficher les bytes sur la pile .

    D'ailleurs ce que je viens de lire c'est ça :

    If the syntax of a conversion specification is invalid, behavior remains undefined, and in fact can cause program termination. If there are too few function arguments provided to supply values for all the conversion specifications in the template string, or if the arguments are not of the correct types, the results are also undefined. Excess arguments are ignored. In a number of cases, the undefined behavior has led to "Format string attack" security vulnerabilities.

    C'est là :

    http://en.wikipedia.org/wiki/Printf


    Et ma question portait sur pouquoi avoir implémenter ça comme ça et comment c'est implémenter .

    De plus à la fin de mon post je comprends pas, %d est censé affiché l'entier que je lui fournis et là il m'affiche pas du tout ce que je lui fourni... il m'affiche seulement le premier byte et non pas les 4 que j'ai mis ...

  4. #4
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par Abbadon1988 Voir le message
    Je n'ai pas dit que toutes les attaques étaient à jour, mais disons qu'on apprend les principes ce qui est toujours interessant.

    Comment c'est implémenter ça en c++, retourner les 4 prochains bytes sur le stack?

    Et soi dit en passant le comportant est soi disant indéfini dans le cas où les arguments sont pas fournis, ce que je comprends pas c'est comment on arrive à ce comportement d'afficher les bytes sur la pile .

    D'ailleurs ce que je viens de lire c'est ça :

    If the syntax of a conversion specification is invalid, behavior remains undefined, and in fact can cause program termination. If there are too few function arguments provided to supply values for all the conversion specifications in the template string, or if the arguments are not of the correct types, the results are also undefined. Excess arguments are ignored. In a number of cases, the undefined behavior has led to "Format string attack" security vulnerabilities.

    C'est là :

    http://en.wikipedia.org/wiki/Printf


    Et ma question portait sur pouquoi avoir implémenter ça comme ça et comment c'est implémenter .

    De plus à la fin de mon post je comprends pas, %d est censé affiché l'entier que je lui fournis et là il m'affiche pas du tout ce que je lui fourni... il m'affiche seulement le premier byte et non pas les 4 que j'ai mis ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    int function(int n = 0xdeadbeef, ...)
    {
       int dummy;
     
       int* addr = &dummy;
       for (int n=0; n<15; ++n, --addr)
       {
          std::cout << addr << " -> " << *addr << std::endl;
       }
    }
     
    int main()
    {
      f(0xdeadbeef, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
    }
    Accéder à une adresse quelconque en C++, qu'elle soit sur la pile ou non, ce n'est pas le plus complexe. Par contre, ça ne peut plus s'appeler du C++ (comportement non défini par la norme, peut potentiellement provoquer des problèmes).

    (accessoirement, &n pointe sur la pile, donc (&n)+1 pointe sur la valeur "1", etc.).

    Les fonctions variadiques sont généralement implémentées à l'aide d'intrinsics sur les compilateurs récents, pour éviter de faire ces choses étranges via du code C++.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  5. #5
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2008
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2008
    Messages : 12
    Points : 7
    Points
    7
    Par défaut
    Coucou,

    merci pour ta réponse, mais il n'y a donc aucun moyen pour ces fonctions variadiques de savoir le nombre d'arguments qui leur sont donnés ? Ca éviterait à printf tous ces problèmes .

    Soit dit en passant ton code ne donne pas du tout les bons résultats chez moi ( je travaille sous windows 7 64 bits code block option de compilation par défault).

    Je viens juste de le trafiquer .


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int function(int n = 0xdeadbeef, ...)
    {
       int* addr = &n+15;
       for (int n=0; n<15; ++n, --addr)
       {
          std::cout << addr << " -> " << *addr << std::endl;
       }
    }
     
    int main()
    {
      function(0xdeadbeef, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
    }

    Ceci donne les résultats attendus .

    Ceci marche aussi . Mais faudrait aller plus loin que 15 car ces petits coquins stockent des trucs entre les arguments et nos variables locales

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    #include <iostream>
     
    int function(int n = 0xdeadbeef, ...)
    {
       int dummy;
     
       int* addr = &dummy;
       for (int n=0; n<15; ++n, ++addr)
       {
          std::cout << addr << " -> " << *addr << std::endl;
       }
    }
     
    int main()
    {
      function(0xdeadbeef, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
    }
    En tout cas merci pour tes explications

  6. #6
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par Abbadon1988 Voir le message
    Coucou,

    merci pour ta réponse, mais il n'y a donc aucun moyen pour ces fonctions variadiques de savoir le nombre d'arguments qui leur sont donnés ? Ca éviterait à printf tous ces problèmes .
    Non. C++0x introduit les variadic templates qui sont beaucoup plus contrôlé - mais en C et en C++98, il n'y a aucun moyen de connaitre le nombre d'argument passé à une fonction variadique de manière sûre. Tu ne peux pas te permettre de faire une vérification sur une valeur particulière qui marquerais la fin des variables, car cette valeur peut être passée en argument de ta fonction.

    Citation Envoyé par Abbadon1988 Voir le message
    Ceci marche aussi . Mais faudrait aller plus loin que 15 car ces petits coquins stockent des trucs entre les arguments et nos variables locales
    En particulier une information rigolote : l'adresse de retour de la fonction. Ce que tu peux faire, et qui peut être drôle, c'est modifier cette adresse si tu l'identifie correctement.

    Le modèle d'appel de fonction du C et du C++ est le suivant (version simplifiée)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    // sur un appel de f(p1, p2, ..., pN)
    push pN
    ...
    push p2
    push p1
    call f
    Le call, sur un processeur x86, sauvegarde sur la pile l'adresse de retour (c'est à dire l'adresse de l'instruction suivant le call). C'est équivalent à

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    push [f]
    jump [f]
    Le fait d'avoir stocké l'adresse de retour à cet endroit est nécessaire : elle est lue par l'instruction assembleur ret à la sortie de la fonction.

    C'est bien sûr une vision très simpliste de ce qui se passe vraiment : le compilateur est tout à fait libre de ses mouvements, et il peut décider de passer tel ou tel paramètre via la pile ou via des registres, etc. Comme tu l'as dit, c'est un petit malin
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

Discussions similaires

  1. Comment lancer Eclipse en incluant ses plugins
    Par eclie dans le forum Eclipse Platform
    Réponses: 8
    Dernier message: 19/02/2009, 08h45
  2. Comment commenter ses prg pr que ses collègues comprennent ?
    Par GFA dans le forum Langages de programmation
    Réponses: 7
    Dernier message: 02/05/2003, 14h19
  3. Editer ses sources
    Par taupin dans le forum Autres éditeurs
    Réponses: 3
    Dernier message: 24/12/2002, 19h17
  4. Installer ses composants
    Par Geronimo dans le forum C++Builder
    Réponses: 14
    Dernier message: 18/06/2002, 14h51
  5. fonction printf
    Par ydeleage dans le forum C
    Réponses: 7
    Dernier message: 30/05/2002, 11h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo