【问题标题】:SIGSEGV handler and mprotect and looping effect when injecting instructions at runtime. Handler can't get info->si_addr在运行时注入指令时的 SIGSEGV 处理程序和 mprotect 和循环效果。处理程序无法获取信息->si_addr
【发布时间】:2012-01-02 17:33:41
【问题描述】:

我查看了与此相关的各种主题,但找不到我遇到的这个具体问题。

我看过的东西: Injecting code into executable at runtime C SIGSEGV Handler & Mprotect Can I write-protect every page in the address space of a Linux process? How to write a signal handler to catch SIGSEGV?

当需要在处理程序中将保护设置为 PROT_READ 或 PROT_WRITE 时,我能够优雅地处理 SIGSEGV。但是,当我尝试使用 mmap 注入指令,然后使用 mprotect 将其设置为仅 PROT_READ,然后通过内联汇编执行指令时,它会按预期导致 SIGSEGV,但处理程序无法获取导致信号,所以我无法将它保护到 PROT_READ | PROT_EXEC。

例子:

void sigHandler(int signum, siginfo_t *info, void *ptr) {

    printf("Received signal number: %d\n", signum);
    printf("Signal originates from process %lu\n",
        (unsigned long)info->si_pid);

    printf("SIGSEGV caused by this address: ? %p\n", info->si_addr);

    char * alignedbaseAddr = (((unsigned int)(info->si_addr)) >> 12) * getPageSize(); 
    printf("Aligning to %p\n", alignedbaseAddr);
    //flip this page to be r+x
    mprotect(alignedbaseAddr, getPageSize(), PROT_READ | PROT_EXEC);
}
void setupSignalHandler() {
    action.sa_sigaction = sigHandler;
    action.sa_flags = SA_SIGINFO;
    sigemptyset(&action.sa_mask);
    sigaction(SIGSEGV, &action, NULL);
}

int main(int argc, char *argv[]) {
    char * baseAddr = (char*)mmap(NULL, getDiskSize(), PROT_READ | PROT_WRITE,    MAP_SHARED, fd, 0);
    if(baseAddr == MAP_FAILED) {
        perror("Unable to mmap.");
    }
    printf("Process address space is %d\n", getDiskSize());
    //no-op filler
    for(int i = 0; i < (getDiskSize()) - 1; i++) {
        baseAddr[i] = 0x90;
    }
    //ret instruction
    baseAddr[i] = 0xc3;

    if( mprotect(baseAddr, getDiskSize(), PROT_READ) == -1) {
        perror("mprotect");
        exit(1);
    }

    printf("Protecting addresses: %p to %p for READ_ONLY\n", baseAddr, baseAddr + getDiskSize() - 1);
    setupSignalHandler();


    __asm__
    (
     "call %%eax;"
     : "=a" (output)
     : "a" (baseAddr)
    );

    printf("Will this ever print?");
    //close fd, and unmap memory
    cleanUp();
    return EXIT_SUCCESS;
}

这是结果输出:

接收信号数:11
信号来自进程 0
此地址引起的 SIGSEGV: ? (无)

//上面的输出重复循环,因为它无法“重新保护”那个页面。

架构: x86 32 位 操作系统: Ubuntu 11.04 - Linux 版本 2.6.38-12-generic (buildd@vernadsky) (gcc 版本 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4))

有什么想法吗?上述逻辑适用于简单地读取和写入内存。有没有 与内联汇编相比,在运行时执行指令的更好方法?

提前致谢!

【问题讨论】:

  • 请缩进您的代码以使其更具可读性!
  • 对不起。预览有点欺骗性。现在已经更正了。

标签: loader mmap virtual-memory segmentation-fault mprotect


【解决方案1】:

在这种情况下,错误地址是指令指针。将您的第三个参数ptr(与SA_SIGINFO 一起安装的信号处理程序)转换为ucontext_t,并检索适当的寄存器,可能是(未经测试的代码!

ucontext_t *uc = ptr;
void* faultyip = uc->uc_mcontext.gregs[REG_IP];

请仔细阅读/usr/include/sys/ucontext.h 了解更多信息。

我很想知道你为什么这么问!!

【讨论】:

  • Basile,我正在尝试通过在执行指令时触发页面错误来模拟虚拟内存,就像真正的操作系统一样。
  • 我的想法是解析一个可执行文件,并将其输入到这个 VM 模拟器中。我尝试使用上面的逻辑来重新 mprotect 以获得执行权限,但仍然是同样的问题。 EIP 正确指向了导致错误的指令(上下文是正确的),但会出现同样的问题。还有其他想法吗?如果你愿意,我可以发布结果吗?
  • siginfo * 类型下的 si-> 代码产生的值为 128,即 SEGV_MAPERR 或 SEGV_ACCERR。你知道是哪一个吗?
  • 所以作为一个测试,如果我用 gbl 变量保存“baseAddr”,然后在保存的值上使用 mprotect,那么它工作正常。问题是 sigHandler 不知道导致故障的原始地址,我感觉这与内联汇编有关。另外,EIP 指向的文本部分已经是“可执行的”,所以除非我遗漏了什么,否则 EIP 在这里无济于事。
猜你喜欢
  • 2011-02-11
  • 2023-03-29
  • 2012-01-07
  • 1970-01-01
  • 1970-01-01
  • 2015-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多