Pointeur de fonction et objdump ~ Langage C ~

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

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;
}

Recherchez l'adresse de la fonction avec objdump

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 \ :". Cet affichage diffère selon le système de traitement.

※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>:

Appel de fonction

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.

Pour les adresses autres que les fonctions ...

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.


Recommended Posts

Pointeur de fonction et objdump ~ Langage C ~
Communication socket par langage C et Python
Fonction de veille simple C ++
File d'attente ALDS1_3_B langage C
[Algorithme de langage C] Endianness
Aller à la langue pour voir et se souvenir du langage Partie 7 C en langage GO
Appelez la fonction C avec dart: ffi et rappelez la fonction Dart
J'ai essayé d'illustrer le temps et le temps du langage C
[Golang] Principes de base du langage Go À propos du récepteur de valeur et du récepteur de pointeur
[Algorithme de langage C] bloquer le mouvement
Intégration du langage machine en langage C
Recherche binaire ALDS1_4_B langage C
Tri de tas fait en langage C
À propos de la fonction fork () et de la fonction execve ()
[Langage C] readdir () vs readdir_r ()
Recherche linéaire ALDS1_4_A en langage C
réglage du fuseau horaire et de la langue de django
Frontière entre C et Golang
[Langage C] Comment créer, éviter et créer un processus zombie