我如果能在内核中很方便地使用HIGHUSER内存该有多好...一个例子

简介:

话说用户态访问内核内存很简单,将这块内核内存映射到用户地址空间即可。依托一个字符设备,实现其mmap回调函数,在用户进程打开那个设备,mmap之,很容易实现用户进程直接访问内核内存。
       但是反过来呢?内核访问用户内存。理论上也很简单,因为所有进程的内核态地址空间都是共享的,所以想访问哪个进程的内存,就切换到那个进程的地址空间,这 将丝毫不会影响当前的执行流。事实上也是这么简单,use_mm就是干这个的,不过既然要切换地址空间,那么当前task的地址空间就必须是明确的,因此 就不能在任意上下文调用use_mm(这从user mm会缺页导致睡眠之外的另一个视角说明了任意上下文不好调用use_mm)。但是这么用的很少,AIO算一个,以至于use_mm竟然没有被 EXPORT出来...不过有办法。我写了一个例子

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kthread.h>

int exit_ = 0;

/*
 * 我所测试的内核版本太老,老到下面两个API还没有被导出。
 * (还没有被导出的符号可以叫做API吗??)
 * 在2.6.34内核版本and newer,就可以直接使用了
 **/
void (*use_mm)(struct mm_struct *mm);
void (*unuse_mm)(struct mm_struct *mm);

static int main_loop(void)
{
    struct task_struct *t = NULL;
    while (exit_) {
        /* 正确的做法是:pid应该由netlink或者procfs等U/K通信接口告知内核 */
        struct pid *pid = find_get_pid(3881);
        t = pid_task(pid, PIDTYPE_PID);
        if (t) {
            /* 正确的做法是:内存指针应该通过U/K通信接口告知内核 */
            char *pshare = (char *)0x22d7010;
            struct mm_struct *mm = t->mm;
            /* 
             * 注意!这是在具体的task上下文而不是任意上下文,否则use_mm就不行!
             * 因为用户内存是可被换出的,也是可能缺页的,缺页的处理可能会睡眠
             * 所以不要指望在网络协议栈接收/发送软中断中进行用户内存的操作,除非
             * 你能确认它们处在task上下文。
             **/
            use_mm(mm);
            /* 
             * 访问用户进程的内存
             * 这里仅仅是一个最简单的例子,事实上,在这里可以任意的像在用户进程自身一样
             * 访问这个地址空间,关键是你要知道符号名称以及位置,这需要设计一个机制,使
             * 符号的位置可以映射到一个名称...
             *
             * 此时,只要用户进程维护一个内存表,该内存表的物理内存当然可以在HIGHUSER区域
             * 被分配!实际上这只是一块虚拟地址空间,缺页中断会映射物理内存。
             *
             * 最好存些什么呢?我知道,在这里-内核空间可以访问sqlite数据库了...
             **/
            printk("kernel info:%s  %p\n", pshare, pshare);
            unuse_mm(mm);
        }  
        schedule_timeout(1000);
    }  
    return 0;
}

static int __init tt_init(void)
{
    exit_ = 1;
    struct task_struct *tsk;
    /* 不要直接用kernel_thread创建内核线程,而应该委托专门的线程来做这件事 */
    /* pid = kernel_thread(test_it, NULL, SIGCHLD); */
    tsk = kthread_run((void *)main_loop, NULL, "KERNEL-ACCESS-USER");
    if (IS_ERR(tsk)) {
        return -1;
    }
    /* 由于接口没有导出,下面的信息来自/proc/kallsyms,
     * 但是起码也要通过模块参数传递过来吧 ...
     **/
    use_mm = 0xffffffff810c7282;
    unuse_mm = 0xffffffff810c7240;
    return 0;
}

static void tt_cleanup(void)
{
    exit_ = 0;
    return;
}

module_init(tt_init);
module_exit(tt_cleanup);
MODULE_LICENSE("GPL");


配合一个用户态程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
        char *pshare = (char *)calloc(1, 32);
        strcpy(pshare, "kernel can read!");
        while(1) {
                sleep(1);
                printf("user info:%s    %p\n", pshare, pshare);
        }
}


我在先运行了用户态程序test之后,获取它的pid为3881,pshare指针地址为0x22d7010,我将这两个值写入内核模块,编译,加载,然后内核就可以和用户态进程共享这块内存了。当然,标准的做法肯定没有这么龌龊,我只是写点代码...



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1601783

相关文章
|
2月前
|
存储 安全 数据安全/隐私保护
3.2 Windows驱动开发:内核CR3切换读写内存
CR3是一种控制寄存器,它是CPU中的一个专用寄存器,用于存储当前进程的页目录表的物理地址。在x86体系结构中,虚拟地址的翻译过程需要借助页表来完成。页表是由页目录表和页表组成的,页目录表存储了页表的物理地址,而页表存储了实际的物理页框地址。因此,页目录表的物理地址是虚拟地址翻译的关键之一。在操作系统中,每个进程都有自己的地址空间,地址空间中包含了进程的代码、数据和堆栈等信息。为了实现进程间的隔离和保护,操作系统会为每个进程分配独立的地址空间。在这个过程中,操作系统会将每个进程的页目录表的物理地址存储在它自己的CR3寄存器中。当进程切换时,操作系统会修改CR3寄存器的值,从而让CPU使用新的页
3.2 Windows驱动开发:内核CR3切换读写内存
|
3月前
|
安全 Windows
3.3 Windows驱动开发:内核MDL读写进程内存
MDL内存读写是一种通过创建MDL结构体来实现跨进程内存读写的方式。在Windows操作系统中,每个进程都有自己独立的虚拟地址空间,不同进程之间的内存空间是隔离的。因此,要在一个进程中读取或写入另一个进程的内存数据,需要先将目标进程的物理内存映射到当前进程的虚拟地址空间中,然后才能进行内存读写操作。
3.3 Windows驱动开发:内核MDL读写进程内存
|
3月前
|
缓存 监控 Anolis
|
3月前
|
Web App开发 缓存 Linux
深入理解Linux内核内存管理机制与实现(下)
深入理解Linux内核内存管理机制与实现
|
3月前
|
缓存 Linux 程序员
深入理解Linux内核内存管理机制与实现(上)
深入理解Linux内核内存管理机制与实现
|
3月前
|
存储 算法 Linux
探索Linux内核内存伙伴算法:优化系统性能的关键技术!
探索Linux内核内存伙伴算法:优化系统性能的关键技术!
|
3月前
|
存储 编译器 Linux
解密Linux内核神器:内存屏障的秘密功效与应用方法(下)
解密Linux内核神器:内存屏障的秘密功效与应用方法(下)
|
1天前
|
存储 编译器 C语言
C语言:数据在内存中的存储形式
C语言:数据在内存中的存储形式
|
1月前
|
存储 搜索推荐 编译器
C learning_11 (数组和在内存存储的理解、数组越界、数组作为形参)
C learning_11 (数组和在内存存储的理解、数组越界、数组作为形参)
|
1月前
|
存储
数据在内存中的存储
数据在内存中的存储
30 0