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 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;
}
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 \
※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>:
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.
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