【问题标题】:Linux signal handling. How to get address of interrupted instruction? [duplicate]Linux 信号处理。如何获取中断指令的地址? [复制]
【发布时间】:2016-01-25 10:08:13
【问题描述】:

有没有办法找出被某个信号中断的机器指令的地址?假设我们在由sigaction() 建立的处理程序中并且可以访问传递的siginfo_tucontext_t。 据我所知,手册页对此一无所知。

【问题讨论】:

    标签: c linux signals


    【解决方案1】:

    让我们看看下面的 linux 和 x86 架构示例

    #include<stdio.h>
    #define __USE_GNU
    #include<signal.h>
    #include<ucontext.h>
    
    void myhandle(int mysignal, siginfo_t *si, void* arg)
    {    
      ucontext_t *context = (ucontext_t *)arg;
      printf("Address from where crash happen is %x \n",context->uc_mcontext.gregs[REG_RIP]);
      context->uc_mcontext.gregs[REG_RIP] = context->uc_mcontext.gregs[REG_RIP] + 0x04 ;
    
    }
    
    int main(int argc, char *argv[])
    {
      struct sigaction action;
      action.sa_sigaction = &myhandle;
      action.sa_flags = SA_SIGINFO;
      sigaction(11,&action,NULL);
    
      printf("Before segfault\n");
    
      int *a=NULL;
      int b;
      b =*a; // Here crash will hapen
    
      printf("I am still alive\n");
    
      return 0;
    }
    

    现在编译运行,看看反编译的委托集。

    jeegar@jeegar:~/stackoverflow$ gcc -g test1.c  -o test1.o
    jeegar@jeegar:~/stackoverflow$ ./test1.o 
    Before segfault
    Signal is 11
    Address from where crash happen is 40065b 
    I am still alive
    jeegar@jeegar:~/stackoverflow$ objdump -S test1.o 
    

    在对象转储中

      printf("Before segfault\n");
      400645:   bf a8 07 40 00          mov    $0x4007a8,%edi
      40064a:   e8 21 fe ff ff          callq  400470 <puts@plt>
    
      int *a=NULL;
      40064f:   48 c7 45 f0 00 00 00    movq   $0x0,-0x10(%rbp)
      400656:   00 
      int b;
      b =*a; // Here crash will hapen
      400657:   48 8b 45 f0             mov    -0x10(%rbp),%rax
      40065b:   8b 00                   mov    (%rax),%eax
      40065d:   89 45 fc                mov    %eax,-0x4(%rbp)
    
      printf("I am still alive\n");
      400660:   bf b8 07 40 00          mov    $0x4007b8,%edi
      400665:   e8 06 fe ff ff          callq  400470 <puts@plt>
    

    在 40065b 地址中存在哪个机器代码以及您的代码的哪一行完成了此操作。


    在这里,我给了你和示例代码,其中发生分段并且在系统的 Seg 故障信号上将调用一个处理程序,并且我已经获取了最后执行的机器周期的地址并打印了该地址。为了验证该地址,我还显示了该代码的对象转储和分段错误行的机器指令匹配。

    我想这就是你想要的。

    【讨论】:

    • 信号处理程序中的信号不安全函数。在 C 中,您不需要强制转换即可将 void* 转换为任何其他数据指针。
    • @MaximEgorushkin 没有强制转换如何使用该指针?>
    • 虽然 Maxim 是正确的(不要在 sighandler 中使用 printf),但我认为这与本文的目的无关(解释 OP 的要求)。铸造 (void*) 是风格问题。投票。
    • 删除实现粗糙度,这个答案讲述了与stackoverflow.com/a/34990181/2878070 相同的方法。请参阅下面的评论。
    • @Ctx 我不同意,不必要的代码会使您的应用程序变得更糟。把事情做大很容易,把事情做得更好很难。想象一下你去看医生,他告诉你一些你知道是完全错误的事情。你可能会找到另一位医生。
    【解决方案2】:

    不便携。但这是针对 x86_64 的:

    结构ucontext_t 包含寄存器REG_RIP 的值,它应该保存您要查找的值。这是从 sighandler 返回后将执行的第一条指令。

    其他架构应该有类似的寄存器(x86_32 上的 EIP 等)。

    【讨论】:

    • 是的,但不幸的是,不能保证处理程序之后的第一条指令将是被中断的指令,可以通过一些包装器调用适当的处理程序,因此这个伪 IP 可能指向 libc 内部的某些东西或其他任何东西,但不是我们的代码。无论如何 - 感谢您的回答。
    • @Serhio 一条机器指令永远不会被中断,甚至不会被硬件中断。它总是首先完成。但这不是你的意思。您的意思是,如果 IP 在库中的某个位置,您想要主代码中的调用指令地址吗?
    • 是的,更准确地说,我需要获取应用程序端指令的地址(不是libc的),它将在信号处理程序之后执行。例如。紧跟在导致 SIGSEGV 的指令之后的地址。
    • @Serhio 但这正是 REG_RIP 中的地址。我认为您在这里有些困惑。
    • @Serhio 不,当处理程序返回时,寄存器会重置为信号发出时的状态,由内核保存在 ucontext_t 结构中(RIP、RSP、RBP、等页)。而且不知何故,您似乎混淆了指令和堆栈指针。
    猜你喜欢
    • 2023-04-08
    • 1970-01-01
    • 2011-12-14
    • 2012-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多