简体   繁体   English

从进程 VMA 读取的 Linux 内核模块

[英]Linux kernel module read from process VMA

I'm trying to build a small demonstration kernel module for Linux which finds a specific process and reads a value from that process' memory.我正在尝试为 Linux 构建一个小的演示内核模块,它可以找到一个特定的进程并从该进程的内存中读取一个值。

I have put together the following code:我整理了以下代码:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/highmem.h>
#include <asm/uaccess.h>

static void read_process_memory(struct task_struct *task) {
    char buf[1024];
    unsigned long size;
    unsigned long ret;
    void __user *addr;
    struct mm_struct *mm = get_task_mm(task);
    struct vm_area_struct *vma;

    if (!mm) {
        // Abort
        return;
    }

    // Lock for reading
    down_read(&mm->mmap_sem);

    vma = mm->mmap;
    memset(buf, 0, 1024);

    // Make sure read is enabled for this region
    if (vma && vma->vm_flags & VM_READ) {

        // Read without overflowing
        size = vma->vm_end - vma->vm_start;
        if (size > 1023) {
            size = 1023;
        }

        // Attempt to get the data from the start of the vma
        addr = (void __user *)vma->vm_start;

        if (access_ok(VERIFY_READ, addr, size)) {
            ret = copy_from_user(buf, addr, size);

            if (ret == 0) {
                // Probably doesn't contain anything relevent
                printk(KERN_ALERT "mymodule: Read '%s'\n", buf);
            } else {
                printk(KERN_ALERT "mymodule: Failed to copy %lu bytes from userspace\n", ret);
            }
        } else {
            printk(KERN_ALERT "mymodule: access_ok check failed\n");
        }

        // Release the lock
        up_read(&mm->mmap_sem);
        mmput(mm);
    }
}

static int __init mymodule_init(void) {
    struct task_struct *task;
    bool found = false;

    printk(KERN_ALERT "mymodule: Starting\n");

    // Find the process
    rcu_read_lock();

    for_each_process(task) {
        if (strcmp(task->comm, "example-process") == 0) {
            printk(KERN_ALERT "mymodule: Found pid %d for process %s\n", task->pid, task->comm);
            found = true;
            break;
        }
    }

    rcu_read_unlock();

    if (!found) {
        printk(KERN_ALERT "mymodule: Process not found, aborting\n");
        return -1;
    }

    read_process_memory(task);

    return 0;
}

static void __exit mymodule_exit(void) {
    printk(KERN_ALERT "mymodule: Stopped\n");
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("user");

Everything works fine until the copy_from_user call, which returns a non-zero count (actually, it returns size , meaning it didn't ready any data), regardless of the process and vma I try to read from.一切正常,直到copy_from_user调用返回一个非零计数(实际上,它返回size ,这意味着它没有准备好任何数据),无论我尝试读取的进程和vma什么。

Is there a misunderstanding on my part, or anything I'm doing wrong?是我有什么误解,还是我做错了什么?

EDIT: Here's a working version of read_process_memory :编辑:这是read_process_memory的工作版本:

static void read_process_memory(struct task_struct *task) {
    char buf[1024];
    unsigned long size;
    int ret;
    void *addr;
    struct mm_struct *mm = get_task_mm(task);
    struct vm_area_struct *vma;
    struct page *pages[1];

    if (!mm) {
        // Abort
        return;
    }

    // Lock for reading
    down_read(&mm->mmap_sem);

    vma = mm->mmap;
    memset(buf, 0, 1024);

    // Make sure read is enabled for this region
    if (vma && vma->vm_flags & VM_READ) {

        // Get the first page
        ret = get_user_pages_remote(task, mm, vma->vm_start, 1, FOLL_FORCE, pages, NULL);

        if (ret > 0) {
            // We got our page, we should now be able to map it and read directly
            addr = kmap(*pages);

            // Read without overflowing
            size = vma->vm_end - vma->vm_start;
            if (size > 1023) {
                size = 1023;
            }
            strncpy(buf, addr, size);

            // Probably doesn't contain anything relevent
            printk(KERN_ALERT "mymodule: Read '%s'\n", buf);

            // Make sure to release our page
            kunmap(*pages);
            put_page(*pages);
        } else {
            printk(KERN_ALERT "mymodule: Failed to read page at %p (errno=%d)\n", (void *)vma->vm_start, ret);
        }

        // Release the lock
        up_read(&mm->mmap_sem);
        mmput(mm);
    }
}

I'd say your code is wrong.我会说你的代码是错误的。 Please be noted you are accessing memory of remote process (not current).请注意,您正在访问远程进程(非当前)的内存。 To do that correctly you must use one of the GUP APIs(eg get_user_pages_remote()) to grab the pages first.要正确执行此操作,您必须先使用 GUP API 之一(例如 get_user_pages_remote())来抓取页面。 The uaccess APIs (eg copy_from_user) only works for current process. uaccess API(例如 copy_from_user)仅适用于当前进程。

Here is an example in iouring: https://elixir.bootlin.com/linux/latest/source/fs/io_uring.c#L4839这是 iouring 的一个例子: https ://elixir.bootlin.com/linux/latest/source/fs/io_uring.c#L4839

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM