吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4328|回复: 10
收起左侧

[其他转载] 开源一个Linux内核里进程内存管理模块源码

  [复制链接]
Victory.ms 发表于 2020-5-4 00:26
本帖最后由 Victory.ms 于 2021-3-26 00:24 编辑

Linux它是一款开源的内核系统。本人也非常喜欢嵌入式Linux系统,特别是它的内核源码,书写的风格,都非常讨我心欢。这个驱动是之前业余的时候写的,不过对于新手来说,至少还是有学习价值的。
QQ截图20210218213825.png
QQ截图20210218213841.jpg


下面将对源码进行简单的讲解。
首先是隐藏内核驱动模块。
[C] 纯文本查看 复制代码
list_del_init(&__this_module.list);
kobject_del(&THIS_MODULE->mkobj.kobj);


list_del_init是将自身驱动模块从驱动列表(lsmod)中抹掉
kobject_del是将自己从/sys/class/xxxxxx中抹掉


接下来是打开进程接口。

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


nr即为进程pid数值


[C] 纯文本查看 复制代码
#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下可以看到

[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机制,所以只能以页为单位计算物理内存地址。计算物理内存地址的方法有很多,这里提供三种思路:
第一种是使用get_user_pages,
第二种是直接使用pagemap文件,
第三种是纯手写算法(将pgd、pud、pmd和pte拼接在一起)
我个人推荐使用第三种方法,也是我正在使用的方法,速度极快而且不触碰任何的进程文件,被调试进程无任何感知。
不推荐使用第一种get_user_pages方法,因为此方法会触发反调试机制,导致调试失败。
知道了物理内存地址后,读、写物理内存地址,其实Linux内核里面也有演示,即drivers/char/mem.c。写的非常详细。最后还要注意MMU机制的离散内存,即buffer不连续问题,通俗的说就是不要一下子读太多,读到另一页去了,要分开页来读

获取进程内存块列表
这个接口就很简单了,通过task_struct取出mm_struct,接下来在mm_struct中遍历取出vma。详情可以参考代码fs\proc\task_mmu.c
[C] 纯文本查看 复制代码
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_MMU
        unsigned 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);
#endif
        unsigned 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变量,储存的地址值即是进程命令行。
但这里有个要注意的地方,经过我多台设备测试发现,并不是每个Linux内核系统的arg_start变量偏移值是一样的,这样子就会非常危险,一旦读错就会死机,而且原因还不好查找。
这里我使用了一些玄学的技巧,驱动可以自适应地在不同的Linux内核中自行修正arg_start变量的偏移值,从而读取到正确的值,不会死机。


获取进程PID列表
驱动里写入了两种方法,第一种是遍历/proc/pid目录,第二种是遍历task_struct结构体。这里有个要注意的地方,经过我多台设备测试发现,并不是每个Linux内核系统的task_struct结构体里的tasks变量偏移值是一样的,但具体玄学修正方法我还没时间进行编写,待有空再补充。

获取进程物理内存大小
读取task_struct结构体里的mm_struct,再读取rss_stat就会有进程的物理内存占用大小,这个来源与/proc/pid/status里的源码编写。这里同样需要玄学技巧修正变量的偏移值,具体方法我已编写在内。


获取进程权限、设置进程权限为ROOT
在取得task_struct进程结构后,观察头文件可以发现里面有两个变量值,一个是real_cred,另一个是cred,其实很简单,将两个cred里面的uid、gid、euid、egid、fsuid、fsgid修改成0即可
[C] 纯文本查看 复制代码
const struct cred __rcu *real_cred;
const struct cred __rcu *cred;

real_cred指向主体和真实客体证书,cred指向有效客体证书。通常情况下,cred和real_cred指向相同的证书,但是cred可以被临时改变
同样需要注意,每个Linux内核的cred结构变量偏移值并不是一样的,读错会死机,同理,我也使用了玄学的技巧,驱动能智能修正Linux内核变量的偏移值,能准确的识别出每个Linux内核版本里real_cred与cred的正确偏移位置

QQ图片20210218221257.png QQ图片20210218221303.png





开源地址(含demo)
https://github.com/abcz316/rwProcMem33  

总结:
首先,编译此源码需要一定的技巧,再者,手机厂商本身已设置多重障碍用来阻止第三方驱动的加载,如果您需要加载此驱动,则需要将内核中的一些限制给去除。(其实这些验证都可以用IDA暴力Patch之~)。以上仅供交流学习Linux内核使用,禁止用于非法用途,另外,在源代码里有一小部分代码已经被替换成效果相同的其他写法,以防止他人作非法用途,希望大家能营造一个良好的技术交流环境。

提醒:
本源码不针对任何程序,仅供交流、学习、调试Linux内核程序的用途,禁止用于任何非法用途,调试器是一把双刃剑,希望大家能营造一个良好的Linux内核Rootkit技术的交流环境。
后续:
后面即将开源:
“不需要源码,强制暴力修改手机Linux内核、去除加载内核驱动的所有验证”
“不需要源码,强制加载启动ko驱动文件、跨Linux内核版本、跨设备启动ko驱动模块文件”
“不需要源码,Linux内核进程万能调试器-可过所有的反调试机制-含硬件断点:Linux天下调,让天下没有难调试的Linux进程!”

不需要源码,突破Linux内核Elf结构限制、将ollvm混淆加入到ko驱动文件中,增加驱动逆向难度

免费评分

参与人数 7吾爱币 +9 热心值 +5 收起 理由
13995937766 + 1 + 1 谢谢@Thanks!
wushaominkk + 3 + 1 鼓励转贴优秀软件安全工具和文档!
wapj5214 + 1 + 1 用心讨论,共获提升!
影视专业 + 1 谢谢@Thanks!
hostclsecho + 1 + 1 我很赞同!
东方学痞 + 1 谢谢@Thanks!
tianshu44 + 1 + 1 热心回复!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

chen299 发表于 2020-5-4 07:42
学习学习
jwpiaoi 发表于 2020-5-4 08:05
hmily65 发表于 2020-5-4 08:34
ioajp 发表于 2020-5-4 08:37
楼主厉害一般人没有用
huohua1991 发表于 2020-5-4 09:23
好牛逼,学习学习
微笑男孩 发表于 2020-5-4 09:37
感谢楼主分享,虽然我只能看懂一小部分
wapj5214 发表于 2020-5-4 10:11
给大哥顶顶
醉卧青山 发表于 2020-5-18 15:03
挺厉害的, 只不过这种门槛较高, 要编译内核,刷内核, 所以他的兼容性并不好, 而且风险高, 还可能一不小心刷废了, 这就意味着不良作者没法拿它卖, 不过学习技术还是不错的.
破解project 发表于 2020-5-30 14:56
仅仅是搞应用层的萌新瑟瑟发抖。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 23:25

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表