安卓Linux - 一个基于页表的幽灵内存实现

项目链接:https://github.com/fuqiuluo/android-wuwa

为某个进程的虚拟地址"绑定"一块物理内存,让进程能够通过指定的虚拟地址访问到实际的物理页面。

但是不修改vma有关的任何东西,虽然是泄漏了些东西,至少实现了,不对吗?不需要去hook show_map 非常的脑子消失,不过采样器是可以检测到这个的,需要过一定的检测,至少不需要过太多...

下方代码只能分配一页,具体代码去项目里面查看!

int do_pte_mapping(struct socket *sock, void *arg) {
    struct wuwa_pte_mapping_cmd cmd;
    if (copy_from_user(&cmd, arg, sizeof(cmd))) {
        return -EFAULT;
    }

    pgd_t *pgd;
    p4d_t *p4d;
    pud_t *pud;
    pmd_t *pmd;
    pte_t *pte;
    struct page* page = NULL;
    int ret = 0;

    struct pid* pid_struct = find_get_pid(cmd.pid);
    if (!pid_struct) {
        wuwa_warn("failed to find pid_struct: %d\n", cmd.pid);
        return -ESRCH;
    }

    struct task_struct *task = get_pid_task(pid_struct, PIDTYPE_PID);
    put_pid(pid_struct);
    if (!task) {
        wuwa_warn("failed to get task: %d\n", cmd.pid);
        return -ESRCH;
    }

    struct mm_struct *mm = get_task_mm(task);
    put_task_struct(task);
    if (!mm) {
        wuwa_warn("failed to get mm: %d\n", cmd.pid);
        return -ESRCH;
    }

    static int (*my__pmd_alloc)(struct mm_struct *mm, pud_t *pud, unsigned long address) = NULL;
    my__pmd_alloc = (int (*)(struct mm_struct *, pud_t *, unsigned long))kallsyms_lookup_name("__pmd_alloc");
    static int (*my__pte_alloc)(struct mm_struct *mm, pmd_t *pmd) = NULL;
    my__pte_alloc = (int (*)(struct mm_struct *, pmd_t *))kallsyms_lookup_name("__pte_alloc");

    if (my__pmd_alloc == NULL || my__pte_alloc == NULL) {
        wuwa_err("failed to find __pmd_alloc or __pte_alloc symbols\n");
        ret = -ENOENT;
        goto out_mm;
    }

#define my_pte_alloc(mm, pmd) (unlikely(pmd_none(*(pmd))) && my__pte_alloc(mm, pmd))
#define my_pte_alloc_map(mm, pmd, address) (my_pte_alloc(mm, pmd) ? NULL : pte_offset_map(pmd, address))

    page = alloc_pages(GFP_USER | __GFP_ZERO, get_order(cmd.num_pages * PAGE_SIZE));
    if (!page) {
        wuwa_err("failed to allocate pages\n");
        ret = -ENOMEM;
        goto out_mm;
    }

    pgd = pgd_offset(mm, cmd.start_addr);
    if (pgd_none(*pgd) || pgd_bad(*pgd)) {
        wuwa_err("invalid pgd\n");
        ret = -EINVAL;
        goto out_page;
    }

    p4d = p4d_alloc(mm, pgd, cmd.start_addr);
    if (!p4d) {
        wuwa_err("failed to allocate p4d\n");
        ret = -ENOMEM;
        goto out_page;
    }

    pud = pud_alloc(mm, p4d, cmd.start_addr);
    if (!pud) {
        wuwa_err("failed to allocate pud\n");
        ret = -ENOMEM;
        goto out_page;
    }

    if (unlikely(pud_none(*pud))) {
        if (my__pmd_alloc(mm, pud, cmd.start_addr)) {
            wuwa_err("failed to allocate pmd\n");
            ret = -ENOMEM;
            goto out_page;
        }
    }
    pmd = pmd_offset(pud, cmd.start_addr);
    if (!pmd) {
        wuwa_err("failed to get pmd\n");
        ret = -ENOMEM;
        goto out_page;
    }

    pte = my_pte_alloc_map(mm, pmd, cmd.start_addr);
    if (!pte) {
        wuwa_err("failed to allocate or map pte\n");
        ret = -ENOMEM;
        goto out_page;
    }

    if (!pte_none(*pte)) {
        wuwa_warn("pte already in use at address 0x%lx\n", cmd.start_addr);
        pte_unmap(pte);
        ret = -EEXIST;
        goto out_page;
    }

    pte_t new_pte = mk_pte(page, PAGE_SHARED);
    new_pte = pte_mkwrite(new_pte);
    new_pte = pte_mkdirty(new_pte);
    new_pte = pte_mkyoung(new_pte);

    set_pte(pte, new_pte);

    pte_unmap(pte);

    flush_tlb_all();

    mmput(mm);

    wuwa_info("successfully mapped page at address 0x%lx for pid %d\n", cmd.start_addr, cmd.pid);
    return 0;

out_page:
    if (page) {
        __free_pages(page, get_order(cmd.num_pages * PAGE_SIZE));
    }
out_mm:
    mmput(mm);
    return ret;
}

代码里面的PAGE_SHARED记得改成PAGE_SHARED_EXEC,不然不带可执行的哦!

其中的start_addr为需要映射到的地址,例如0x900000,看你心意了