use*_*140 6 c x86 assembly segmentation-fault
首先,我要说的是,我在这里做的事情是大多数人没有正当理由去做.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 <signal.h>
#include <stdio.h>
#include <ucontext.h>
#include <sys/mman.h>
#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);
}
Run Code Online (Sandbox Code Playgroud)
执行此代码的结果将在运行Linux内核3.11.X的基于intel的arch上将指令指针提前1,并最终超出指令.我知道这可能不适用于所有指令.实际上,当在我的测试环境中执行时,处理程序进入6次(对于指令的6个字节),然后执行继续经过CRASHME.
在给定现有指令的情况下,仅仅将给定指令指针推进到下一条指令似乎是一项微不足道的任务; 处理器每个周期都会这样做.在其他设置中,它被称为"查看指令表并构建自己的"或"实现反汇编程序".这些对于任务既不合适也不必要,因为两者都已经由其他人完成并且(几乎?)仅在我的工作计算机无法访问的网站上发布(并且几乎没有),并且我不信任我提交我的家PC.但是我在哪里可以找到这样的表或库来完成指令计算,而不是查看我已经知道我无法访问的站点?
Linux 内核源代码具有 X86 操作码映射的编码,然后由 Awk 脚本解析该编码以生成一组可用于读取指令的表。它有足够的信息来为您提供准确的指令大小,尽管您可能需要扩展它以包含浮点指令和一些较新的英特尔扩展(例如 AVX)的信息。
如果您有权访问 Linux 内核源代码树,请查看 arch/x86/lib/x85-opcode-map.txt。
其中包含确定指令大小所需的所有数据。
有一个 AWK 脚本 @ arch/x86/tools/gen-insn-attr-x86.awk 它将读取操作码文件并生成一系列对操作码映射中的信息进行编码的表。
最后,如果您查看 arch/x86/lib/insn.c,其中有一个函数 insn_get_length(...) ,它将使用从操作码映射生成的表为您提供指令的长度。这应该足以让您回答您的特定问题“这条指令有多大”。
该代码没有什么特别“核心”的地方。您无需执行任何特殊操作即可适应用户模式。
我假设访问 Linux 内核源代码对您来说不应该是一个安全问题,并且没有什么会妨碍您阅读/采用 GPL 代码。