修改运行中代码之神器——PTRACE的秘诀

ptrace是个神奇的系统调用,是gdb等工具的实现基础,它可以用来监控进程的执行,读取或修改进程的寄存器和memory内容包括代码段。有的底层码农老司机甚至也会觉得奇怪,代码段不都是不可写的吗,为啥ptrace就能修改呢?

下面解释一下,老司机直接看后面代码秒懂:
  • 首先,代码段不可写,其实是在页表中对代码段相应的页设置了不可写的flag,而页表只能控制进程地址空间的访问权限。
  • 其次,这里的不可写只对用户态进程起作用。当然,进程的地址空间是隔离的,而进程在用户态只能通过自己地址空间的虚拟地址访问内存,所以当前进程在用户态也无法访问其他进程的地址空间。
  • 最后,在内核态就能随便修改其他进程的代码段了?是的,但是你得先找到目标代码段地址空间对应的物理页,然后再映射到内核地址空间,然后新的地址空间的访问权限决定了你能如何访问这些物理页;ptrace中如下这段代码实现了这些功能,如何能执行到这些代码又是另外一个话题了;这里面牵涉到权限管理等等的问题,ptrace系统调用已经考虑的很周全了,博主会在下一篇博客里面详解ptrace系统调用的权限检查~
ptrace_request()
    case PTRACE_POKETEXT:
    case PTRACE_POKEDATA:
        return generic_ptrace_pokedata(child, addr, data); //调用__access_remote_vm
 最后的关键代码:
ret = get_user_pages(tsk, mm, addr, 1,
                write, 1, &page, &vma);
……
maddr = kmap(page);
            if (write) {
                copy_to_user_page(vma, page, addr,
                          maddr + offset, buf, bytes);
                set_page_dirty_lock(page);
            } else {
                copy_from_user_page(vma, page, addr,
                            buf, maddr + offset, bytes);
            }
            kunmap(page);

Leave a Reply

Your email address will not be published. Required fields are marked *