此文章是对《打印指定进程中的数据》的扩展,增加了用户空间的控制接口,可以实现从用户空间发送指令,指定要获取数据的进程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处的数据,执行方法和获取的内容如下: