-1

我想更改程序计数器(指令指针)的内容。我认为通过覆盖 C 中的系统信号处理程序,我可以在系统堆栈帧中获取指针。从那里我可以得到函数的返回地址并改变它。

但是,我得到了堆栈中的指针,但我不知道返回地址在堆栈帧上的确切存储位置。

void signal_handler(int signal){

   char* ptr = (char*) & signal;
   // As, signal is stored on the paramter list of the stack, 
   // I get the address in the current stack frame. From , here
   // I want to change the return address(that is stored in the 
   // current stack frame).
}
4

2 回答 2

4

发生段错误后,程序处于不确定状态。您不能再依赖任何具有正确值的东西(例如寄存器)。

即使您可以在故障后返回到下一条指令,除非您的信号处理程序 [in context] 反汇编有问题的指令,并更改寄存器值以进行补偿,您现在有一个不可信的程序,并且可能会继续段错误或[更糟] 操作会带来更灾难性的结果(例如unlink,在错误的文件上等)。

但是,由于信号“蹦床” ,您不能这样做。有关详细信息,请参见sigreturn手册页。提供给信号处理程序的堆栈帧甚至不一定是正常的。

您必须将程序恢复到已知的“安全”状态。做到这一点的唯一方法是 [正如我在上面的评论中提到的] 是在信号处理程序中设置恢复点sigsetjmp并在其中执行。旁注:使用这​​些类似于在 中使用异常,但您必须手动完成更多工作。siglongjmpC++

我已经做了很多带有恢复功能的段错误捕获信号处理程序,但它们都涉及使用sigsetjmp/siglongjmp.

这也引出了一个问题:为什么不首先调试您的程序以使其不会出现段错误?您有什么特殊需要可以排除这种情况?

于 2016-01-29T00:00:51.053 回答
0

如果您只想跳转到一个地址,而不为您要跳转到的上下文设置堆栈,或者从您要离开的上下文中清除它,您可以使用GNU C computed-goto

void *ptr;
/* ... */
ptr = &&foo;

// then somewhere else

goto *ptr;

据我了解,这几乎等同于

asm volatile("jmp *ptr");  // AT&T syntax for memory-indirect jump

尝试从段错误中恢复的正确方法是使用setjmp/longjmp强烈不建议在那个时候尝试做太多事情。使用此功能打印自定义消息、记录某些内容并退出。如果您的代码导致了段错误,那么它很可能在尝试访问一些不允许访问的内存之前已经在您的程序的一些数据上乱涂乱画。这甚至可能破坏了堆栈上的返回地址。

即使 asm 指令 1:1 映射到代码行(它们没有),如果跳过一行代码,大多数程序也会严重崩溃。更不用说只有在您尝试恢复执行的数据或代码出现问题时才会出现段错误。

于 2016-01-28T23:37:19.063 回答