Linux x64 -- 内核程序(驱动程序)读取任意进程数据实现

四级页表结构

现在的64位Linux系统中,并没有使用全部的64位地址空间,而是使用其低48位,高16位并没有使用.

其中

39至47这9位用于索引PGD(page global directory),其中读取的值是PUD(page upper directory)的地址

30至38这9位用于索引PUD以获取PMD(page middle directory)的地址

21至29这9位用于索引PMD以获取PTE(the lowest level page table)的地址

12至20这9位用于索引PTE以获数据所在物理page frame的地址

最后的12位是page frame 的偏移,用于定位具体的数据所在地址

图形表示如图:

注:

原来的三级页表为:PGD-->PMD-->PTE

二级页表为:PGD(PGD)-->PTE

获取系统的page size

$ getconf PAGE_SIZE

获取当前内核使用多少级的页表结构

通过编译内核的config文件,搜索CONFIG_PGTABLE_LEVELS,既可以知道使用的是多少级的页表结构

内核地址空间中物理地址与虚拟地址的转换

#include <asm/io.h>

static inline void *phys_to_virt(phys_addr_t address);//__pa

static inline phys_addr_t virt_to_phys(volatile void *address)__va

读取任意程序内存实现

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/mm_types.h>
#include <asm/pgtable.h>

static ulong data_addr = 0;
static int data_len = 0;
static int target_pid = 0;

module_param(data_addr,ulong,S_IRUGO);
module_param(data_len,int,S_IRUGO);
module_param(target_pid,int,S_IRUGO);

static int __init main_init(void)
{
    struct task_struct *task;
    char *vaddr;
    int retval = 0;
    int i = 0;
    pgd_t *pgd = NULL;
    pud_t *pud = NULL;
    pmd_t *pmd = NULL;
    pte_t *pte = NULL;
    unsigned long paddr = 0;
    unsigned long page_addr = 0;
    unsigned long page_offset = 0;

    if(data_addr == 0 || data_len == 0 || target_pid == 0){
        printk("insmod main <data_addr=?> <data len=?> <target_pid=?>\n");
        return 0;
    }

    printk("data_addr:0x%lX, data_len:%d, target_pid:%d\n",data_addr,data_len,target_pid);

    for_each_process(task){
        if(task->pid == target_pid)
        {
            printk("find task:%s\n",task->comm);
            retval = 1;
            break;
        }
    }

    if(retval == 0){
        printk("not find task\n");
        return -1;
    }

    pgd = pgd_offset(task->mm,data_addr);
    if(pgd_none(*pgd)){
        printk("not mapped in pgd\n");
        return -1;
    }

    pud = pud_offset((p4d_t*)pgd,data_addr);
    if(pud_none(*pud)){
        printk("not mapped in pud\n");
        return -1;
    }

    pmd = pmd_offset(pud,data_addr);
    if(pmd_none(*pmd)){
        printk("not mapped in pmd\n");
        return -1;
    }

    pte = pte_offset_kernel(pmd,data_addr);
    if(pte_none(*pte)){
        printk("not mapped in pte\n");
        return -1;
    }

    page_addr = pte_val(*pte) & PTE_PFN_MASK;
    page_offset = (data_addr) & (0xFFF);
    paddr = page_addr | page_offset;

    vaddr = __va(paddr);

    for(i = 0;i<data_len;i++){
        printk("0x%X(‘%c‘)\n",vaddr[i],vaddr[i]);
    }

    return 0;
}

static void __exit main_exit(void)
{
}

module_init(main_init);
module_exit(main_exit);
MODULE_LICENSE("GPL");

makefile:

obj-m := main.o

all:
    make -C /usr/src/linux-headers-$(shell uname -r) M=$(shell pwd) modules
clean:
    make -C /usr/src/linux-headers-$(shell uname -r) M=$(shell pwd) clean

参考资料

How The Kernel Manages Your Memory:

https://manybutfinite.com/post/how-the-kernel-manages-your-memory/

Five-level page tables:

https://lwn.net/Articles/717293/

Page Table Management:

https://www.kernel.org/doc/gorman/html/understand/understand006.html

Page table

https://github.com/lorenzo-stoakes/linux-vm-notes/blob/master/sections/page-tables.md

Virtual memory map

https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt

Linux 内存管理分析

https://www.jianshu.com/p/fc719d1cfbc2

Linux中的虚拟地址转换物理地址实现

http://lzz5235.github.io/2014/12/10/linux.html

原文地址:https://www.cnblogs.com/r1ng0/p/9720057.html

时间: 2024-10-25 04:28:08

Linux x64 -- 内核程序(驱动程序)读取任意进程数据实现的相关文章

12_通过 CR3 切换_读取指定进程数据

注意: cr3 切换 ,导致eip 指向的页面,改变为对应cr3 的页面:所以代码也变了:这里需要将这部分代码放入公共区域. 解决: 使用 类似前面 山寨 systemfastcallentry 的方法:使用 int 20 将代码拷贝到 高2gb 公共区域. 注册到 int 21,然后中断调用即可. 原文地址:https://www.cnblogs.com/leibso-cy/p/11719250.html

Linux程序设计学习笔记----System V进程通信(共享内存)

转载请注明出处:http://blog.csdn.net/suool/article/details/38515863 共享内存可以被描述成内存一个区域(段)的映射,这个区域可以被更多的进程所共享.这是IPC机制中最快的一种形式,因为它不需要中间环节,而是把信息直接从一个内存段映射到调用进程的地址空间. 一个段可以直接由一个进程创建,随后,可以有任意多的进程对其读和写.但是,一旦内存被共享之后,对共享内存的访问同步需要由其他 IPC 机制,例如信号量来实现.象所有的System V IPC 对象

Linux程序设计学习笔记----System V进程通信之消息队列

一个或多个进程可向消息队列写入消息,而一个或多个进程可从消息队列中读取消息,这种进程间通讯机制通常使用在客户/服务器模型中,客户向服务器发送请求消息,服务器读取消息并执行相应请求.在许多微内核结构的操作系统中,内核和各组件之间的基本通讯方式就是消息队列.例如,在 MINIX 操作系统中,内核.I/O 任务.服务器进程和用户进程之间就是通过消息队列实现通讯的. Linux中的消息可以被描述成在内核地址空间的一个内部链表,每一个消息队列由一个IPC的标识号唯一的标识.Linux 为系统中所有的消息队

Linux下编写驱动程序(VFS)

转:http://hi.baidu.com/firstm25/item/8fe022155e1fa78988a9568f 摘要:设备驱动程序是操作系统内核与机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节.那么驱动程序如何书写实现这一接口功能是本文讨论的重点,并以一简单的驱动程序介绍书写细节. 在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.(应用程序一般是在用户态下进行)也就是说系统必须在驱动程序的子函数返回后才能进行其它的工作,即驱动程序不能进入死循环. 字符型设备

Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介

Linux内核分析(四) 两天没有更新了,上次博文我们分析了linux的内存管理子系统,本来我不想对接下来的进程管理子系统.网络子系统.虚拟文件系统在这个阶段进行分析的,但是为了让大家对内核有个整体的把握,今天还是简单的介绍一下剩余的几个子系统,我们对这几个子系统的分析,只要了解其作用和部分内容即可,不必深究,等我们写上几个驱动,到时候按照驱动再来分析这几个子系统我们就清晰多了. 在http://www.cnblogs.com/wrjvszq/p/4257164.html一文我们提到过linux

Linux下的TCP/IP编程----进程及多进程服务端

在之前的学习中我们的服务端同一时间只能为一个客户端提供服务,即使是将accept()函数包含在循环中,也只能是为多个客户端依次提供服务,并没有并发服务的能力,这显然是不合理的.通过多进程的使用,我们可以很便捷的实现服务端的多进程,这样就可以同时为多个客户端提供服务. 首先我们要理解程序,进程,进程ID,僵尸进程,线程的概念. 程序:广泛的说就是为了达到某一目的二规定的途径,在编程中具体的就是为了实现某一功能而编写的代码实体,是静态的. 进程:程序的一次动态执行就是一个进程,它是占用了一定内存空间

【转载】 java利用snmp4j包来读取snmp协议数据(Manager端)

https://www.cnblogs.com/xdp-gacl/p/4187089.html http://doc.okbase.net/yuanfy008/archive/265663.html java利用snmp4j包来读取snmp协议数据(Manager端) 1 snmp简单介绍 java利用snmp4j包来读取snmp协议数据,很简单的一个流程,就是利用java来读取运行snmp协议的数据,例如服务器.PC机或者路由器等运行了snmp协议的设备. snmp协议是什么呢? 简单网络管理

第一次作业:基于Linux系统深入源码分析进程模型

1.前言 本文主要基于Linux 2.6源代码分析进程模型.源代码下载地址:https://elixir.bootlin.com/linux/v2.6.39/source 2.进程 定义:进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础. 3.Linux系统进程的组织 进程是由进程控制块(PCB).程序段.数据段三部分组成. 3.1 进程控制块 进程控制块(Processing Control Block),是操作系统核心中一种数据结构

[转帖]Linux系统进程的知识总结,进程与线程之间的纠葛...

Linux系统进程的知识总结,进程与线程之间的纠葛... https://cloud.tencent.com/developer/article/1500509 当一个程序开始执行后,在开始执行到执行完毕退出这段时间内,它在内存中的部分就叫称作一个进程. Linux 是一个多任务的操作系统,也就是说,在同一时间内,可以有多个进程同时执行.我们大家常用的单CPU计算机实际上在一个时间片段内只能执行一条指令.那么Linux是如何实现多进程的同时执行的呢?原来Linux使用了一种称为” 进程调度 “的