首先,我要说的是,我在这里做的事情是大多数人没有正当理由去做.99.99%所有段错误应该导致明确的终止,并且在任何但最简单的情况下快乐地处理它们将导致非常糟糕的行为和损坏的堆栈.如果您来到这里寻求解决段错误,请查看以下链接:https://www.securecoding.cert.org/confluence/display/seccode/SIG35-C.+Do+not+return+from+a+计算+异常+信号+处理器
也就是说,我正在致力于从外部标准实现一个环境,该标准定义了从计算逻辑错误的信号处理程序返回的行为,即跳过一条指令.我明白这很糟糕,但是我无法控制它; 我不能仅仅定义这个定义,因为它是一个嵌入式系统,其中已经编写了其他软件元素,这些元素依赖于定义的行为(它们通常是安全关键的,并且需要能够优雅地退出,即使它们不合适或者糟糕的事情;进一步我没有源代码,所以我不能只修复段错误,实际上需要任何现有的坏段错误/崩溃行为,因为我正在模拟现有系统的行为).
虽然系统本身是在具有固定指令长度的PowerPC上运行,但我们的开发是在并行x86/x64环境中进行的,其中指令不是固定长度的.我知道下面的代码可以工作,尽管x86很糟糕:
#define _GNU_SOURCE #include#include #include #include #define CRASHME *((int*)NULL) = 0 //for x86 #ifdef REG_EIP #define INCREMENT(x) (x)->uc_mcontext.gregs[REG_EIP]++ //for x64 #elif defined REG_RIP #define INCREMENT(x) (x)->uc_mcontext.gregs[REG_RIP]++ //for PPC arch #elif defined PT_NIP #define INCREMENT(x) (x)->uc_mcontext.uc_regs->gregs[PT_NIP]+=4 #endif static void handler(int sig, siginfo_t *si, void *vcontext) { ucontext_t *context = (ucontext_t *)vcontext; INCREMENT(context); } void crashme_function(void) { printf("entered new context, segfaulting!\n"); CRASHME; printf("SEGFAULT handled!\n"); } int main (int argc, char* args) { struct sigaction sa; printf("Printing a thing\n"); sa.sa_flags = SA_SIGINFO; sigemptyset(&sa.sa_mask); sa.sa_sigaction = handler; sigaction(SIGSEGV, &sa, NULL); printf("Entering new context...\n"); crashme_function(); printf("context exited successfully\n"); return(0); }
执行此代码的结果将在运行Linux内核3.11.X的基于intel的arch上将指令指针提前1,并最终超出指令.我知道这可能不适用于所有指令.实际上,当在我的测试环境中执行时,处理程序进入6次(对于指令的6个字节),然后执行继续经过CRASHME.
在给定现有指令的情况下,仅仅将给定指令指针推进到下一条指令似乎是一项微不足道的任务; 处理器每个周期都会这样做.在其他设置中,它被称为"查看指令表并构建自己的"或"实现反汇编程序".这些对于任务既不合适也不必要,因为两者都已经由其他人完成并且(几乎?)仅在我的工作计算机无法访问的网站上发布(并且几乎没有),并且我不信任我提交我的家PC.但是我在哪里可以找到这样的表或库来完成指令计算,而不是查看我已经知道我无法访问的站点?