ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

ptrace内核源码实现

2022-09-13 11:36:05  阅读:238  来源: 互联网

标签:tsk 内核 struct 源码 user ptrace page PTRACE


一、ptrace系统调用

ptrace在内核中的实现是sys_ptrace函数,也可以说是SYSCALL_DEFINE4(ptrace, ...)。

其中sys_ptrace负责attach相关请求的处理,之后调用arch_ptrace函数来处理其余请求,而arch_ptrace函数也只处理一部分请求,剩下的请求都由ptrace_request函数来处理。

sys_ptrace
  |
  ----arch_ptrace
      |
      ----ptrace_request

 

相关代码太多,代码主要位于/kernel/ptrace.c。

二、相关请求处理的实现

1.PTRACE_PEEKTEXT, PTRACE_PEEKDATA的实现

ptrace函数peekdata获取目标进程的内存数据的代码实现在/kernel/ptrace.c。具体函数调用过程如下:

generic_ptrace_peekdata
    |
     ----ptrace_access_vm
             |
              ----__access_remote_vm

 

__access_remote_vm 函数读取数据的关键部分代码,最终是通过每个vma的access操作函数来拿取数据,也就是vma->vm_ops->access(...),详细如下。

while (len) {
        int bytes, ret, offset;
        void *maddr;
        struct page *page = NULL;

        ret = get_user_pages_remote(tsk, mm, addr, 1,
                gup_flags, &page, &vma, NULL);
        if (ret <= 0) {
#ifndef CONFIG_HAVE_IOREMAP_PROT
            break;
#else
            /*
             * Check if this is a VM_IO | VM_PFNMAP VMA, which
             * we can access using slightly different code.
             */
            vma = find_vma(mm, addr);
            if (!vma || vma->vm_start > addr)
                break;
            if (vma->vm_ops && vma->vm_ops->access)
                ret = vma->vm_ops->access(vma, addr, buf,
                              len, write);
            if (ret <= 0)
                break;
            bytes = ret;
#endif
        } else {
            bytes = len;
            offset = addr & (PAGE_SIZE-1);
            if (bytes > PAGE_SIZE-offset)
                bytes = PAGE_SIZE-offset;

            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);
            put_page(page);
        }
        len -= bytes;
        buf += bytes;
        addr += bytes;
    }

 

2.PTRACE_TRACEME的实现

该实现主要是检查子进程与父进程是否在一个命名空间内以及凭证信息,若都通过设置进程描述符的ptrace标志位。

if (request == PTRACE_TRACEME) {
        ret = ptrace_traceme();
        if (!ret)
            arch_ptrace_attach(current);
        goto out;
}

 

3.PTRACE_POKETEXT, PTRACE_POKEDATA的实现

ptrace函数pokedata将数据写入目标进程的内存的代码实现在/kernel/ptrace.c。具体函数调用过程如下:

generic_ptrace_pokedata
    |
     ----ptrace_access_vm
             |
              ----__access_remote_vm

 

可以看出其实现过程与peekdata差不多。

4.PTRACE_SINGLESTEP、PTRACE_SINGLEBLOCK、PTRACE_SYSEMU、PTRACE_SYSEMU_SINGLESTEP、PTRACE_SYSCALL、PTRACE_KILL和PTRACE_CONT的实现

以上请求都是通过ptrace_resume函数实现。

if (request == PTRACE_SYSCALL)
        set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
    else
        clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);

#ifdef TIF_SYSCALL_EMU
    if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP)
        set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
    else
        clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
#endif

    if (is_singleblock(request)) {
        if (unlikely(!arch_has_block_step()))
            return -EIO;
        user_enable_block_step(child);
    } else if (is_singlestep(request) || is_sysemu_singlestep(request)) {
        if (unlikely(!arch_has_single_step()))
            return -EIO;
        user_enable_single_step(child);
    } else {
        user_disable_single_step(child);
    }

set_tsk_thread_flag函数实现内容为设置进程描述符中thread_info->stack标志,设置为TIF_SYSCALL_TRACE、TIF_SINGLESTEP或TIF_SYSCALL_EMU。user_enable_single_step函数也是调用set_tsk_thread_flag函数。

5.PTRACE_PEEKUSR的实现

从USER区域中读取一个字节,偏移量为addr。其实现函数为ptrace_read_user,定义如下。

static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
                unsigned long __user *ret)
{
    unsigned long tmp;

    if (off & 3)
        return -EIO;

    tmp = 0;
    if (off == PT_TEXT_ADDR)
        tmp = tsk->mm->start_code;
    else if (off == PT_DATA_ADDR)
        tmp = tsk->mm->start_data;
    else if (off == PT_TEXT_END_ADDR)
        tmp = tsk->mm->end_code;
    else if (off < sizeof(struct pt_regs))
        tmp = get_user_reg(tsk, off >> 2);
    else if (off >= sizeof(struct user))
        return -EIO;

    return put_user(tmp, ret);
}

 

6.PTRACE_POKEUSR的实现

向USER区域中写入一个字节,偏移量为addr。其实现函数为ptrace_write_user,定义如下。

static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
                 unsigned long val)
{
    if (off & 3 || off >= sizeof(struct user))
        return -EIO;

    if (off >= sizeof(struct pt_regs))
        return 0;

    return put_user_reg(tsk, off >> 2, val);
}

 

7.PTRACE_GETREGS的实现

获取寄存器信息,通过copy_regset_to_user函数实现的,该函数通过调用寄存器集user_regset的操作函数get来获取寄存器集的数据信息。

static inline int copy_regset_to_user(struct task_struct *target,
                      const struct user_regset_view *view,
                      unsigned int setno,
                      unsigned int offset, unsigned int size,
                      void __user *data)
{
    const struct user_regset *regset = &view->regsets[setno];

    if (!regset->get)
        return -EOPNOTSUPP;

    if (!access_ok(VERIFY_WRITE, data, size))
        return -EFAULT;

    return regset->get(target, regset, offset, size, NULL, data);
}

 

其中,view参数的内容来自user_arm_view变量,它是一个全局变量,记录当前寄存器的状态信息。

 

标签:tsk,内核,struct,源码,user,ptrace,page,PTRACE
来源: https://www.cnblogs.com/glodears/p/16688557.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有