【问题标题】:How to get current program counter inside mprotect handler and update it如何在 mprotect 处理程序中获取当前程序计数器并更新它
【发布时间】:2013-11-14 00:30:10
【问题描述】:

我想在 mprotect 处理程序中获取当前程序计数器 (PC) 值。从那里我想将 PC 的值增加“n”条指令,以便程序跳过一些指令。我想为 linux 内核版本 3.0.1 做所有这些。关于我可以获得 PC 值的数据结构以及如何更新该值的任何帮助?示例代码将不胜感激。提前致谢。

我的想法是在写入内存地址时使用一些任务。所以我的想法是使用 mprotect 使地址写保护。当一些代码试图在那个内存地址上写一些东西时,我将使用 mprotect 处理程序来执行一些操作。处理好处理程序后,我想让写操作成功。所以我的想法是使内存地址在处理程序内部不受保护,然后再次执行写操作。当代码从处理函数返回时,PC 将指向原始写指令,而我希望它指向下一条指令。因此,无论指令长度如何,我都想将 PC 增加一条指令。

检查以下流程

MprotectHandler(){
    unprotect the memory address on which protection fault arised
    write it again
    set PC to the next instruction of original write instruction
}

主函数内部:

main(){
    mprotect a memory address
    try to write the mprotected address // original write instruction
    Other instruction    // after mprotect handler execution, PC should point here
}

【问题讨论】:

  • 实际上想要完成什么?你在修改mprotect吗?这是什么架构?固定宽度或可变宽度指令集?您要跳过哪些说明? mprotect是系统调用……系统调用完成后会返回进程。
  • 我已经编辑了我的问题。

标签: linux kernel mprotect program-counter


【解决方案1】:

由于在多个 CISC 处理器上计算指令长度很繁琐,我推荐一个稍微不同的过程:使用clone(..., CLONE_VM, ...)clone(..., CLONE_VM, ...) 分叉到跟踪器和跟踪器线程中,并在跟踪器中代替

    write it again
    set PC to the next instruction of original write instruction

做一个

    ptrace(PTRACE_SINGLESTEP, ...)

- 在跟踪陷阱之后,您可能想再次保护内存。

【讨论】:

    【解决方案2】:

    这里是演示基本原理的示例代码:

    #include <signal.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/ucontext.h>
    
    static void
    handler(int signal, siginfo_t* siginfo, void* uap) {
        printf("Attempt to access memory at address %p\n", siginfo->si_addr);
        mcontext_t *mctx = &((ucontext_t *)uap)->uc_mcontext;
        greg_t *rsp = &mctx->gregs[15];
        greg_t *rip = &mctx->gregs[16];
    
        // Jump past the bad memory write.
        *rip = *rip + 7;
    }
    
    static void
    dobad(uintptr_t *addr) {
        *addr = 0x998877;
        printf("I'm a survivor!\n");
    }
    
    int
    main(int argc, char *argv[]) {
        struct sigaction act;
        memset(&act, 0, sizeof(struct sigaction));
        sigemptyset(&act.sa_mask);
        act.sa_sigaction = handler;
        act.sa_flags = SA_SIGINFO | SA_ONSTACK;
    
        sigaction(SIGSEGV, &act, NULL);
    
        // Write to an address we don't have access to.
        dobad((uintptr_t*)0x1234);
    
        return 0;
    }
    

    它向您展示了如何更新 PC 以响应页面错误。它缺少您必须自己实现的以下内容:

    • 指令长度解码。正如你所看到的,我已经硬编码了+ 7,它恰好在我的 64 位 Linux 上工作,因为导致页面错误的指令是一个 7 字节的MOV。正如 Armali 在他的回答中所说,这是一个乏味的问题,您可能必须使用像 libudis86 这样的外部库。
    • mprotect() 处理。您在siginfo-&gt;si_addr 中有导致页面错误的地址,并且使用该地址可以轻松找到受 mprotected 页面的地址并取消保护它。

    【讨论】:

      猜你喜欢
      • 2011-02-11
      • 2018-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-29
      相关资源
      最近更新 更多