git获取linux内核源码_开源一个Linux进程内存内核管理模块源码

本文为看雪论坛优秀文章

看雪论坛作者ID:abcz316

Linux它是一款开源的内核系统。本人也非常喜欢嵌入式Linux系统,特别是它的内核源码,书写的风格,都非常讨我心欢。这个驱动是之前业余的时候写的用于嵌入式开发版,点亮LED灯时候留下的,现在代码里已删除ioremap。不过对于新手来说,至少还是有学习价值的。

1c93129c020c13941617d05e82e43784.png

源码仅供交流学习之用,不得用于非法用途。

同时为了避免不法分子将此驱动用在非法的用途,我后面也将附上侦测建议。如果您是游戏厂商,那么可以尝试听取我的侦测建议。

下面我将带大家分析这个驱动,看看驱动是怎么写出来的。

首先是打开进程接口。

在Linux内核里,不区分进程与线程。统一按照线程来看待。那么每个线程都有一个对应的pid_t、pid、task_struct。他们之间的关系是这样的:pid_t <–> struct pid
nr为进程pid数值

#include <linux/pid.h>pid_t pid_vnr(struct pid *pid){return pid_nr_ns(pid, current->nsproxy->pid_ns);}EXPORT_SYMBOL_GPL(pid_vnr);struct pid *find_pid_ns(int nr, struct pid_namespace *ns);EXPORT_SYMBOL_GPL(find_pid_ns);struct pid *find_vpid(int nr){return find_pid_ns(nr, current->nsproxy->pid_ns);}EXPORT_SYMBOL_GPL(find_vpid);struct pid *find_get_pid(int nr){struct pid *pid;rcu_read_lock();pid = get_pid(find_vpid(nr));rcu_read_unlock();return pid;}EXPORT_SYMBOL_GPL(find_get_pid);void put_pid(struct pid *pid);EXPORT_SYMBOL_GPL(put_pid);struct pid * –> struct task_struct *#include <linux/pid.h>struct pid *get_task_pid(sturct task_struct *task, enum pid_type);EXPORT_SYMBOL_GPL(get_task_pid);struct task_struct *pid_task(struct pid *pid, enum pid_type);EXPORT_SYMBOL(pid_task);struct task_struct *get_pid_task(struct pid *pid, enum pid_type){struct task_struct *result;rcu_read_lock();result = pid_task(pid, type);if (result)get_task_struct(result);rcu_read_unlock();return result;}EXPORT_SYMBOL(get_pid_task);#include <linux/sched.h>#define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while (0)static inline void put_task_struct(struct task_struct *t){if (atomic_dec_and_test(&t->usage))__put_task_struct(t);}void __put_task_struct(struct task_struct *t);EXPORT_SYMBOL_GPL(__put_task_struct);

看完以上的逻辑。大家是不是柳暗花明又一村,心里开朗了许多,他们之间是可以相互转换的。通过进程pid_t可以拿到pid,通过pid可以拿到task_struct。又可以反过来通过task_struct拿到进程pid。

接下来是关闭进程接口

驱动源码是使用put_pid将进程pid*的使用次数减去1

在Linux内核源码/kernel/pid.c下可以看到

void put_pid(struct pid *pid){struct pid_namespace *ns;if (!pid)return;ns = pid->numbers[pid->level].ns;if ((atomic_read(&pid->count) == 1) ||atomic_dec_and_test(&pid->count)) {kmem_cache_free(ns->pid_cachep, pid);put_pid_ns(ns);}}EXPORT_SYMBOL_GPL(put_pid);

读、写进程内存接口

首先根据pid*用get_pid_task取出task_struct。再用get_task_mm取出mm_struct结构。因为这个结构包含了进程的内存信息。首先检查内存是否可读if (vma->vm_flags & VM_READ)
如果可读。那么开始计算物理内存地址位置。由于Linux内核默认开启MMU机制,所以只能以页为单位计算物理内存地址。计算物理内存地址的方法有很多。如

pagemap、pgd pud pmd pte、get_user_pages

驱动里演示pagemap。其他方法可自行参考
Linux内核源码/fs/proc/task_mmu.c
知道了物理内存地址后,读、写物理内存地址,Linux内核也有演示:drivers/char/mem.c。写的非常详细。最后还要注意MMU机制的离散内存,即buffer不连续问题

获取进程内存块列表

这个接口没什么技术含量,都是照抄Linux内核源码的代码,fsproctask_mmu.c。核心思想是通过task_struct取出mm_struct,接下来在mm_struct中遍历取出vma。

struct mm_struct {struct vm_area_struct * mmap; /* list of VMAs */struct rb_root mm_rb;struct vm_area_struct * mmap_cache; /* last find_vma result */#ifdef CONFIG_MMUunsigned long (*get_unmapped_area) (struct file *filp,unsigned long addr, unsigned long len,unsigned long pgoff, unsigned long flags);void (*unmap_area) (struct mm_struct *mm, unsigned long addr);#endifunsigned long mmap_base; /* base of mmap area */unsigned long mmap_legacy_base; /* base of mmap area in bottom-up allocations */unsigned long task_size; /* size of task vm space */unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */unsigned long free_area_cache; /* first hole of size cached_hole_size or larger */unsigned long highest_vm_end; /* highest vma end address */pgd_t * pgd;atomic_t mm_users; /* How many users with user space? */atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */int map_count; /* number of VMAs */spinlock_t page_table_lock; /* Protects page tables and some counters */struct rw_semaphore mmap_sem;struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung* together off init_mm.mmlist, and are protected* by mmlist_lock*/unsigned long hiwater_rss; /* High-watermark of RSS usage */unsigned long hiwater_vm; /* High-water virtual memory usage */unsigned long total_vm; /* Total pages mapped */unsigned long locked_vm; /* Pages that have PG_mlocked set */unsigned long pinned_vm; /* Refcount permanently increased */unsigned long shared_vm; /* Shared pages (files) */unsigned long exec_vm; /* VM_EXEC & ~VM_WRITE */unsigned long stack_vm; /* VM_GROWSUP/DOWN */unsigned long def_flags;unsigned long nr_ptes; /* Page table pages */unsigned long start_code, end_code, start_data, end_data;unsigned long start_brk, brk, start_stack;unsigned long arg_start, arg_end, env_start, env_end;unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */

获取进程命令行

mm_struct结构体里面有个arg_start变量,储存的地址值即进程命令行

为了避免不法分子将此驱动用在非法的用途, 在此给出侦测建议:

  1. 检查是否有/dev/rwProcMem33此文件
  2. 检查SELinux是否被关闭。从安卓5.0启用SELinux后,APP想要与驱动进行通讯,必须得关闭SELinux,如果发现SELinux是关闭状态,并且是高版本的安卓系统,那么此安卓使用者必有问题。检测SELinux的方法很多,如open打开某个文件、ioctl等等,如果SELinux是打开状态,那么这些都会直接返回EACCES (Permission denied),提示拒绝访问。另外,在高版本的安卓系统中,如安卓10,如果SELinux是打开的状态,那么lsmod查看驱动列表,也会直接返回Permission denied拒绝访问,如果lsmod能直接显示驱动列表,那么此使用者的SELinux也是有问题的,有可能被关闭了的

最后开源地址(含demo)

Github跳转

总结:

首先,编译此源码需要一定的技巧,再者,手机在出厂时本身已设置多重障碍用来阻止第三方驱动的加载(即使你拥有root权限也无法加载),此源码仅供交流学习Linux系统使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值