获取指定进程中的数据

此文章是对《打印指定进程中的数据》的扩展,增加了用户空间的控制接口,可以实现从用户空间发送指令,指定要获取数据的进程id和内存地址,然后将取到的数据返回给用户空间。

下面是驱动部分的代码


#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/device.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/fs_struct.h>
#include <linux/miscdevice.h>
#include <linux/sched/signal.h>

#define CM_MINOR 234

/*
pid     进程id
addr    内存地址
buf     保存取到的内容
buf_len 内存长度
*/
static int mem_print(int pid, long long addr, char* buf, int buf_len)
{
    int offset = 0;
    void *p_addr = NULL;
    unsigned long pfn = 0;
	struct task_struct * p;
    pgd_t *pgd = NULL;
    p4d_t *p4d = NULL;
    pud_t *pud = NULL;
    pmd_t *pmd = NULL;
    pte_t *pte = NULL;
    struct page *page = NULL;
    struct mm_struct *mm = NULL;
    struct fs_struct *fs = NULL;
 
    rcu_read_lock();
    // 遍历系统中的每个进程
	for_each_process(p) {
		// 不是目标进程
		if (pid != p->pid)
		{
			continue;
		}
 
		printk("------\n");
		printk("state - %lX\n", p->state);
		printk("pid - %X\n", p->pid);
 
		fs = p->fs;
		if (NULL != fs)
		{
			struct path cur_path = fs->pwd;
			if (NULL != cur_path.dentry)
			{
				struct dentry *dentry = cur_path.dentry;
				while (NULL != dentry)
				{
					printk("d_iname = %s ,", dentry->d_iname);
 
					if (0 == strcmp("/", dentry->d_iname))
					{
						break;
					}
 
					dentry = dentry->d_parent; // 父目录
				}
			}
		}
 
		mm = p->mm;
		if (NULL != mm)
		{
			//struct vm_area_struct *vm_area = mm->mmap;
 
			pgd = pgd_offset(mm, addr);
			printk("pgd = %ld, %p\n", pgd, pgd);
 
			p4d = p4d_offset(pgd, addr);
 
			pud = pud_offset(p4d, addr);
			printk("pud = %ld, %p\n", pud, pud);
			
			pmd = pmd_offset(pud, addr);
			printk("pmd = %d, %p\n", pmd, pmd);
 
			pte = pte_offset_kernel(pmd, addr); // 页表的线性地址
 
			page = pte_page(*pte); // 页框的线性地址
			if (NULL != page)
			{
				printk("page = %lx\n", page);
 
				printk("flags = %u\n", page->flags);
 
				// 地址在页框内的偏移
				offset = addr & 0xFFF;
				printk("offset = %d\n", offset);
 
				pfn = page_to_pfn(page);
				printk("pfn = %x\n", pfn);
 
				p_addr = page_address(page);
				printk("p_addr = %lx\n", p_addr);
 
				printk("data: %s\n", p_addr + offset);

                memcpy(buf, p_addr + offset, buf_len);
			}
		}
	}

    rcu_read_unlock();
 
	return 0;
}

static ssize_t cm_read(struct file *file, __user char *buffer, size_t count,
			loff_t *ppos)
{
    int retval = 0;
    int pid = 0;
    long long addr = 0;

    // 申请空间
    char *buf = (char *) __get_free_page(GFP_USER);

    if (!buf)
		return -ENOMEM;
    
    // 读取用户空间的数据:4字节进程id,8字节目标数据内存地址
    if (copy_from_user(buf, buffer, 12))
		goto out;
    
    memcpy(&pid, buf, 4);
    memcpy(&addr, buf+4, 8);

	// 读取指定内容
    mem_print(pid, addr, buf, count);

    // 返回给用户空间
    copy_to_user(buffer, buf, count);

 out:
	free_page((unsigned long)buf);

	return retval;
}


static int cm_open(struct inode *inode, struct file *file)
{
    int retval = 0;
    printk("cm_open\n");

	return retval;
}


static int cm_close(struct inode *inode, struct file *file)
{
    int retval = 0;
    printk("cm_close\n");

	return retval;
}

static const struct file_operations cm_fops = {
	.owner	= THIS_MODULE,
	.read  = cm_read,
	.open	= cm_open,
	.release = cm_close,

};

static struct miscdevice cm_miscdev = {
	.minor = CM_MINOR,
	.name = "copymemory",
	.nodename = "copymemory",
	.fops = &cm_fops,
};

static int __init cm_init(void)
{
    int ret = 0;

    printk("cm_init\n");

    ret = misc_register(&cm_miscdev);
	if (ret) {
		pr_err("Can't register misc device %d\n", CM_MINOR);
		goto err_cm;
	}
    return 0;

err_cm:
    return ret;
}

static void cm_cleanup(void)
{
    printk("cm_cleanup\n");
	misc_deregister(&cm_miscdev);
}

module_init(cm_init);
module_exit(cm_cleanup);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("copy memory driver");
MODULE_ALIAS_MISCDEV(CM_MINOR);

下面是用户空间,发送指令的示例代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
    if (argc < 3)
    {
        printf("invalid param,need: pid addr\n");
        return -1;
    }

    int pid = atoi(argv[1]); // 进程id
    long long addr = atoll(argv[2]); // 数据内存地址

    int fd = open("/dev/copymemory", O_RDONLY);
    if (fd < 0)
    {
        printf("open failed, ret: %d\n", fd);
        return -1;
    }

    char buf[100] = {0};
    memcpy(buf, &pid, sizeof(int));
    memcpy(buf+4, &addr, sizeof(addr));
    read(fd, buf, 100);

    printf("content: %s\n", buf);

    close(fd);
    return 0;
}

 a.out为用户空间代码编译的程序,获取进程17750,内存0x4006b7处的数据,执行方法和获取的内容如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/367671.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年混合云:趋势和预测

混合云环境对于 DevOps 团队变得越来越重要&#xff0c;主要是因为它们能够弥合公共云资源的快速部署与私有云基础设施的安全和控制之间的差距。这种环境的混合为 DevOps 团队提供了灵活性和可扩展性&#xff0c;这对于大型企业中的持续集成和持续部署 (CI/CD) 至关重要。 在混…

react+ProComponents简单实现表格

文章目录 使用ProComponents的原因 一般后台管理系统&#xff0c;大部分页面功能都是列表和表单的形式。 即便使用了组件、等&#xff0c;依旧需要写大量高度重复性的代码&#xff0c;比如列表页通常会有 筛选栏、操作栏、表格区域、和分页栏四个部分&#xff0c; 新增/编辑页…

【JavaEE Spring】Spring事务和事务传播机制

Spring事务和事务传播机制 1. 事务回顾1.1 什么是事务?1.2 为什么需要事务?1.3 事务的操作 2. Spring 中事务的实现2.1 Spring编程式事务(了解)2.2 Spring声明式事务Transactional 3. Transactional 详解3.1 rollbackFor3.2 事务隔离级别3.2.1 MySQL事务隔离级别(回顾)3.2.2 …

avast网页随机密码生成器

随机密码生成器 | 告别 12345 | Avast 可以生成随机密码 按需调整

C++ 新特性 构造函数

1.委托构造函数 委托构造函数出现的意义: 委托构造初始化能够减少代码的冗余的问题 使代码变得简洁 明朗 现在大家来看一个例子: 本代码采用了普通函数的构造方法 看起来简洁一些 但是并没有从实际上解决问题 尤其是对于 复杂数据类型的存储 比如String类型 已经发生了默认的…

cherles抓包,安卓,http/https

前置条件&#xff1a;charles抓取手机数据包的前提&#xff0c;手机和电脑需要在一个局域网内。 1、charles官网下载地址 Download a Free Trial of Charles • Charles Web Debugging Proxy 下载以后按提示安装即可。 2、charles的配置 在charles中【proxy->proxy Set…

nba2k24 灌篮高手Q版流川枫面补

nba2k24 灌篮高手Q版流川枫面补 此面补nba2k23-nba2k24通用 下载地址&#xff1a; https://www.changyouzuhao.cn/9979.html

kubernetes基本概念和操作

基本概念和操作 1.Namespace1.1概述1.2应用示例 2.Pod2.1概述2.2语法及应用示例 3.Label3.1概述3.2语法及应用示例 4.Deployment4.1概述4.2语法及应用示例 5.Service5.1概述5.2语法及应用示例5.2.1创建集群内部可访问的Service5.2.2创建集群外部可访问的Service5.2.3删除服务5.…

【Go语言成长之路】创建Go模块

文章目录 创建Go模块一、包、模块、函数的关系二、创建模块2.1 创建目录2.2 跟踪包2.3 编写模块代码 三、其它模块调用函数3.1 修改hello.go代码3.2 修改go.mod文件3.3 运行程序 四、错误处理4.1 函数添加错误处理4.2 调用者获取函数返回值4.4 执行错误处理代码 五、单元测试5.…

R语言分析任务:

有需要实验报告的可CSDN 主页个人私信 《大数据统计分析软件&#xff08;R语言&#xff09;》 实 验 报 告 指导教师&#xff1a; 专 业&#xff1a; 班 级&#xff1a; 姓 名&#xff1a; 学 …

【Pg数据库】删除数据库失败,提示有session正在连接

目录 问题现象原因分析处理方法1.断开所有连接2. 查找相关连接数据库的主机信息3. 再次删除 总结 问题现象 Navicat 删除 PostgreSQL 数据库时失败&#xff0c;提示&#xff1a;正在被其他用户访问&#xff08;有session正在连接&#xff09;如何处理&#xff1f; 如下所示&am…

【七】【C++】模版初阶

泛型编程 C中的泛型编程是一种编程范式&#xff0c;它强调代码的重用性和类型独立性。通过泛型编程&#xff0c;你可以编写与特定数据类型无关的代码&#xff0c;使得相同的代码可以用于多种数据类型。 利用重载实现泛型编程 /*利用重载实现泛型编程*/ #include<iostream&…

python中的可变与不可变、深拷贝和浅拷贝

个人猜想&#xff08;很遗憾失败了&#xff09; 在硬盘或者系统中存在一个字符集 如果存在硬盘中&#xff0c;那么硬盘出厂的时候他的字符集所占用的空间就已经确定了。 如果存在于系统的话&#xff0c;硬盘应该在出厂的时候为系统设置一个存储系统字符集的地方。在安装系统…

js获取文件名或文件后缀名(扩展名)的几种方法

有时候我们需要通过含有文件名和后缀名的一个字符串中提取出该文件的文件名或文件后缀名&#xff08;扩展名&#xff09;&#xff0c;可以通过如下几种方式进行截取。 例如文件名为: var fileName"12345.txt"; 方式一&#xff1a;subtring() 用法参考博文 【js截取字…

灵伴科技(Rokid)借助 Knative 实现 AI 应用云原生 Serverless 化

作者&#xff1a;朱炜栋、元毅、子白 公司介绍 Rokid 创立于 2014 年&#xff0c;是一家专注于人机交互技术的产品平台公司&#xff0c;2018 年即被评为国家高新技术企业。Rokid 作为行业的探索者、领跑者&#xff0c;目前致力于 AR 眼镜等软硬件产品的研发及以 YodaOS 操作系…

K8s 集群可观测性-数据分流最佳实践

简介 在微服务架构下&#xff0c;一个 k8s 集群中经常会部署多套业务&#xff0c;同时也意味着不同团队、不同角色、不同的业务会在同一集群中&#xff0c;需要将不同业务的数据在不同的空间进行管理和查看。 在传统的主机环境下&#xff0c;这个是可以通过不同的主机部署 Da…

力扣每日一题 ---- 1906. 查询差绝对值的最小值

本题中&#xff0c;我们的题目求的是差值的最小值&#xff0c;我们考虑一个因素&#xff0c;当前题目中给出的数组是没有排序过的&#xff0c;那么想要求的差值&#xff0c;是不是要两两配对进行判断差值最小值。这里我们就很费时间了&#xff0c; O(N^2)的时间复杂度&#xf…

学习笔记:超详解换根法(换根DP)(匠心之作)

一.换根DP的概念 1.换根DP是什么&#xff1f; 换根DP&#xff0c;又叫二次扫描&#xff0c;是树形DP的一种。 2.换根DP能解决什么问题&#xff1f; 换根DP能解决不指定根结点&#xff0c;并且根节点的变化会对一些值产生影响的问题。例如子结点深度和、点权和等。如果要 暴力…

【数据库】分区的优点和缺点

​ &#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;数据库 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 优点&#xff1a; 缺点&#xff1a; 结语 我的其他博客 ​ 前言 数据库中的分区技术为处理大规模数据提供了一种有效的手段…

C++:输入流/输出流

C流类库简介 C为了克服C语言中的scanf和printf存在的缺点。&#xff0c;使用cin/cout控制输入/输出。 cin&#xff1a;表示标准输入的istream类对象&#xff0c;cin从终端读入数据。cout&#xff1a;表示标准输出的ostream类对象&#xff0c;cout向终端写数据。cerr&#xff…