LSM内核模块 Demo 保姆级教程

LSM 内核模块实现 Demo

前言

 最近为了完成作业,被折磨的不轻。版本的不兼容,各种的报错经历了太多。在此记录,希望对大家有所帮助。

 本人在写这篇文章的时候,重新安装虚拟机,从头开始的,所以,如果能坚持到最后,大概率会成功的。祝你们好运。(当然,如果这篇文章还没有过太久的话)

准备工作

 经过了大量测试,红帽公司的系统好像并不是很好编译 busybox,本人使用 Fedora 系统进行编译时,总是显示缺少依赖,而网上也找不到相应的库。转而使用教程较多的 Ubuntu 系统。为了本身系统的安全,当然使用虚拟机。

 系统参数:Ubuntu 20.04,处理器 2 CPU * 1 内核/CPU,内存 4G,其他使用默认设置,然后直接创建就可以了。

linux 内核源码版本:linux-4.20.17。内核版本不能选用太低版本,因为当前 binutils 工具不是前向兼容的,之前有些类型现在版本的工具是无法识别的。比如 linux-4.14.0 版本,部分数据类型就已经不受支持了。

busybox:一般用最新版本即可,本人使用 busybox-1.35.0,是当前最新。

 上面简单介绍一下各种工具或系统的版本,下面开始具体的实施工作。

 虚拟机安装完成后,首先进行换源(Software & Updates软件),然后执行apt update && apt upgrade升级各种软件包(或者他会自动弹出一个Software Updater的窗口,直接 install),避免之后在安装工具时出现缺少依赖包或者依赖包安装困难的问题。但是,现在不要着急安装gcc,gcc的安装稍后再说,现在只要先升级即可。

安装 gcc

 因为 Ubuntu 默认安装的是 gcc-9,所以我们需要指定版本进行安装,否则在内核编译过程中会出现版本不兼容的问题。

​ 首先apt remove gcc-9卸载高版本。

 使用安装命令apt install gcc-8进行安装4。但是安装之后执行 gcc 命令可能还会提示你安装 gcc,这是因为我们安装了 gcc-8,在 /usr/bin 目录下只有 gcc-8 而没有 gcc,所以 shell 找不到命令。

 为此,执行命令ln /usr/bin/gcc-8 /usr/bin/gcc。我们就直接将 gcc-8 链接为 gcc 就可以了。

Qemu

​ 当然,我们不可能直接在系统中运行编译好的内核,还是需要在虚拟机中运行的。所以,我们安装 Qemu 轻量级虚拟机。

​ 命令为sudo apt install qemu-system-x86

内核下载

​ 下载 linux 内核,从中选择相应的版本。笔者使用的是:linux-4.20.17。

使用 busybox 制作根文件系统

​ 在系统启动的时候,需要先加载一段内存镜像,以便于找到系统真正的入口。在我们调试内核的时候,更多的使用 busybox。他可以帮助我们生成一个根文件系统,我们将其打包到一个 cpio 镜像文件之后就可以重复使用了。

​ 首先,我们安装所需要的依赖4

sudo apt-get install bison
sudo apt-get install flex
sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc

​ 在安装的过程中,可以先下载 busybox5,从中选择最新版本,或者使用笔者使用的版本:busybox-1.35.0。但是在依赖安装完成后才能进行编译工作。

​ 将下载好的 busybox 解压然后放到解压好的 linux 目录下并改名为 busybox。

Qemu 运行内核

​ 为了保险起见,我们先编译一下内核试一试,能不能运行。下面借用1中所给出的步骤(略有改动):

  1. 进入到下载好的 Linux 内核文件中,将配置文件中的选项清空
make allnoconfig
  1. make menuconfig 进入配置文件菜单选项,将以下选择勾选。注意,要吧窗口调大一点,要不然他会提示窗口太小无法运行的报错。
64-bit kernel ---> yes
General setup ---> Initial RAM filesystem and RAM disk (initramfs/initrd) support ---> yes
General setup ---> Configure standard kernel features ---> Enable support for printk ---> yes
Executable file formats / Emulations ---> Kernel support for ELF binaries ---> yes
Executable file formats / Emulations ---> Kernel support for scripts starting with #! ---> yes
Device Drivers ---> Generic Driver Options ---> Maintain a devtmpfs filesystem to mount at /dev ---> yes
Device Drivers ---> Generic Driver Options ---> Automount devtmpfs at /dev, after the kernel mounted the rootfs ---> yes
Device Drivers ---> Character devices ---> Enable TTY ---> yes
Device Drivers ---> Character devices ---> Serial drivers ---> 8250/16550 and compatible serial support ---> yes
Device Drivers ---> Character devices ---> Serial drivers ---> Console on 8250/16550 and compatible serial port ---> yes
File systems ---> Pseudo filesystems ---> /proc file system support ---> yes
File systems ---> Pseudo filesystems ---> sysfs file system support ---> yes
  1. 编译内核,只要不报错就可以。但时间可能有点长。
sudo make -j8
  1. 进入busybox文件夹,使用默认的配置文件
make defconfig
  1. make menuconfig 编辑配置文件,一定开启静态编译
Settings ---> Build BusyBox as a static binary (no shared libs) ---> yes
  1. 编译busybox,老样子,无视警告,只要不报错就可以。
time make -j 8
  1. 安装 busybox, install 之后出现 _install 文件夹
make install
  1. cd _install 创建一些文件夹以及文件
mkdir -p proc sys dev etc etc/init.d lib tmp
ln -sf linuxrc init

cat > etc/init.d/rcS <<EOF
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
/sbin/mdev -s
ifconfig lo up
EOF

chmod +x etc/init.d/rcS

cat > etc/inittab <<EOF
# /etc/inittab
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
EOF
  1. 使用 cpio 生成根文件系统
find . -print0 | cpio --null -ov --format=newc   | gzip -9 > ../initramfs.cpio.gz

​ 到 busybox 文件夹中,解压刚才生成的initramfs.cpio.gzinitramfs.cpio

  1. 使用 qemu 启动编译好的内核

qemu-system-x86_64 -kernel ~/Downloads/linux-4.20.17/arch/x86/boot/bzImage -initrd ~/Downloads/linux-4.20.17/busybox/initramfs.cpio

​ 这里的路径要视个人的文件路径而定,不要直接复制,先看清楚。确定路径后执行命令,就可以看到一个不带图形界面的 shell 了。

​ 如果到这里还没有问题的话,说明你的准备工作已经完成了,恭喜!

LSM 内核模块实现

模块编写

​ 不知道 LSM 原理的可以先补充一下前置知识(虽然我也不知道,狗头)。但总体来说,思想就是利用系统的 hook。我们编写钩子程序,然后添加到一个钩子列表中并加入系统钩子。注册刚刚已经加入的钩子函数,重新编译内核程序就可以使用了。

​ 下面我沿用了2中的demo代码,但可能由于版本的不兼容,原文中有一些函数无法使用,就进行了小型的修改。在此感谢大佬无私提供的代码。

// demo_lsm.c
#include 
#include 
#include 
#include 

static unsigned long long count = 0;
 
int demo_task_alloc(struct task_struct *task,unsigned long clone_flags) // 2. implement relevant function 
{
    printk("[+demo] call task_create(). count=%llu\n", ++count);    
    return 0;
}
int demo_inode_create (struct inode *dir, struct dentry *dentry, umode_t mode)
{
	printk ("[+demo] call [inode_create] by pid: %d\n", get_current()->pid) ;
	return 0 ;
}
int demo_file_permission(struct file *file, int mask){
	int max_use = 10;
	if(file->f_inode->__i_nlink > max_use){
		printk("[+demo] permisson deny!\n");
		return 1;
	}
	return 0;
}

static struct security_hook_list demo_hooks[] __lsm_ro_after_init = {
    LSM_HOOK_INIT(task_alloc,demo_task_alloc),
    LSM_HOOK_INIT(inode_create,demo_inode_create), //3. add to security_hook_list
    LSM_HOOK_INIT(file_permission,demo_file_permission),
 
};
void __init demo_add_hooks(void)
{
    pr_info("Demo: becoming mindful.\n");        //print relevant mesg, cat by dmesg | grep demo  
    security_add_hooks(demo_hooks, ARRAY_SIZE(demo_hooks),"demo");   //add security model function
}
 
static int __init demo_init(void){
    demo_add_hooks();
    return 0;
}
 
__initcall(demo_init); //4. register this hook function

内核编译相关文件

  1. 在内核源码的security文件夹下面建立demo文件夹,该文件夹内有demo_lsm.c 、Makefile 、 Kconfig三个文件

Makefile如下

obj-$(CONFIG_SECURITY_DEMO) := demo.o
demo-y := demo_lsm.o

Kconfig如下

config SECURITY_DEMO
	bool "Demo support"
	depends on SECURITY
	default n
	help
		introduction of demo LSM
  1. 然后修改security文件夹下面的Makefile 、 Kconfig文件

​ 按照 selinux 添加自己的 LSM 配置即可。

Makefile如下

# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the kernel security code
#

obj-$(CONFIG_KEYS)			+= keys/
subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
subdir-$(CONFIG_SECURITY_SMACK)		+= smack
subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
subdir-$(CONFIG_SECURITY_YAMA)		+= yama
# -------- demo LSM -------- #
# -------- 在这里添加 -------- #
subdir-$(CONFIG_SECURITY_DEMO)	+= demo
subdir-$(CONFIG_SECURITY_LOADPIN)	+= loadpin

# always enable default capabilities
obj-y					+= commoncap.o
obj-$(CONFIG_MMU)			+= min_addr.o

# Object file lists
obj-$(CONFIG_SECURITY)			+= security.o
obj-$(CONFIG_SECURITYFS)		+= inode.o
obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/
obj-$(CONFIG_SECURITY_SMACK)		+= smack/
obj-$(CONFIG_AUDIT)			+= lsm_audit.o
obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/
obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/
obj-$(CONFIG_SECURITY_YAMA)		+= yama/
# -------- demo LSM ------- #
# -------- 在这里添加 -------- #
obj-$(CONFIG_SECURITY_DEMO)		+= demo/
obj-$(CONFIG_SECURITY_LOADPIN)		+= loadpin/
obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o

# Object integrity file lists
subdir-$(CONFIG_INTEGRITY)		+= integrity
obj-$(CONFIG_INTEGRITY)			+= integrity/

Kconfig文件拉到最下面,找到对应位置添加:

source security/demo/Kconfig

default DEFAULT_SECURITY_DEMO if SECURITY_DEMO

config DEFAULT_SECURITY_DEMO
		bool "Demo" if SECURITY_DEMO=y
	
default "Demo" if DEFAULT_SECURITY_DEMO
  1. 在内核源码目录下执行以下命令
make defconfig
make menuconfig

​ 选择自己的 LSM 作为安全模块

Security options ---> Demo support --> yes
Security options ---> Default security module ---> Demo ---> yes
  1. 编译内核
# 安装依赖库安装库
sudo apt install libelf-dev
sudo make -j8

​ 如果这里报错了,首先查看是否 gcc 版本正确。可以通过以下命令检查:

apt list | grep gcc
ls /usr/bin/ | grep gcc

​ 如果有 gcc-9,直接apt remove gcc-9卸载,然后ln /usr/bin/gcc-8 /usr/bin/gcc重新建立硬链接。

​ 整个内核编译的过程十分缓慢,需要耐心等候。可能是因为,在之前编译内核时使用了系统默认的安全模块,而这次需要重新对自定义安全模块编译并加载,所以导致了编译效率的降低。

​ 如果编译成功了会显示:Kernel: arch/x86/boot/bzImage is ready,否则就说明你的某一个环节出错了。

运行效果

​ 最后如果你已经到这里,说明你已经成功了,恭喜你!赶紧来运行一下编译好的内核看看吧。上面已经有了 qemu 启动内核的命令,这里再次给出方便使用。

qemu-system-x86_64 -kernel ~/Downloads/linux-4.20.17/arch/x86/boot/bzImage -initrd ~/Downloads/linux-4.20.17/busybox/initramfs.cpio

​ 加载了我们的 demo LSM 后,qemu 在启动系统时,你就可以看到屏幕上显示除了很多的:[+demo] call ...,这就说明我们的安全模块已经成功的编译,挂载到内核程序中了。

​ 我们回车进入终端,然后输入ls。发现输出了permission deny的字样,也就是我们的 LSM 阻挡了我们此次的文件访问。

后记

​ 如果你单纯调试这个版本的内核,那么 busybox 生成的内存镜像是不需要再修改的了,直接使用就可以,你甚至可以为了方便将生成的 cpio 文件放置到其他路径。只有修改模块的时候编译内核即可。但如果更换内核版本的话,还是需要将 busybox 放入新的 linux 内核目录中进行编译安装的。

最后的最后

​ 祝大家学业、工作顺利,永不报错。

[1]. huzai9527. “裁剪Linux内核,用qemu进行调试”. https://blog.csdn.net/huzai9527/article/details/116769974

[2]. huzai9527. “LSM内核模块实现demo”. https://blog.csdn.net/huzai9527/article/details/119870485

[3]. 0xJDchen. “学习LSM(Linux security module)之二:编写并运行一个简单的demo”. https://www.cnblogs.com/0xJDchen/p/6040446.html

[4]. yqf. “ubuntu 20 下搭建linux 内核+BusyBox+Qemu开发环境”. https://zhuanlan.zhihu.com/p/383583821#:~:text=%E5%AE%89%E8%A3%85%E7%BC%96%E8%AF%91busybox%20%E7%9A%84%E4%BE%9D%E8%B5%96%20sudo%20apt-get%20install%20libncurses5-dev%20libncursesw5-dev%20%E8%BF%9B%E5%85%A5,make%20install%20-j4%20%E5%9C%A8%E6%A0%B9%E7%9B%AE%E5%BD%95%E4%B8%8B%E7%94%9F%E6%88%90_install%20%E6%96%87%E4%BB%B6%E5%A4%B9%20%E9%87%8C%E9%9D%A2%E5%8C%85%E5%90%AB%E5%90%AF%E5%8A%A8linu%20%E5%86%85%E6%A0%B8%E6%96%87%E4%BB%B6%20%E5%85%B6%E4%B8%ADqemu_rootfs.img%E6%98%AF%E6%96%87%E4%BB%B6%E5%90%8D%EF%BC%8C500m%E6%98%AF%E7%A3%81%E7%9B%98%E5%A4%A7%E5%B0%8F%EF%BC%8C%E6%A0%B9%E6%8D%AE%E9%9C%80%E8%A6%81%E4%BF%AE%E6%94%B9%E3%80%82

[5]. TrueDei. “使用Busybox制作根文件系统”. https://cloud.tencent.com/developer/article/1882513

[6]. 孤星入命孑然一身. “编译错误 error New address family defined, please update secclass_map.解决”. https://blog.csdn.net/zhangpengfei991023/article/details/109672491

你可能感兴趣的:(杂,ubuntu,linux,LSM)