[LINUX] Funktionszeiger und objdump ~ C language ~

Experimentieren Sie, um eine Funktion aus dem erstellten Programm mit einem Funktionszeiger und objdump aufzurufen.

Sie können sich ein Bild davon machen, was es bedeutet, eine Funktion aufzurufen und wie die Funktion ausgeführt wird.

Die experimentelle Umgebung ist Linux (CentOS7 64bit) + gcc (Version 4.8.5).

Quellcode

Quellcode für Experimente verwendet. Dies ist ein Beispiel, das einen Funktionszeiger verwendet, um die Adresse zu verarbeiten, die bei der Ausführung des Programms als Argument übergeben wird.

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); /*Dies ist der Funktionszeiger*/

    if (argc != 2) {
        printf("%s <address>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /*Konvertieren Sie die an das Argument übergebene hexadezimale Zeichenfolge in einen numerischen Wert*/
    myfunc = (void(*)(void))strtol(argv[1], NULL, 16);

    /*Lauf*/
    myfunc();

    return 0;
}

Suchen Sie die Funktionsadresse mit objdump

Kompilieren Sie den erstellten Quellcode und analysieren Sie das fertige Programm mit objdump.

$ gcc sample.c
$ objdump -d a.out
~ Ausgelassen ~
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   
~ Ausgelassen ~

Die Adresse der Funktion ist der Ort, an dem der Funktionsname und der numerische Wert festgelegt werden, z. B. "000000000040060d \ :". Diese Anzeige unterscheidet sich je nach Verarbeitungssystem.

※Hinweis Im Fall des Debian-Systems (Debian, Ubuntu, Mint usw.) scheint es, dass PIE (Funktion zur Sicherheitsverbesserung) zur Kompilierungszeit standardmäßig aktiviert ist. In diesem Fall funktioniert es nicht. Fügen Sie der Option gcc -no-pie hinzu.

Wenn PIE aktiviert ist, ist der Wert der Adresse wie unten gezeigt kleiner und die höhere Adresse (der durch 0 angezeigte Teil) ändert sich jedes Mal, wenn das Programm ausgeführt wird.

0000000000000855 <dog>:

Funktionsaufruf

Nachdem Sie die Adresse der Funktion kennen, probieren Sie sie aus.

Hund anrufen () (0x40060d)

$ ./a.out 0x40060d
wan wan

Rufen Sie cat () (0x40061d) auf.

$ ./a.out 0x40061d 
nyaaan

Rufe Vögel an () (0x40062d)

$ ./a.out 0x40062d
piyo piyo
chun chun
kah kah

Ich konnte die Funktion wie erwartet aufrufen.

Für andere Adressen als Funktionen ...

Im vorherigen Beispiel haben wir die Adresse der Funktion im Funktionszeiger angegeben und eine beliebige Funktion aufgerufen.

Was aber, wenn Sie eine andere Adresse angeben?

Lassen Sie uns die Adresse mitten in Vögeln übergeben ().

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 
★^^^^^^^^Geben Sie die Adresse dieser Zeile an.
  40064a:	e8 71 fe ff ff       	callq  4004c0 <puts@plt>
  40064f:	5d                   	pop    %rbp
  400650:	c3                   	retq   

Ausführungsbeispiel

$ ./a.out 0x400645
kah kah
Segmentation fault (Core-Dump)

Ich konnte den Prozess von der Mitte der Funktion aus ausführen. Es endete jedoch abnormal mit dem Core Dump.

Der Grund für den Core-Dump ist, dass ich nicht weiß, an welche Adresse ich nach der Ausführung von myfunc () zurückkehren soll (die Rücksprungadresse ist falsch).

Sobald Sie diesen Bereich verstanden haben, wird die C-Sprache ein bisschen mehr Spaß machen.


Recommended Posts

Funktionszeiger und objdump ~ C language ~
Socket-Kommunikation in C-Sprache und Python
C ++ einfache Schlaffunktion
C-Sprache ALDS1_3_B Warteschlange
[C-Sprachalgorithmus] Endianness
Gehen Sie in die Sprache, um Teil 7 C in der Sprache GO zu sehen und sich daran zu erinnern
Rufen Sie die C-Funktion mit dart: ffi auf und rufen Sie die Dart-Funktion zurück
Ich habe versucht, die Zeit und die Zeit der C-Sprache zu veranschaulichen
[Golang] Grundlagen der Go-Sprache Über Wertempfänger und Zeigerempfänger
[C-Sprachalgorithmus] Blockbewegung
Einbettung der Maschinensprache in die Sprache C.
C-Sprache ALDS1_4_B Binäre Suche
Heap-Sortierung in C-Sprache
Informationen zur Funktion fork () und zur Funktion execve ()
[C Sprache] readdir () vs readdir_r ()
C-Sprache ALDS1_4_A Lineare Suche
Django Zeitzoneneinstellung und Spracheinstellung
Grenze zwischen C und Golang
[C-Sprache] So erstellen, vermeiden und erstellen Sie einen Zombie-Prozess