ASM - Utilisation des registres EBP, ESP, SS
Résolu/Fermé
Stratosphr
-
23 avril 2011 à 11:00
nicocorico Messages postés 799 Date d'inscription dimanche 19 juin 2011 Statut Membre Dernière intervention 3 juillet 2018 - 26 juil. 2011 à 06:56
nicocorico Messages postés 799 Date d'inscription dimanche 19 juin 2011 Statut Membre Dernière intervention 3 juillet 2018 - 26 juil. 2011 à 06:56
7 réponses
nicocorico
Messages postés
799
Date d'inscription
dimanche 19 juin 2011
Statut
Membre
Dernière intervention
3 juillet 2018
138
Modifié par nicocorico le 26/07/2011 à 05:51
Modifié par nicocorico le 26/07/2011 à 05:51
J'ai bien vu que le sujet est résolu, mais j'aimerais apporter une petite précision;
Enter et leave sont l'équivalent de ces instructions :
Permettant de créer un cadre de pile standard basé sur EBP;
D'une part, les instructions enter et leave sont extrêmement lentes, très loin du temps d'exécution de la suite d'instructions équivalentes, d'autre part il est possible d'utiliser la pile en soustrayant à ESP la taille escomptée et en l'ajoutant à la fin de la fonction, et en utilisant ESP comme ceci :
Ce qui permet d'utiliser EBP à d'autres fins et c'est beaucoup plus rapide, mais il faut faire attention au push et pop dans la fonction, qui décalent les variables sur la pile, ainsi que les éventuels paramètres empilés de la fonction)...
Enter et leave sont l'équivalent de ces instructions :
PUSH EBP MOV EBP,ESP SUB ESP,Locals ... code ... MOV ESP,EBP POP EBP
Permettant de créer un cadre de pile standard basé sur EBP;
D'une part, les instructions enter et leave sont extrêmement lentes, très loin du temps d'exécution de la suite d'instructions équivalentes, d'autre part il est possible d'utiliser la pile en soustrayant à ESP la taille escomptée et en l'ajoutant à la fin de la fonction, et en utilisant ESP comme ceci :
Sub ESP,08; Mov [ESP],EAX; // Variable dword 1 Mov [ESP+04],EAX; // 2ème variable dword Add ESP,08; // Rétabli la pile de l'appelant
Ce qui permet d'utiliser EBP à d'autres fins et c'est beaucoup plus rapide, mais il faut faire attention au push et pop dans la fonction, qui décalent les variables sur la pile, ainsi que les éventuels paramètres empilés de la fonction)...
ghuysmans99
Messages postés
2496
Date d'inscription
jeudi 14 juillet 2005
Statut
Contributeur
Dernière intervention
5 juin 2016
339
Modifié par ghuysmans99 le 24/04/2011 à 00:33
Modifié par ghuysmans99 le 24/04/2011 à 00:33
Normalement tu ne peux pas : EBP a été affecté par la fonction appelante (runtime C) et tu es en train de massacrer sa partie de stack. Si tu veux te créer des variables locales, fais comme ceci :
EDIT: dword [ebp-4] c'est bon en fait. Confusion avec les paramètres ...
Google is your best friend
VB.NET is good ... VB6 is better !
asm_main: enter 8,0 mov dword [ebp-4], 0 mov dword [ebp-8], 64 leave ret
EDIT: dword [ebp-4] c'est bon en fait. Confusion avec les paramètres ...
Google is your best friend
VB.NET is good ... VB6 is better !
Merci beaucoup :)
J'ai regardé ce qui se disait sur "enter".
Le premier paramètre me permet d'indiquer le nombre d'octets que je souhaite utiliser, c'est bien ça ? Jusque là, pas de problème.
Le second... fixe le niveau d'imbrication de la procédure ? Je pense comprendre à peu près... Mais j'aimerais plus d'informations... J'ai vu qu'effectivement, dans les conventions d'appel du C, on mettait toujours 0. Une explication serait cependant la bienvenue...
Une dernière chose, pourquoi commencer l'assignation à [ebp-8] et pas [ebp-4] par exemple ?
Est-ce que les 8 premiers octets sont réservés ou quelque chose dans ce genre là ? Je pense d'après ce que je viens de lire, que [ebp] ne peut pas être modifiée ou doit au moins être restaurée à sa valeur d'origine avant la fin du programme. Mais pour [ebp-4] ?
Merci encore pour votre réponse :)
J'ai regardé ce qui se disait sur "enter".
Le premier paramètre me permet d'indiquer le nombre d'octets que je souhaite utiliser, c'est bien ça ? Jusque là, pas de problème.
Le second... fixe le niveau d'imbrication de la procédure ? Je pense comprendre à peu près... Mais j'aimerais plus d'informations... J'ai vu qu'effectivement, dans les conventions d'appel du C, on mettait toujours 0. Une explication serait cependant la bienvenue...
Une dernière chose, pourquoi commencer l'assignation à [ebp-8] et pas [ebp-4] par exemple ?
Est-ce que les 8 premiers octets sont réservés ou quelque chose dans ce genre là ? Je pense d'après ce que je viens de lire, que [ebp] ne peut pas être modifiée ou doit au moins être restaurée à sa valeur d'origine avant la fin du programme. Mais pour [ebp-4] ?
Merci encore pour votre réponse :)
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
ghuysmans99
Messages postés
2496
Date d'inscription
jeudi 14 juillet 2005
Statut
Contributeur
Dernière intervention
5 juin 2016
339
25 avril 2011 à 11:54
25 avril 2011 à 11:54
(Tu peux me tutoyer, y'a pas de raison que je sois le seul à le faire). Un petit peu plus d'explications sur ENTER : http://www.posix.nl/linuxassembly/nasmdochtml/nasmdoca.html#section-A.27. Perso je comprends pas trop à quoi ça sert et je n'ai jamais vu un programme qui se servait de la 2ème opérande ...
On dirait que tu n'as pas très bien compris comment fonctionne l'appel de fonctions avec paramètres et stackframe ... Pas grave, je t'explique.
On utilise principalement la pile lorsqu'on veut appeler des fonctions. Cette pile commence à partir du bout de la mémoire et va en descendant. Dans l'archi x86, on ne peut pas empiler autre chose que 4 octets (int, pointeur) via l'instruction push. Le pointeur de pile ESP indique où se trouve le dernier élément de la pile en mémoire et EBP sert dans les fonctions à accéder facilement aux paramètres et aux variables locales (s'il y en a). Lorsqu'on fait un CALL, le processeur empile l'adresse de retour (=adresse de l'instruction qui suit le CALL) et saute à l'adresse de la fonction. Pour quitter la fonction, on fait un RET qui dépile la dernière adresse sur la pile et saute à celle-ci. Pour appeler une fonction en ASM, on fait ça http://img20.imageshack.us/img20/489/appelfct.png : on empile les paramètres dans l'ordre inverse et on appelle la fonction. Il y a donc ça sur la pile lorsqu'on rentre dans celle-ci (la pile avance vers le haut) : http://img96.imageshack.us/img96/3955/stackappel.png. On n'a toujours pas réservé de la place pour les locales ... Pour ce faire, il faut sauver EBP, y mettre l'adresse contenue dans ESP et retirer n octets à ESP pour les locales (voilà ce que fait ENTER n, 0). Le stack devient donc comme ceci http://img193.imageshack.us/img193/9603/stackvarlocales.png. Il est donc logique que la première variable locale ne se trouve pas en ebp car il y a à cette adresse le contenu de l'ancienne valeur d'EBP ...
Si y'a un truc que tu ne comprends pas dans ce pavé, n'hésite pas ...
On dirait que tu n'as pas très bien compris comment fonctionne l'appel de fonctions avec paramètres et stackframe ... Pas grave, je t'explique.
On utilise principalement la pile lorsqu'on veut appeler des fonctions. Cette pile commence à partir du bout de la mémoire et va en descendant. Dans l'archi x86, on ne peut pas empiler autre chose que 4 octets (int, pointeur) via l'instruction push. Le pointeur de pile ESP indique où se trouve le dernier élément de la pile en mémoire et EBP sert dans les fonctions à accéder facilement aux paramètres et aux variables locales (s'il y en a). Lorsqu'on fait un CALL, le processeur empile l'adresse de retour (=adresse de l'instruction qui suit le CALL) et saute à l'adresse de la fonction. Pour quitter la fonction, on fait un RET qui dépile la dernière adresse sur la pile et saute à celle-ci. Pour appeler une fonction en ASM, on fait ça http://img20.imageshack.us/img20/489/appelfct.png : on empile les paramètres dans l'ordre inverse et on appelle la fonction. Il y a donc ça sur la pile lorsqu'on rentre dans celle-ci (la pile avance vers le haut) : http://img96.imageshack.us/img96/3955/stackappel.png. On n'a toujours pas réservé de la place pour les locales ... Pour ce faire, il faut sauver EBP, y mettre l'adresse contenue dans ESP et retirer n octets à ESP pour les locales (voilà ce que fait ENTER n, 0). Le stack devient donc comme ceci http://img193.imageshack.us/img193/9603/stackvarlocales.png. Il est donc logique que la première variable locale ne se trouve pas en ebp car il y a à cette adresse le contenu de l'ancienne valeur d'EBP ...
Si y'a un truc que tu ne comprends pas dans ce pavé, n'hésite pas ...
Tu ne devrais pas toucher ni a [ebp] ni à [ebp-4] car ils stockent respectivement (si je me souviens bien) l'adresse de ta fonction et l'adresse de retour. Donc si tu modifie [ebp-4] tu fera planter ton programme lorsque tu appelera ret.
Utiliser enter est la bonne solution. Si tu code sous MASM tu peux utiliser le mot clé LOCAL.
SS est le segment de pile. C'est généralement le même que CS et DS, ça dépend du programme.
Quelle est ta deuxième question?
Utiliser enter est la bonne solution. Si tu code sous MASM tu peux utiliser le mot clé LOCAL.
SS est le segment de pile. C'est généralement le même que CS et DS, ça dépend du programme.
Quelle est ta deuxième question?
ghuysmans99
Messages postés
2496
Date d'inscription
jeudi 14 juillet 2005
Statut
Contributeur
Dernière intervention
5 juin 2016
339
Modifié par ghuysmans99 le 25/04/2011 à 12:26
Modifié par ghuysmans99 le 25/04/2011 à 12:26
ils stockent respectivement l'adresse de ta fonction et l'adresse de retourL'adresse de la fonction ne se trouve pas sur le stack ! Regarde mon dernier lien. Il ne code pas sous MASM vu qu'il est sous Linux et on se fiche complètement des segments ici (on est en 32 bits et on ne fait pas de prog système)
Merci à vous deux pour les infos, les liens, et le pavé ! (Au moins je vois que je ne suis pas le seul à m'intéresser à l'assembleur ^^) Je comprends qu'ENTER remplace une série d'instructions ASM qui reviennent très souvent dans les programmes que j'ai regardé. Je comprends aussi à présent pourquoi ces instructions sont utilisées, et je t'en remercie.
Ma deuxième question, la voici :
j'ai désassemblé un fichier objet (avec objdump et également gdb).
Cette instruction n'est pas claire pour moi :
MOV $0x1c, %eax
Je vois qu'il semble y avoir une adresse hexadécimale mais à quoi correspond-elle exactement ?
Ma deuxième question, la voici :
j'ai désassemblé un fichier objet (avec objdump et également gdb).
Cette instruction n'est pas claire pour moi :
MOV $0x1c, %eax
Je vois qu'il semble y avoir une adresse hexadécimale mais à quoi correspond-elle exactement ?
ghuysmans99
Messages postés
2496
Date d'inscription
jeudi 14 juillet 2005
Statut
Contributeur
Dernière intervention
5 juin 2016
339
27 avril 2011 à 00:37
27 avril 2011 à 00:37
Le désassembleur t'a produit un code en syntaxe AT&T (inversion dst<->src, préfixes obligatoires pour chaque truc). En syntaxe Intel, ça donne mov eax,0x1C
ghuysmans99
Messages postés
2496
Date d'inscription
jeudi 14 juillet 2005
Statut
Contributeur
Dernière intervention
5 juin 2016
339
27 avril 2011 à 22:55
27 avril 2011 à 22:55
C'est pas une adresse mais une valeur (préfixe $). L'adresse 0x0 ne représente rien du tout, et essayer d'y accéder c'est être sûr que le programme se fera tuer par l'OS pour violation de mémoire. Tu pourrais choisir tes adresses en bidouillant un peu mais quel intérêt ?
Merci de m'avoir éclairé. J'ai l'impression d'apprendre un nouveau langage à chaque code source que je regarde ^^ Je vais tâcher de relire quelques tutoriels sur l'assembleur avant de continuer à faire du grand n'importe quoi en pensant avoir compris. J'avais besoin de ces explications. Merci à toi !
fiddy
Messages postés
11069
Date d'inscription
samedi 5 mai 2007
Statut
Contributeur
Dernière intervention
23 avril 2022
1 840
29 avril 2011 à 22:57
29 avril 2011 à 22:57
OllyDbg sur GNU/Linux ??? Il n'est que sur windows à ma connaissance...
Modifié par nicocorico le 26/07/2011 à 07:24
Lorqu'on appelle Enter, chacunes des fonctions imbriquées sauvegardent EBP sur la pile, enregistrant par là-même le pointeur du cadre de pile de la fonction appelante, donc chaque fonction peut accéder au cadre de pile de la fonction appelante en lisant le contenu de [EBP]; Et si on appelle Enter avec 1 en second paramètre, la procédure recopie la valeur présente en [EBP] avant de sauvegarder EBP à son tour et de le fixer sur le nouveau cadre de pile;
On a donc maintenant le cadre de pile de l'appelante en [EBP] et le cadre de pile de l'appelante de l'appelante en [EBP + 04]... Et ainsi de suite en fonction du niveau d'imbrication;
Ce tableau étant élargi à chaque niveau d'imbrication, on peut alors accéder à l'ensemble des paramètres et locales de l'ensemble des fonctions appelantes, sans avoir à repasser les paramètres similaires de fonctions en fonctions... Puissant, non ?