首页
社区
课程
招聘
[原创]Kernel从0开始(一)
发表于: 2021-11-2 11:57 45048

[原创]Kernel从0开始(一)

2021-11-2 11:57
45048

网上一大堆教编译内核的,但由于我的水平太菜,很多教程我看得特别迷糊。还有第一次编译内核时,没设置好参数,直接把虚拟机编译炸开了。所以就想着能不能先做个一键获取内核源码和相关vmlinux以及bzImage的脚本,先试试题,后期再深入探究编译内核,加入debug符号什么的,所以就有了这个一键脚本。

这个直接看我的项目就好了,我是直接拖官方的docker,然后把编译所需要的环境都重新安装了一遍,基本可以适配所有环境,安装各个版本的内核,外加调试信息也可以配置

PIG-007/kernelAll (github.com)

前置环境,前置知识啥的在上面已经足够了,如果还是感觉有点迷糊可以再去搜搜其他教程。看雪的钞sir师傅和csdn上的ha1vk师傅就很不错啊,还有安全客上的ERROR404师傅

钞sir师傅:Ta的论坛 (pediy.com)

ha1vk师傅:kernel- CSDN搜索

error404师傅:Kernel Pwn 学习之路(一) - 安全客,安全资讯平台 (anquanke.com)

这个系列记录新的kernel解析,旨在从源码题目编写,不同内核版本来进行各式各样的出题套路解析和exp的解析。另外内核的pwn基本都是基于某个特定版本的内核来进行模块开发,而出漏洞地方就是这个模块,我们可以借助这个模块来攻破内核,所以我们进行内核pwn的时候,最应该先学习的就是一些简单内核驱动模块的开发。

首先最简单和经典的的Hello world

module.h:包含可装载模块需要的大量符号和函数定义。

init.h:指定初始化模块方面和清除函数。

另外大部分模块还包括moduleparam.h头文件,这样就可以在装载的时候向模块传递参数。而我们常常用的函数_copy_from_user则来自头文件uaccess.h

这个就是模块许可证,具体有啥用不太清楚,如有大佬恳请告知。可以通过下列命令查询

image-20210927100912684

或者网址Linux内核许可规则 — The Linux Kernel documentation

一般以 __init标识声明,返回值为0表示加载成功,为负数表示加载失败。用来初始化,定义之类的。

在整个模块的最后加上

来通过这个init函数加载模块

一般以 __exit标识声明,用来释放空间,清除一些东西的。

同样的模块最后加上以下代码来卸载

▲其实加载和卸载有点类似于面向对象里的构造函数和析构函数。

以上是一个最简单的例子,下面讲讲实际题目的编写,实际的题目一般涉及驱动的装载。

由于模块装载是在内核启动时完成的(root下也可以设置再insmod装载),所以一般需要安装驱动,通过驱动来启动模块中的代码功能。而驱动类型也一般有两种,一种是字符型设备驱动,一种是globalmem虚拟设备驱动。

首先了解一下驱动设备的结构体:

然后就是套路编写

这样简单的驱动就安装完了,安装完了之后,我们想要使用这个驱动的话,还需要进行交互,向驱动设备传递数据,所以上面的xxx_fops,即file_operations这个结构体就起到了这个功能。

有的时候安装注册设备驱动需要用到class来创建注册,原因未知:

安装完成之后还需要交互,用到file_operations结构体中的成员函数,首先了解下这个结构体。

其中常用的就是read,write等函数。

之后也是正常的调用函数,套路编写

然后需要file_operations结构体中的函数来重写用户空间的write,open,read等函数:

这样当用户空间打开该设备,调用该设备的write函数,就能通过.write进入到xxx_write函数中。

▲这样一些常规kernel题的编写模板就总结出来了。

原题:https://github.com/black-bunny/LinKern-x86_64-bypass-SMEP-KASLR-kptr_restric

其中日志级别定义如下:

其中flags一般设置为GFP_KERNEL或者GFP_DMA,在堆题中一般就是

GFP_KERNEL模式,如下:

 |– 进程上下文,可以睡眠     GFP_KERNEL
 |– 进程上下文,不可以睡眠    GFP_ATOMIC
 |  |– 中断处理程序       GFP_ATOMIC
 |  |– 软中断          GFP_ATOMIC
 |  |– Tasklet         GFP_ATOMIC
 |– 用于DMA的内存,可以睡眠   GFP_DMA | GFP_KERNEL
 |– 用于DMA的内存,不可以睡眠  GFP_DMA |GFP_ATOMIC

具体可以看

Linux内核空间内存申请函数kmalloc、kzalloc、vmalloc的区别【转】 - sky-heaven - 博客园 (cnblogs.com)

这个就不多说了,就是简单的释放。

这两个就不讲了,顾名思义。

剩下的好多就是常见的注册函数了

这个不太清楚,题目见的也少,先忽略。

printk()函数的总结 - 深蓝工作室 - 博客园 (cnblogs.com)

Linux kernel pwn notes(内核漏洞利用学习) - hac425 - 博客园 (cnblogs.com)

Linux设备驱动(二)字符设备驱动 | BruceFan's Blog (pwn4.fun)

 
 
 
 
 
 
 
//注释头
 
//由于基本都是用下载的内核编译,所以这里的头文件直接放到正常的编译器中可能找不到对应的头文件。
//在自己下载的编译好的内核中自己找对应的,然后Makefile中来设置内核源码路径
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("Dual BSD/GPL");
static int __init hello_init(void)
{
    printk("PIG007:Hello world!\n");
    return 0;
}
static void __exit hello_exit(void)
{
    printk("PIG007:Bye,world\n");
}
module_init(hello_init);
module_exit(hello_exit);
//注释头
 
//由于基本都是用下载的内核编译,所以这里的头文件直接放到正常的编译器中可能找不到对应的头文件。
//在自己下载的编译好的内核中自己找对应的,然后Makefile中来设置内核源码路径
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("Dual BSD/GPL");
static int __init hello_init(void)
{
    printk("PIG007:Hello world!\n");
    return 0;
}
static void __exit hello_exit(void)
{
    printk("PIG007:Bye,world\n");
}
module_init(hello_init);
module_exit(hello_exit);
 
 
//注释头
 
MODULE_LICENSE("Dual BSD/GPL");
//注释头
 
MODULE_LICENSE("Dual BSD/GPL");
grep "MODULE_LICENSE" -B 27 /usr/src/linux-headers-`uname -r`/include/linux/module.h
grep "MODULE_LICENSE" -B 27 /usr/src/linux-headers-`uname -r`/include/linux/module.h
 
static int __init hello_init(void)
static int __init hello_init(void)
module_init(hello_init);
module_init(hello_init);
static void __exit hello_exit(void)
static void __exit hello_exit(void)
module_exit(hello_exit);
module_exit(hello_exit);
 
///linux/cdev.h   kernel 5.14.8
 
struct cdev {
    struct kobject kobj;    // 内嵌的kobject对象
    struct module *owner;    // 所属模块
    const struct file_operations *ops;    // 文件操作结构体,用来进行交互
    struct list_head list;
    dev_t dev;                // 设备号
    unsigned int count;
} __randomize_layout;
///linux/cdev.h   kernel 5.14.8
 
struct cdev {
    struct kobject kobj;    // 内嵌的kobject对象
    struct module *owner;    // 所属模块
    const struct file_operations *ops;    // 文件操作结构体,用来进行交互
    struct list_head list;
    dev_t dev;                // 设备号
    unsigned int count;
} __randomize_layout;
// 设备结构体
struct xxx_dev_t {
    struct cdev cdev;
} xxx_dev;
 
// 设备驱动模块加载函数
static int __init xxx_init(void)
{
    // 初始化cdev
    cdev_init(&xxx_dev.cdev, &xxx_fops);
    xxx_dev.cdev.owner = THIS_MODULE;
 
    // 获取字符设备号
    if (xxx_major) {
         //register_chrdev_region用于已知起始设备的设备号的情况
        register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
    } else {
        alloc_chrdev_region(&xxx_dev_no, 1, DEV_NAME);
    }
    //申请设备号常用alloc_chrdev_region,表动态申请设备号,起始设备设备号位置。
 
    // 注册设备
    ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1);
 
}
// 设备驱动模块卸载函数
static void __exit xxx_exit(void)
{
    // 释放占用的设备号
    unregister_chrdev_region(xxx_dev_no, 1);
    cdev_del(&xxx_dev.cdev);
}
// 设备结构体
struct xxx_dev_t {
    struct cdev cdev;
} xxx_dev;
 
// 设备驱动模块加载函数
static int __init xxx_init(void)
{
    // 初始化cdev
    cdev_init(&xxx_dev.cdev, &xxx_fops);
    xxx_dev.cdev.owner = THIS_MODULE;
 
    // 获取字符设备号
    if (xxx_major) {
         //register_chrdev_region用于已知起始设备的设备号的情况
        register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
    } else {
        alloc_chrdev_region(&xxx_dev_no, 1, DEV_NAME);
    }
    //申请设备号常用alloc_chrdev_region,表动态申请设备号,起始设备设备号位置。
 
    // 注册设备
    ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1);
 
}
// 设备驱动模块卸载函数
static void __exit xxx_exit(void)
{
    // 释放占用的设备号
    unregister_chrdev_region(xxx_dev_no, 1);
    cdev_del(&xxx_dev.cdev);
}
 
static int __init xxx_init(void)
{
    buffer_var=kmalloc(100,GFP_DMA);
    printk(KERN_INFO "[i] Module xxx registered");
    if (alloc_chrdev_region(&dev_no, 0, 1, "xxx") < 0)
    {
        return -1;
    }
    if ((devClass = class_create(THIS_MODULE, "chardrv")) == NULL)
    {
        unregister_chrdev_region(dev_no, 1);
        return -1;
    }
    if (device_create(devClass, NULL, dev_no, NULL, "xxx") == NULL)
    {
        printk(KERN_INFO "[i] Module xxx error");
        class_destroy(devClass);
        unregister_chrdev_region(dev_no, 1);
        return -1;
    }
    cdev_init(&cdev, &xxx_fops);
    if (cdev_add(&cdev, dev_no, 1) == -1)
    {
        device_destroy(devClass, dev_no);
        class_destroy(devClass);
        unregister_chrdev_region(dev_no, 1);
        return -1;
    }
 
    printk(KERN_INFO "[i] <Major, Minor>: <%d, %d>\n", MAJOR(dev_no), MINOR(dev_no));
    return 0;
 
}
static int __init xxx_init(void)
{
    buffer_var=kmalloc(100,GFP_DMA);
    printk(KERN_INFO "[i] Module xxx registered");
    if (alloc_chrdev_region(&dev_no, 0, 1, "xxx") < 0)
    {
        return -1;
    }
    if ((devClass = class_create(THIS_MODULE, "chardrv")) == NULL)
    {
        unregister_chrdev_region(dev_no, 1);
        return -1;
    }
    if (device_create(devClass, NULL, dev_no, NULL, "xxx") == NULL)
    {
        printk(KERN_INFO "[i] Module xxx error");
        class_destroy(devClass);
        unregister_chrdev_region(dev_no, 1);
        return -1;
    }
    cdev_init(&cdev, &xxx_fops);
    if (cdev_add(&cdev, dev_no, 1) == -1)
    {
        device_destroy(devClass, dev_no);
        class_destroy(devClass);
        unregister_chrdev_region(dev_no, 1);
        return -1;
    }
 
    printk(KERN_INFO "[i] <Major, Minor>: <%d, %d>\n", MAJOR(dev_no), MINOR(dev_no));
    return 0;
 
}
///linux/fs.h     kernel 5.14.8
 
struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
    ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
    int (*iopoll)(struct kiocb *kiocb, bool spin);
    int (*iterate) (struct file *, struct dir_context *);
    int (*iterate_shared) (struct file *, struct dir_context *);
    __poll_t (*poll) (struct file *, struct poll_table_struct *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    unsigned long mmap_supported_flags;
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, loff_t, loff_t, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **, void **);
    long (*fallocate)(struct file *file, int mode, loff_t offset,
              loff_t len);
    void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
    unsigned (*mmap_capabilities)(struct file *);
#endif
    ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
            loff_t, size_t, unsigned int);
    loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
                   struct file *file_out, loff_t pos_out,
                   loff_t len, unsigned int remap_flags);
    int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;
///linux/fs.h     kernel 5.14.8
 
struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
    ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
    int (*iopoll)(struct kiocb *kiocb, bool spin);
    int (*iterate) (struct file *, struct dir_context *);
    int (*iterate_shared) (struct file *, struct dir_context *);
    __poll_t (*poll) (struct file *, struct poll_table_struct *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    unsigned long mmap_supported_flags;
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, loff_t, loff_t, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **, void **);
    long (*fallocate)(struct file *file, int mode, loff_t offset,
              loff_t len);
    void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
    unsigned (*mmap_capabilities)(struct file *);
#endif
    ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
            loff_t, size_t, unsigned int);
    loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
                   struct file *file_out, loff_t pos_out,
                   loff_t len, unsigned int remap_flags);

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-11-2 12:06 被PIG-007编辑 ,原因:
收藏
免费 12
支持
分享
打赏 + 5.00雪花
打赏次数 1 雪花 + 5.00
 
赞赏  菱志漪   +5.00 2022/06/01 谢谢大佬的demo
最新回复 (8)
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
 有兴趣交换下联系方式么?  一起学习yhnu1991
2021-11-2 16:08
0
雪    币: 4600
活跃值: (6846)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
不收藏你收藏谁
2021-11-3 10:51
0
雪    币: 5633
活跃值: (4789)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
4
github_yhnu 有兴趣交换下联系方式么? 一起学习yhnu1991
发送好友申请啦
2021-11-8 15:32
0
雪    币: 3
活跃值: (80)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
2021-11-12 09:36
0
雪    币: 138
活跃值: (557)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
大佬可以发一下联系方式吗?我最近想学习下内核。
2022-6-1 16:28
0
雪    币: 2006
活跃值: (550)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
编译内核简单啊,主要是一些开源代码移植解报错这有难度
2022-6-1 18:48
0
雪    币: 5633
活跃值: (4789)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
8
菱志漪 大佬可以发一下联系方式吗?我最近想学习下内核。
发送消息啦
2022-6-1 23:50
0
雪    币: 275
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
大佬,有群吗,能加一下吗
2023-1-20 22:44
0
游客
登录 | 注册 方可回帖
返回
//