Expérimentez pour appeler une fonction du programme créé en utilisant un pointeur de fonction et objdump.
Vous pouvez avoir une petite idée de ce que signifie appeler une fonction et comment la fonction est exécutée.
L'environnement expérimental est Linux (CentOS7 64 bits) + gcc (version 4.8.5).
Code source utilisé pour les expériences. Il s'agit d'un exemple qui utilise un pointeur de fonction pour traiter l'adresse passée en argument lorsque le programme est exécuté.
sample.c
#include <stdio.h>
#include <stdlib.h>
void dog(void)
{
printf("wan wan\n");
}
void cat(void)
{
printf("nyaaan\n");
}
void birds(void)
{
printf("piyo piyo\n");
printf("chun chun\n");
printf("kah kah\n");
}
int main(int argc, char* argv[])
{
void (*myfunc)(void); /*C'est le pointeur de fonction*/
if (argc != 2) {
printf("%s <address>\n", argv[0]);
exit(EXIT_FAILURE);
}
/*Convertit la chaîne de caractères hexadécimaux passée à l'argument en valeur numérique*/
myfunc = (void(*)(void))strtol(argv[1], NULL, 16);
/*Courir*/
myfunc();
return 0;
}
Compilez le code source créé et analysez le programme terminé avec objdump.
$ gcc sample.c
$ objdump -d a.out
~ Omis ~
000000000040060d <dog>:
40060d: 55 push %rbp
40060e: 48 89 e5 mov %rsp,%rbp
400611: bf 50 07 40 00 mov $0x400750,%edi
400616: e8 a5 fe ff ff callq 4004c0 <puts@plt>
40061b: 5d pop %rbp
40061c: c3 retq
000000000040061d <cat>:
40061d: 55 push %rbp
40061e: 48 89 e5 mov %rsp,%rbp
400621: bf 58 07 40 00 mov $0x400758,%edi
400626: e8 95 fe ff ff callq 4004c0 <puts@plt>
40062b: 5d pop %rbp
40062c: c3 retq
000000000040062d <birds>:
40062d: 55 push %rbp
40062e: 48 89 e5 mov %rsp,%rbp
400631: bf 5f 07 40 00 mov $0x40075f,%edi
400636: e8 85 fe ff ff callq 4004c0 <puts@plt>
40063b: bf 69 07 40 00 mov $0x400769,%edi
400640: e8 7b fe ff ff callq 4004c0 <puts@plt>
400645: bf 73 07 40 00 mov $0x400773,%edi
40064a: e8 71 fe ff ff callq 4004c0 <puts@plt>
40064f: 5d pop %rbp
400650: c3 retq
~ Omis ~
L'adresse de la fonction est l'endroit où le nom de la fonction et la valeur numérique sont définis, par exemple "000000000040060d \
※Mise en garde
Dans le cas de Debian (Debian, Ubuntu, Mint, etc.), il semble que PIE (fonction utilisée pour l'amélioration de la sécurité) puisse être activée par défaut au moment de la compilation. Dans ce cas, cela ne fonctionnera pas, alors ajoutez -no-pie à l'option gcc.
Lorsque PIE est activé, la valeur de l'adresse sera plus petite comme indiqué ci-dessous, et l'adresse la plus élevée (la partie affichée par 0) changera chaque fois que le programme est exécuté.
0000000000000855 <dog>:
Maintenant que vous connaissez l'adresse de la fonction, essayez-la.
Chien d'appel () (0x40060d)
$ ./a.out 0x40060d
wan wan
Appelez cat () (0x40061d)
$ ./a.out 0x40061d
nyaaan
Appeler les oiseaux () (0x40062d)
$ ./a.out 0x40062d
piyo piyo
chun chun
kah kah
J'ai pu appeler la fonction comme prévu.
Dans l'exemple précédent, nous avons spécifié l'adresse de la fonction dans le pointeur de fonction et appelé n'importe quelle fonction.
Mais que faire si vous spécifiez une autre adresse?
Passons l'adresse au milieu des oiseaux ().
000000000040062d <birds>:
40062d: 55 push %rbp
40062e: 48 89 e5 mov %rsp,%rbp
400631: bf 5f 07 40 00 mov $0x40075f,%edi
400636: e8 85 fe ff ff callq 4004c0 <puts@plt>
40063b: bf 69 07 40 00 mov $0x400769,%edi
400640: e8 7b fe ff ff callq 4004c0 <puts@plt>
400645: bf 73 07 40 00 mov $0x400773,%edi
★^^^^^^^^Précisons l'adresse de cette ligne.
40064a: e8 71 fe ff ff callq 4004c0 <puts@plt>
40064f: 5d pop %rbp
400650: c3 retq
Exemple d'exécution
$ ./a.out 0x400645
kah kah
Segmentation fault (Vidage de base)
J'ai pu exécuter le processus à partir du milieu de la fonction. Cependant, il s'est terminé anormalement avec le vidage de mémoire.
La raison du vidage de mémoire est que je ne sais pas à quelle adresse retourner après la fin de l'exécution de myfunc () (l'adresse de retour est incorrecte).
Une fois que vous aurez compris ce domaine, le langage C deviendra un peu plus amusant.