其他
Kernel从0开始
本文为看雪论坛优秀文章
看雪论坛作者ID:PIG-007
一
简介
二
例子编写
//注释头
//由于基本都是用下载的内核编译,所以这里的头文件直接放到正常的编译器中可能找不到对应的头文件。
//在自己下载的编译好的内核中自己找对应的,然后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);
1、头文件简介
2、模块许可证
//注释头
MODULE_LICENSE("Dual BSD/GPL");
grep "MODULE_LICENSE" -B 27 /usr/src/linux-headers-`uname -r`/include/linux/module.h
3、模块加载卸载
加载
static int __init hello_init(void)
module_init(hello_init);
卸载
static void __exit hello_exit(void)
module_exit(hello_exit);
三
题目编写
1、字符型设备驱动
(1)安装套路
///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);
}
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;
}
(2)交互套路
///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;
// 读设备
ssize_t xxx_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
...
copy_to_user(buf, ..., ...); // 内核空间到用户空间缓冲区的复制
...
}
// 写设备
ssize_t xxx_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
...
copy_from_user(..., buf, ...); // 用户空间缓冲区到内核空间的复制
...
}
// ioctl函数命令控制
long xxx_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
case XXX_CMD1:
...
break;
case XXX_CMD2:
...
break;
default:
// 不支持的命令
return -ENOTTY;
}
return 0;
}
static struct file_operations xxx_fops =
{
.owner = THIS_MODULE,
// .open = xxx_open,
// .release = xxx_close,
.write = xxx_write,
.read = xxxx_read
};
(3)具体的题目
①代码和简单的解析
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
//正常的设置了dev_t和cdev,但是这里使用的class这个模板来创建设备驱动
static dev_t first; // Global variable for the first device number
static struct cdev c_dev; // Global variable for the character device structure
static struct class *cl; // Global variable for the device class
static char *buffer_var;
//打开关闭设备的消息提示函数
static int vuln_open(struct inode *i, struct file *f)
{
printk(KERN_INFO "[i] Module vuln: open()\n");
return 0;
}
static int vuln_close(struct inode *i, struct file *f)
{
printk(KERN_INFO "[i] Module vuln: close()\n");
return 0;
}
//从buffer_var中读取数据
static ssize_t vuln_read(struct file *f, char __user *buf, size_t len, loff_t *off)
{
if(strlen(buffer_var)>0) {
printk(KERN_INFO "[i] Module vuln read: %s\n", buffer_var);
kfree(buffer_var);
buffer_var=kmalloc(100,GFP_DMA);
return 0;
} else {
return 1;
}
}
//向buffer中写入数据,然后拷贝给buffer_var,这里就是漏洞存在点。
//由于len和buf都是我们可以控制的,而buffer是栈上的数据,长度为100。
//所以我们可以通过len和buf,将数据复制给buffer从而进行栈溢出。
static ssize_t vuln_write(struct file *f, const char __user *buf,size_t len, loff_t *off)
{
char buffer[100]={0};
if (_copy_from_user(buffer, buf, len))
return -EFAULT;
buffer[len-1]='\0';
printk("[i] Module vuln write: %s\n", buffer);
strncpy(buffer_var,buffer,len);
return len;
}
//file_operations结构体初始化
static struct file_operations pugs_fops =
{
.owner = THIS_MODULE,
.open = vuln_open,
.release = vuln_close,
.write = vuln_write,
.read = vuln_read
};
//驱动设备加载函数
static int __init vuln_init(void) /* Constructor */
{
buffer_var=kmalloc(100,GFP_DMA);
printk(KERN_INFO "[i] Module vuln registered");
if (alloc_chrdev_region(&first, 0, 1, "vuln") < 0)
{
return -1;
}
if ((cl = class_create(THIS_MODULE, "chardrv")) == NULL)
{
unregister_chrdev_region(first, 1);
return -1;
}
if (device_create(cl, NULL, first, NULL, "vuln") == NULL)
{
printk(KERN_INFO "[i] Module vuln error");
class_destroy(cl);
unregister_chrdev_region(first, 1);
return -1;
}
cdev_init(&c_dev, &pugs_fops);
if (cdev_add(&c_dev, first, 1) == -1)
{
device_destroy(cl, first);
class_destroy(cl);
unregister_chrdev_region(first, 1);
return -1;
}
printk(KERN_INFO "[i] <Major, Minor>: <%d, %d>\n", MAJOR(first), MINOR(first));
return 0;
}
//驱动设备卸载函数
static void __exit vuln_exit(void) /* Destructor */
{
unregister_chrdev_region(first, 3);
printk(KERN_INFO "Module vuln unregistered");
}
module_init(vuln_init);
module_exit(vuln_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("blackndoor");
MODULE_DESCRIPTION("Module vuln overflow");
②内核函数解析
printk
printk(日志级别 "消息文本");
#defineKERN_EMERG "<0>"/*紧急事件消息,系统崩溃之前提示,表示系统不可用*/
#defineKERN_ALERT "<1>"/*报告消息,表示必须立即采取措施*/
#defineKERN_CRIT "<2>"/*临界条件,通常涉及严重的硬件或软件操作失败*/
#define KERN_ERR "<3>"/*错误条件,驱动程序常用KERN_ERR来报告硬件的错误*/
#define KERN_WARNING "<4>"/*警告条件,对可能出现问题的情况进行警告*/
#define KERN_NOTICE "<5>"/*正常但又重要的条件,用于提醒。常用于与安全相关的消息*/
#define KERN_INFO "<6>"/*提示信息,如驱动程序启动时,打印硬件信息*/
#define KERN_DEBUG "<7>"/*调试级别的消息*/
kmalloc
static inline void *kmalloc(size_t size, gfp_t flags)
|– 进程上下文,不可以睡眠 GFP_ATOMIC
| |– 中断处理程序 GFP_ATOMIC
| |– 软中断 GFP_ATOMIC
| |– Tasklet GFP_ATOMIC
|– 用于DMA的内存,可以睡眠 GFP_DMA | GFP_KERNEL
|– 用于DMA的内存,不可以睡眠 GFP_DMA |GFP_ATOMIC
(https://www.cnblogs.com/sky-heaven/p/7390370.html)
kfree
copy_from_user
copy_from_user(void *to, const void __user *from, unsigned long n)
copy_to_user
copy_to_user(void __user *to, const void *from, unsigned long n)
注册函数
alloc_chrdev_region(&t_dev, 0, 1, "xxx");
unregister_chrdev_region(t_dev, 1);
xxx_class = class_create(THIS_MODULE, "xxx");
device_create(xxx_class, NULL, devno, NULL, "xxx");
cdev_init(&c_dev, &pugs_fops);
cdev_add(&c_dev, t_dev, 1)
2、globalmem虚拟设备驱动
看雪ID:PIG-007
https://bbs.pediy.com/user-home-904686.htm
# 往期推荐
2.通过CmRegisterCallback学习注册表监控与反注册表监控
3.BCTF2018-houseofatum-Writeup题解
球分享
球点赞
球在看
点击“阅读原文”,了解更多!