上一个文章结束之后应该具备了一点基础的前置知识,接下来只需要尽情的敲代码就可以了
那么我们先来看一眼Kernel源码
编译后的目录有些乱,无视一下
简单介绍一下各个目录(摘自Linux内核目录结构介绍)
序号 | 第一级目录 | 第二级目录和文件 |
---|---|---|
1 | arch | 这个文件夹包含了一个Kconfig文件,它用于设置这个目录里的源代码编译所需的一系列设定。每个支持的处理器架构都在它相应的文件夹中,如arm64、arm32、x86、mips等。/boot:内核需要的特定平台代码 /boot/dts:设备树文件 /lib:通用函数在特定体系结构的文件 /math-emu:模拟FPU的代码,在ARM中,使用/math-xxx代替 /mm:特定体系结构的内存管理文件 /include:特定体系的头文件 |
2 | block | 此文件夹包含块设备驱动程序的代码,该目录用于实现块设备的基本框架和块设备的I/O调度算法。块设备是以数据块方式接收和发送的数据的设备。数据块都是一块一块的数据而不是持续的数据流。 |
3 | crypto | 这个文件夹包含许多加密算法的源代码。例如,“sha1_generic.c”这个文件包含了SHA1加密算法的代码。存放加密、压缩、CRC校验等算法相关代码 |
4 | Documentation | 存放相关说明文档,很多实用文档,包括驱动编写等 |
5 | drivers | 存放 Linux 内核设备驱动程序源码。驱动源码在 Linux 内核源码中站了很大比例,常见外设几乎都有可参考源码,对驱动开发而言,该目录非常重要。该目录包含众多驱动,目录按照设备类别进行分类,如char、block、input、i2c、spi、pci、usb等 |
6 | firmware | 保存用于驱动第三方设备的固件。 |
7 | fs | 这是文件系统的文件夹。理解和使用的文件系统所需要的所有的代码就在这里。在这个文件夹里,每种文件系统都有自己的文件夹。例如,ext4文件系统的代码在ext4文件夹内。在fs文件夹内,开发者会看到一些不在文件夹中的文件。这些文件用来控制整个文件系统。例如,mount.h中会包含挂载文件系统的代码。文件系统是以结构化的方式来存储和管理的存储设备上的文件和目录。每个文件系统都有自己的优点和缺点。这是由文件系统的设计决定的。 |
8 | include | 存放内核所需、与平台无关的头文件,与平台相关的头文件已经被移动到 arch 平台的include 目录,如 ARM 的头文件目录<arch/arm/include/asm/> |
9 | init | 包含内核初始化代码,init文件夹包含了内核启动的处理代码(INITiation)。main.c是内核的核心文件,这是用来衔接所有的其他文件的源代码主文件。 |
10 | ipc | 存放进程间通信代码,此文件夹中的代码是作为内核与进程之间的通信层。内核控制着硬件,因此程序只能请求内核来执行任务。假设用户有一个打开DVD托盘的程序。程序不直接打开托盘,该程序通知内核,然后,内核给硬件发送一个信号去打开托盘。 |
11 | kernel | 这个文件夹中的代码控制内核本身,在该文件夹下有个"power"文件夹,这里的代码可以使计算机重新启动、关机和挂起。 |
12 | lib | 这个文件夹包含了内核需要引用的一系列内核库文件代码。 |
13 | mm | mm文件夹中包含了内存管理代码。内存并不是任意存储在RAM芯片上的。相反,内核小心地将数据放在RAM芯片上。内核不会覆盖任何正在使用或保存重要数据的内存区域。 |
14 | net | net文件夹中包含了网络协议代码。这包括IPv6、AppleTalk、以太网、WiFi、蓝牙等的代码,此外处理网桥和DNS解析的代码也在net目录。 |
15 | samples | 存放提供的一些内核编程范例,如kfifo;后者相关用户态编程范例,如hidraw。此文件夹包含了程序示例和正在编写中的模块代码。假设一个新的模块引入了一个想要的有用功能,但没有程序员说它已经可以正常运行在内核上。那么,这些模块就会移到这里。这给了新内核程序员一个机会通过这个文件夹来获得帮助,或者选择一个他们想要协助开发的模块。 |
16 | scripts | 这个文件夹有内核编译所需的脚本。最好不要改变这个文件夹内的任何东西。否则,您可能无法配置或编译内核。 |
17 | security | 这个文件夹是有关内核安全的代码。它对计算机免于受到病毒和黑客的侵害很重要。否则,Linux系统可能会遭到损坏。 |
18 | sound | 这个文件夹中包含了声卡驱动,存放声音系统架构相关代码和具体声卡的设备驱动程序 |
19 | tools | 编译过程中一些主机必要工具,这个文件夹中包含了和内核交互的工具。 |
20 | usr | 早期用户空间代码(所谓的initramfs) |
21 | virt | 内核虚拟机KVM |
对于大部分的目录有一个简单的理解将可以,我们在这里主要关注的是security和fs
security就不用说了,见名知意,是这次的主题
fs是文件系统的目录,因为需求是基于文件扩展属性的安全控制,需要用到其中的xattr(当然引入的话还是要从include引入)
关于LSM和VFS的话可以看看下面的文章
看完之后想必你已经知道代码要写在哪里了,就是security目录内
那么让我们看一下这个目录里都有些什么
虽然前面已经介绍过了但是这里还是再唠叨一遍
Makefile
: 用于编译该目录下的所有代码
Kconfig
: 用于配置内核编译选项
security.c
: 包含核心安全框架的实现。
再就是一些目录,这些目录大部分都是系统自带的安全模块,比如selinux
,apparmor
,yama
,landlock
,lockdown
等,编程水平进步最快的办法就是抄别人的代码 学习别人的代码,我们可以先参考一些功能简单,代码量少的安全模块,观察他们的代码是怎么实现的,有那些共同点
比如我们通过查看hooks、yama等文件发现有很多代码都是相同的
那我来摘取一部分并添加一点点讲解注释
#include <linux/lsm_hooks.h>
#include <uapi/linux/lsm.h>
// 定义一个静态的安全钩子列表,名为 bpf_lsm_hooks,并加上 __ro_after_init 标签。
// 该标签表示此内存段在初始化后将变为只读。
static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = {
//*****************************************//
// 这里会放置具体的安全钩子函数
};
// 定义一个常量结构体 lsm_id,名为 bpf_lsmid,用于标识 BPF LSM。
static const struct lsm_id bpf_lsmid = {
.name = "bpf", // LSM 的名称为 "bpf"
.id = LSM_ID_BPF, // LSM 的 ID
};
// 定义一个初始化函数,用于初始化 BPF LSM。
static int __init bpf_lsm_init(void)
{
// 将 BPF 的安全钩子添加到 LSM 框架中。
security_add_hooks(bpf_lsm_hooks, ARRAY_SIZE(bpf_lsm_hooks),
&bpf_lsmid);
// 打印信息到内核日志,表示 BPF LSM 已激活。
pr_info("LSM support for eBPF active\n");
return 0; // 返回 0 表示成功
}
// 使用 DEFINE_LSM 宏定义一个 LSM 结构体,名为 bpf。
DEFINE_LSM(bpf) = {
.name = "bpf", // LSM 的名称为 "bpf"
.init = bpf_lsm_init, // 初始化函数为 bpf_lsm_init
/****************/
};
这样一个简易安全模块代码就写好了,当然如果你善于观察的话会发现这样肯定还是不够的
如果你想把这些代码编译进内核还需要一些别的操作
在security目录下创建一个目录,名称取你模块的名字,目录内包含你的源代码和Makefile文件
Makefile文件内容如下:(XuanWu是我的模块名字)
obj-$(CONFIG_SECURITY_XUANWU) += XuanWu.o
在编译内核时根据配置选项(CONFIG_SECURITY_XUANWU)决定是否包含特定的对象文件,如果是y的话则将 XuanWu.o 添加到最终要编译的对象文件列表中。
那问题来了,配置选项(CONFIG_SECURITY_XUANWU)在哪里,还是学习别人的代码,可以使用vsc或者linux的文件内容查找功能,看看别人的配置选项在哪里,比如我们查看yama_lsm的Makefile文件,并搜索yama的配置选项,你会发现两个文件:
security/Makefile
security/security.c
虽然现在还不知道这两个文件是干什么用的,但是很明显,配置选项应写在这里,按照yama的格式写上自己的配置选项即可
可选:创建Kconfig
Kconfig文件是用来配置菜单选项的,只有加上这个在make menuconfig
的时候才能看见自己的模块选项(当然不加也不影响就是了)
config SECURITY_XUANWU # 声明配置选项-这个值可以在meunconfig中看见
bool "XuanWu support" # 表示该配置选项是bool类型(y/n)
depends on SECURITY # 定义了配置选项的依赖关系
default y # 设置默认值为y(启用)
help # 关于配置选项的说明文本
LSM XuanWu Module
Kconfig 需要在根目录的security/Kconfig里添加相应代码
那么一个基础的LSM模块就到此结束了,接下来只需要编译安装即可,回到源码根目录执行make menuconfig
你就可以在security options选项中看到自己的模块
security options
配置好的选项(因为默认值是y,所以默认就是启用状态)
但是只是启用还不够,还要在rdered list of enabled LSMs
里添加你启用的安全模块
这样就大功告成了,接下来编译安装就可以了
注意事项:
- 编译好的内核有可能无法安装/内核模块无法启用等问题
make menuconfig
--> Cryptographic API --> Certificates for signature checking --> Additional X.509 keys for default system keryring
- 编译耗时
前面说过漫长的只有第一次编译的时候,在你内核编译完成之后,你不需要执行make clear
来清除编译产生的文件后再重新编译。
在你对内核做出修改的后保存,直接make -j 32
就可以了,它能识别到你修改了的文件自动编译(要不然也不能叫自动化建构软件了)
- 其实只需要
make install
就可以了
前面我说更换内核只需要一条命令,就是这个,虽然LSM说是Linux Security Modules,但是并不在Linux的模块范围里,是随着内核一起安装/加载的