获取指定进程中的数据

此文章是对《打印指定进程中的数据》的扩展,增加了用户空间的控制接口,可以实现从用户空间发送指令,指定要获取数据的进程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);

上述代码中的mem_print也可以用下面的方法来实现。

static int mem_print(int pid, long long addr, char* buf, int buf_len)
{
	int copied;
	struct task_struct * p;
 
    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);

		copied = access_process_vm(p, addr, buf, buf_len,
			FOLL_FORCE);
		break;
	}
	rcu_read_unlock();

	return 0;
}

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

#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处的数据,执行方法和获取的内容如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值