最近在复现一篇论文的时候发现linux内核的强大之处了,就简单的学习了下关于内核的一些知识,其中主要用到了Kasan这个检测UAF漏洞的工具(严格来说只能是内核编译插桩),本以为很简单,结果搞了好长时间,鉴于目前这方面资料很少,设置过程也缺乏,故将这个设置过程记录下来,以便交流(本文仅限学术交流,严谨转载)
内核编译过程主要参考了《鸟哥的Linux私房菜》,以及https://www.jianshu.com/p/e1f550ba164d
1 查看自己ubuntu的内核版本 uname -a(-r)
3 安装与自己内核头文件相应的的内核源码 sudo apt-get install linux-source-4.10.0(内核文件并不大,一般的也就在100M左右),下载完后会直接在/usr/src中看到相应的内核源码,运行 cd /usr/src 转到目录后解压源码:sudo tar -axvf linux-source-4.10.0.tar.bz2,转到相应的源码目录:cd /usr/src/linux-source-4.10.0
源码中一些基本的信息:
arch:与硬件平台有关的选项,大部分指的是CPU的类别,例如 x86,x86_64,Xen虚拟支持等;
block:与区块设备较相关的设置数据,区块数据通常指的是大量存储媒体,还包括ext3等文件系统的支持是否允许等;
crypto:内核所支持的加密技术,如 md5或des等;
Documention:与内核有关的帮助文档;
drivers:一些硬件的驱动程序,如显卡、网卡、PCI相关硬件;
firmware:一些旧式硬件的微指令(固件)数据;
fs:内核所支持的filesystems,如vfat、nfs等;
include:一些可让其他程序调用的头(header)定义数据;
init:一些内核初始化的定义功能,包括挂载与init程序的调用等;
ipc:定义Linux操作系统内各程序的通信;
kernel:定义内核的程序、内核状态、线程、程序的调度(schedule)、程序的信号(signal)等;
lib:一些函数库;
mm:与内存单元有关的各种信息,包括swap与虚拟内存等;
net:与网络有关的各项协议信息,还有防火墙(net/ipv4/netfilter/*)等;
security:包括SELinux等安全性设置;
sound:与音效有关的各项模块;
virt:与虚拟化及其有关的信息,目前内核支持的是KVM(Kernel base Virtual Machine)
4 修改设置:sudo make menuconfig(==make oldconfig ==make xconfig == make gconfig == make config ==sudo vim .config)
报错,其原因是缺少库,解决方法:
sudo apt-get install libncurses5-dev
之后再次运行 sudo make menuconfig,得到图形化界面,在kernel hacking/memory debugging中,可以使用空格进行选择,前面有M的表示模块
找到kasan,首先查看Help,查看相关的依赖选项:
注意,KASAN[=n]表示的是目前的状态时未打开,selects 则和symbol保持着一致性(symbol是啥,selects就是啥),而相关的依赖需要打开。要是找不到,可以在选好后保存,然后 sudo gedit .config进行修改,不过相关的依赖前面会加上CONFIG_的前缀。同时还要注意:enable,don‘t work with后面的选项。当然你也可以使用别人已经设置好的.config文件。
在Kasan的子目录里,进行模式选择,inline比outline要快一些,所以设置
另外还有SLUB,设置后,保存,退出,在源码的目录下,Ctrl+H,会发现多出了.config文件,这个就是配置文件了。
5 编译内核、模块
make -j 2 clean #先清除临时文件
make -j 2 bzImage #先编译内核,生成bzImage文件(中量时间)
sudo make -j 2 modules #再编译模块,要权限(大量时间)
制作出来的资料是被放置在/usr/src/linux-source-4.10.0/kernel 目录下,还没有被放到系统的相关路径中
##-j [N], --jobs[=N] 同时允许 N 个任务;无参数表明允许无限个任务
6 安装模块
上步编译内核、模块后,还没有将其放入相关的系统路径中,模块需要安装,内核也需要移动位置。模块会被安装在/lib/modules的目录下,这儿一定要注意,当两个版本一模一样时,模块放置的目录也一样,此时就会产生冲突,解决方法:
方法1:先将旧的模块目录更名,然后才安装内核模块到目标目录中去;
方法2:在 make menuconfig 时,将General setup内的Local version修改成新的名称;
我在我的机子上实验并没有遇到这个问题,所以直接运行:sudo make modules_install,安装之后,会在/lib/modules下生成一个新目录
7 移动内核
内核文件通常以 vmlinuz 为开头,后面跟上内核版本的文件格式。复制新内核文件到 /boot/ 下并改名 vmlinuz-3.10.107,保留旧内核文件。(命名时,我参考了我的模块生成的目录)
sudo cp /usr/src/linux-source-4.10.0/arch/x86/boot/bzImage /boot/vmlinuz-4.10.17
sudo cp /usr/src/linux-source-4.10.0/.config /boot/config-4.10.17
#给新内核文件添加 X 权限
sudo chmod a+x /boot/vmlinuz-4.10.17
sudo cp /usr/src/linux-source-4.10.0/System.map /boot/System.map-4.10.17
sudo gzip -c /usr/src/linux-source-4.10.0/Module.symvers > /boot/symvers-4.10.17(这个要最高权限)
##sudo passwd设置密码(第一次时)
##su
##获取最高权限
8 建立相对应的 Initial Ram Disk(initrd)
首先安装dracut:sudo apt install dracut
其次:sudo dracut -v /boot/initramfs-4.10.17.img 4.10.17
9 编辑开机选项(grub)
在/boot/grub下没有发现grub2,只能用grub
grub-mkconfig -o /boot/grub/grub.cfg
由此,就可以重新开机并选择新内核来启动系统啦!(启动时进入ubuntu界面按住shift,进入ubuntu高级选项,可以自由选择内核启动)
注意:在虚拟机上编译内核后重启会出现一个错误:
VBoxClient (seamless): failed to start. Stage: Setting guest IRQ filter mas Error: VERR_INTERNAL_ERROR
这个错误是关于VBOX的增强功能的,通过以下命令来解决:
参考:https://askubuntu.com/questions/985815/vboxclient-seamless-failed-to-start-stage-setting-guest-irq-filter-mask-err
sudo apt-get install gcc make perl
cd /media/$USER/VBox_GAs_5.2.22
sudo ./VBoxLinuxAdditions.run
sudo reboot
至此,我们完成了内核编译的整个过程。
上节内容讲解了内核、模块的编译,安装过程。事实上,有时候我们仅仅只是想安装一个模块,而不想重新编译内核,那么在内核需要的功能开启的情况下,单独编译模块是否可行呢?,回答是正确的,可以实现内核模块的编译过程。本节内容主要参考:https://blog.csdn.net/yeshennet/article/details/82315604
简单的hello world模块
1 hello.c文件
/*
* hello.c - The simplest kernel module.
*/
#include /* Needed by all modules */
#include /* Needed for KERN_INFO */
int init_module(void)
{
printk(KERN_INFO "Hello world 1.\n");
/*
* A non 0 return means init_module failed; module can't be loaded.
*/
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "Goodbye world 1.\n");
}
2 Makefile文件
obj-m += hello.o
all:
make -C /lib/modules/4.10.17/build M=$(PWD) modules
clean:
make -C /lib/modules/4.10.17/build M=$(PWD) clean
注意三点:1 hello.o要和hello.c对应,2 Makefile中的目录是你安装模块时的模块安装目录,3 Makefile中make前面是一个TAB符。运行:make,结果如下
同时在目录里生成一些文件
其中hello.ko文件就是我们说的模块
我们对这个hello模块文件进行操作
1插入一个模块:
sudo insmod hello.ko
要是报错:
insmod: ERROR: could not insert module hello.ko: Invalid module format
可能原因:make时使用的内核版本和本系统的内核版本不一致
需要重新启动,选择内核来实现模块插入等操作
uname -r
运行插入模块的命令
2 查看模块的相关信息
sudo modinfo hello.ko
3 卸载模块
sudo rmmod hello.ko
4 lsmod 命令是查看当前已载入的模块
模块的基本介绍就到此结束,下节介绍对kasan功能的实现
实际上,linux内核源码提供了关于测试kasan的模块,在内核编译中可以直接选择,但是作者本人太笨,一直不知道怎么搞,另外也想简单的学习下内核模块是怎么搞出来的,就没打开test_kasan模块,而是采用上一小节的方法尝试着将test_kasan.ko做出来。
在内核源码usr/src/linux-source-4.10.0/lib中可以找到test_kasan.c文件,之后,我们选择其中的一个函数进行测试就行,不过依然需要我们编写一个Makefile文件。
首先从test_kasan.c中抽出一个函数作为test.c文件
#include
#include
#include
#include
int init_module(void)
{
char *ptr;
size_t size = 123;
pr_info("out-of-bounds to right\n");
ptr = kmalloc(size, GFP_KERNEL);
if (!ptr) {
pr_err("Allocation failed\n");
return 0;
}
ptr[size] = 'x';
kfree(ptr);
}
void cleanup_module(void)
{
printk(KERN_INFO "GOODBYE\n");
}
其次,制作Makefile文件
obj-m += test.o
all:
make -C /lib/modules/4.10.17/build M=$(PWD) modules
clean:
make -C /lib/modules/4.10.17/build M=$(PWD) clean
之后,sudo make和 make 产生了不同的结果(先sudo make,后make)
最终还是生成了相关的文件
运行sudo insmod test.ko后,在运行 dmesg 查看相关信息
会在打印的内容中找到以下的字样:
或者,也可以在/usr/log/kern.log中找到相关的记录
由此来看,kasan是一个自动化的内核插桩工具,他不会给你任何的提示来说明是否打开了kasan,需要在对内核的相关过程中实现自动化的检测,要是不懂dmesg命令,还真不知道这个kasan怎么搞。KASAN藏的真够深的!!
想了解kasan原理的可以参考:https://www.cnblogs.com/alantu2018/p/8457420.html
到此,我们实现了整个过程,从中学习到了内核的编译,模块的编译,kasan的相关操作。内核的编译很容易出错,要是在虚拟机中做,最好要学会时刻保存备份。模块的编译,我只看了最基础的那部分内容,其他的并没有深入,有兴趣的可以参考文中的网址。Kasan的操作是一个很有用的工具,能够自动化检测UAF漏洞。总之,经过这次学习,总算理解了为啥搞安全的都喜欢Linux,这个功能真的强大。
再次感谢文中的博客提供的宝贵资料!!