在C中更改程序计数器

时间:2016-01-28 23:07:04

标签: c debugging assembly signal-handling program-counter

我想更改程序计数器(指令指针)的内容。我想通过覆盖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).
}

2 个答案:

答案 0 :(得分:3)

在段错之后,程序处于不确定状态。您不能再依赖具有正确值的任何内容(例如寄存器)。

即使你能够在故障后返回到下一条指令,除非你的信号处理程序反汇编违规指令[在上下文中],并更改寄存器值以进行补偿,你现在已经得到了一个程序这是不值得信任的,并且可能继续以更加灾难性的结果进行分段或[更糟糕的]操作(例如错误的文件上的unlink等)。

但是,由于信号“trampoline”,你无法这样做。有关详细信息,请参见sigreturn手册页。给你的信号处理程序的堆栈帧不一定是正常的。

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

我已经完成了大量具有恢复功能的segfault捕获信号处理程序,但它们都涉及使用sigsetjmp/siglongjmp

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

答案 1 :(得分: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

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

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