Forum Programmation.c essayer de recupérer l'adresse de retour d'une fonction

Posté par  . Licence CC By‑SA.
Étiquettes : aucune
6
22
juil.
2020

bonjour à tous,

J'essaye de récupérer l'adresse de retour stockée dans la pile puis de faire un jump sur cette adresse afin de revenir à mon main, je vous montre mon code puis vous explique ma démarche :

    int addOne ( int value )
    {
        char * ptr = (char*)&value; // la variable value est situé a RBP-0x14
        void (*adresseDeRetour) (void) = (ptr + 28);
        adresseDeRetour();
        return ++value; //on arrive jamais ici
    }

    int main (int argc, char **argv)
    {   
        printf("coucou\n");
        int v = addOne(65);    
        return 0;
    }

en regardant via la commande : objdump -M intel -DTCs ./a.out , on voit que ma variable value est stocké à la position RBP-0x14, donc je fais un (ptr + 20) afin d'etre au niveau du RBP qui est stocké dans la RAM, puis je fais + 8 (car RBP est un registre de 8 octet afin de récupérer l'adresse de retour, d'ou le (ptr + 28). Puis je fais adresseDeRetour() afin de ratterrir dans mon main() mais j'ai en sortie :

coucou
Erreur de segmentation

Pour moi au niveau de la RAM quand la fonction addOne est appelé, le processeur fait :
->on stock l'adresse de retour
->on stock RBP
->j'ajoute les arguments de la fonction …

avez vous une idée de comment faire ?

merci d'avance pour votre aide

  • # .

    Posté par  . Évalué à 8.

    Salut,

    Le plus simple est d'ajouter un niveau d'indirection à adresseDeRetour :

     void (**adresseDeRetour) (void) = (ptr + 28);
    

    Sinon ton appel saute dans la pile (à l'adresse où se trouve l'adresse de retour).

    Autre problème : tu ne restaures pas le cadre de pile de l'appelant. Quand tu rends la main à main() (pour ainsi dire), on reste dans addOne() en réalité et à la fin de main(), on revient après l'appel de addOne(). Ajoute un printf("coucou2\n"); après l'appel à addOne() et tu verras que "coucou2" sera affiché deux fois.

    • [^] # Re: .

      Posté par  . Évalué à 4.

      Et bien sûr, tu adaptes l'appel :
      (*adresseDeRetour)();

      • [^] # Re: .

        Posté par  . Évalué à 1.

        yes merci énormément pour ta reponse t'as dit tout ce dont j'avais besoin de savoir !

  • # setjmp et longjmp

    Posté par  . Évalué à 3.

    Plutôt que de bidouiller la pile à la main, tu devrais utiliser les fonctions C, setjmp() et longjmp() qui sont faites pour ça. Elles permettent de faire un nonlocal goto.

    http://manpagesfr.free.fr/man/man3/setjmp.3.html

    • [^] # Re: setjmp et longjmp

      Posté par  . Évalué à 1.

      merci pour ta réponse, alors oui c'est du bidouillage mais c'est dans le but de me faire progresser avec le fonctionnement des pointeurs et de la logique de fonctionnement du code

      • [^] # Re: setjmp et longjmp

        Posté par  . Évalué à 4.

        Je ne vois pas le lien entre jouer dans la pile et apprendre les pointeurs.

        Le format de la pile, qui dépend de l’ABI du langage et de la plateforme ne sont intéressant qu’en debug ou si on écrit un module en assembleur… à la rigueur pour https://www.root-me.org/ aussi ça peut servir.

        Sur du x86, le retour de la fonction, est plutôt (à ma connaissance) contenue dans le registre AX enfin maintenant c’est EAX ou RAX pour tout ce qui est en dessous de 64bits.

      • [^] # Re: setjmp et longjmp

        Posté par  . Évalué à 2. Dernière modification le 24 juillet 2020 à 14:22.

        La valeur de retour dans la pile doit être juste avant ptr.

        void (*adresseDeRetour) (void) = (&ptr - 1); // Arythmétique pointeur, on se décale de la taille de la variable… soit un pointeur.

        Mais ça ne marchera pas car tes registres SP et BP ne seront pas remis à jour correctement.

        En gros si ça marchait, tu irais finir main puis au retour de main tu retournerais dans addOne pour la finir pour retourner encore dans main.

        Avec l’aléa que l’accès à la variable v du main est relative au BP courant… donc tu jardines probablement la pile n’importe où, donc là ou ça casse une adresse de retour.

        Édit: en fait baulieu à déjà répondu. J’avais lu trop vite.

  • # appel recursif infini

    Posté par  . Évalué à 2. Dernière modification le 24 juillet 2020 à 14:16.

    Ce que tu fais n'est pas un retour à l'appellant, mais un appel de fonction, dans le cas où ton appel serait correct (voir le commentaire de beaulieu1), l'instruction "adresseDeRetour();" aurait pour effet d'appeler la fonction "main", qui à son tour appellerait la fonction "addOne", qui appellerait "main"… jusqu'à ce que ton programme finisse par être tué pour un débordement de pile.

    • [^] # Re: appel recursif infini

      Posté par  . Évalué à 1.

      le programme marche tres bien et il n'y a aucun débordement pile, il se termine correctement.

      • [^] # Re: appel recursif infini

        Posté par  . Évalué à 2.

        effectivement je l'ai lu un peu vite.

        ton appel te revoie à la troisième ligne du main (à peu près, à voir dans le code assembleur généré comment a été faite l'affectation de la valeur de retour à v), qui du coup fait un return 0 qui te fait revenir juste après l'appel adresseDeRetour() et tu reviens vers main avec le return ++value; et ton programme se termine.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.