2017-01-27 6 views
0

J'essaye de modifier l'adresse de retour sur la pile dans un gestionnaire d'erreur de segment, de sorte qu'il saute l'instruction de défaut. Cependant, je reçois une erreur de segment chaque fois que j'essaie de modifier l'adresse de retour, si je n'appelle pas le gestionnaire de signal directement. Gdb n'est pas bon pour le débogage quand le programme segfault, mais quand je fais info frame je vois que, après un segfault, il y a "frame level 2" par opposition à "frame level 0"? Je ne sais pas où GDB obtient cette information, parce que quand j'essaye x/12xw $ebp ou autant de mots, je ne vois pas l'adresse de retour à main() ...Différences de pile entre un gestionnaire de signaux appelé directement et par raise()?

Compilé avec -m32 -z execstack -fno-pile- protecteur sur linux CentOS

#include <signal.h> 
#include <stdio.h> 
#include <stdlib.h> 

void segment_fault_handler(int signum) 
{ 
    char* ret = (char*)(&signum)-4; 
    *(ret) += 8; 
} 

int main() 
{ 
    int phail = 0; 

    signal(SIGSEGV, segment_fault_handler); 

    segment_fault_handler(7); //Only by using this can I skip the next instruction 

    phail = *((int *) 0); 

    printf("Win!\n"); 

    return 0; 
} 

la raison pour laquelle j'augmente de 8 est parce que l'instruction phail dans main() est de 8 octets:

0x080484e2 <+37>: movl $0x7,(%esp) 
0x080484e9 <+44>: call 0x8048480 <segment_fault_handler> 
0x080484ee <+49>: mov $0x0,%eax 
0x080484f3 <+54>: mov (%eax),%eax 
0x080484f5 <+56>: mov %eax,0x1c(%esp) 
0x080484f9 <+60>: movl $0x80485b4,(%esp) 
0x08048500 <+67>: call 0x8048350 <[email protected]> 

Ai-je besoin d'augmenter le décalage un peu? Est-ce que ma méthode d'accès à la pile (que je pense correspondre à EBP + 4) doit changer quand il s'agit d'une situation de défaut de segment?

+1

Vous devez modifier le PC sauvegardé dans la structure 'ucontext_t' transmise au gestionnaire, * et non l'adresse de retour immédiate de la fonction de traitement du signal. (Et * ne l'appelle pas directement.) Le programme d'exemple à https://stackoverflow.com/questions/5119288/getting-fault-address-that-generated-a-unix-signal/5120984 montre comment accéder au Structure de 'ucontext_t'. – zwol

Répondre

0

Enregistrez le gestionnaire de signaux avec sigaction, non signal, et utiliser le drapeau SA_SIGINFO pour obtenir un siginfo_t décrivant la cause du signal. Cela vous permettra de gérer SIGSEGV causé par une erreur différente de celle explicitement raise d ou envoyé par kill, sigqueue, etc. Cela vous donne également le ucontext_t nécessaire pour examiner l'état au moment de l'erreur et éventuellement le changer avant de retourner.

BTW, en général signal est une mauvaise idée d'utiliser de toute façon, car il est spécifié si le drapeau SA_RESTART est défini - et que vous voulez presque toujours SA_RESTART. Prenez l'habitude d'utiliser sigaction et de traiter signal comme obsolète.