Kernel环境搭建
2022-03-22 07:35:57

Kernel环境搭建

内核下载与编译

Linux-5.4.98下载

curl -O -L https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.4.98.tar.xz

unxz linux-5.4.98.tar.xz

验证内核签名

防止内核被恶意修改

curl -O -L https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.4.98.tar.sign

gpg --verify linux-5.4.98.tar.sign

使用相应的公钥验证签名

下载对应内核个版本发布者的公钥

gpg --locate-keys torvalds@kernel.org gregkh@kernel.org

导入torvalds的公钥

gpg --verify linux-5.4.98.tar.sign

消除警告:选择使用TOFU信任对应的密钥

gpg --tofu-policy good 647F28654894E3BD457199BE38DBBDC86092693E

gpg --trust-model tofu --verify linux-5.4.98.tar.sign

解压压缩包得到内核源码

tar -xf linux-5.4.98.tar

Linux-5.4.98编译

编译选项配置

make menuconfig

选择:Kernel hacking==》Compile-time checks and compiler options==》

勾选Compile the kernel with debug info用于调试(默认已勾选)

编译内核

sudo -s

make bzImage -j4

出现报错

解决

sudo vim .config

\\修改为
CONFIG_SYSTEM_TRUSTED_KEYS=""
CONFIG_SYSTEM_REVOCATION_KEYS=""

编译成功:

内核常见文件:

bzImage:目前主流的kernel镜像格式==》big zImage==》适用于大于512kb的kernel

该镜像会被加载至内存的高地址,高于1MB

bzImage使用gzip压缩,文件的开头部分有gzip解压缩的代码==》不能使用gunzip解压缩

文件目录:arch/x86/boot/bzImage

zImage:比较老的kernel镜像格式,适用于不大于512kb的kernel

这个镜像会被加载至内存的低地,内存的前640kb

不能使用gunzip解压缩

vmlinuz:包含压缩后的vmlinux,还包含gzip解压缩的代码(zImage或者bzImage文件)

该文件是bootable==》

bootable:它能够把内核加载至内存中,对于Linux来说,该文件位于/boot目录下

该目录包含了启动系统时所需要的文件

vmlinux:静态链接的Linux kernel,以可执行文件的形式存在,未经过压缩

该文件为再生成vmlinuz的过程中产生==》适合于调试,不是bootable的

vmlinux.bin:静态链接的Linux kernel,只是一个可启动的(bootable)二进制文件

所有的符号信息和重定位信息都被删除

生成命令:

objcopy -O binary vmlinux vmlinux.bin

uImage:是U-boot专用的镜像文件,它是在zImage之前加上一个长度为0x40的tag构成

tag==》说明这个镜像文件的类型,加载的位置,生成时间,大小等信息

下载编译busybox

busybox下载

wget https://busybox.net/downloads/busybox-1.32.1.tar.bz2

tar -jxf busybox-1.32.1.tar.bz2

busybox编译

配置编译参数

make menuconfig

Setttings==》选中Build static binary (no shared libs) busybox编译为静态文件

==》Linux System Utilities==》取消Support mounting NFS file systems on Linux < 2.6.23(NEW)

==》Networking Utilities==》取消inetd

编译

make -j4

配置文件系统

使用busybox创建_install目录

make install

编译完成后,进入_install目录,创建文件夹

cd _install
mkdir -pv {bin,sbin,etc,proc,sys,home,lib64,lib/x86_64-linux-gnu,usr/{bin,sbin}}
touch etc/inittab
mkdir etc/init.d
touch etc/init.d/rcS
chmod +x ./etc/init.d/rcS

配置初始化脚本

首先配置etc/inttab,指定系统初始化脚本

::sysinit:/etc/init.d/rcS
::askfirst:/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init

配置etc/init.d/rcS,配置目录的挂载

#!/bin/sh
mount -t proc none /proc
mount -t sys none /sys
/bin/mount -n -t sysfs none /sys
/bin/mount -t ramfs none /dev
/sbin/mdev -s

也可以在根目录下创建init

#!/bin/sh

mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev

exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console

echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh

umount /proc
umount /sys
poweroff -d 0  -f
chmod +x ./init

配置用户组,创建两个用户组root和ctf,两个用户root和ctf

echo "root:x:0:0:root:/root:/bin/sh" > etc/passwd
echo "ctf:x:1000:1000:ctf:/home/ctf:/bin/sh" >> etc/passwd
echo "root:x:0:" > etc/group
echo "ctf:x:1000:" >> etc/group
echo "none /dev/pts devpts gid=5,mode=620 0 0" > etc/fstab

打包文件系统为镜像文件

find . | cpio -o --format=newc > ../../rootfs.cpio
\\或者
find . | cpio -o -H newc > ../core.cpio

若后续需要向文件系统中添加其他文件,可以选择在_install文件夹中添加(不推荐)

也可以解压文件系统镜像后添加文件再重新打包

\\解压磁盘镜像
cpio -idv < ./rootfs.cpio

\\打包文件镜像
find . | cpio -o --format=newc > ../new_rootfs.cpio

使用qemu运行内核

1、将bzImage和rootfs.cpio复制到同一个目录下

2、编写启动脚本

vim boot.sh
#!/bin/sh
qemu-system-x86_64 \
    -m 128M \
    -kernel ./bzImage \
    -initrd  ./rootfs.cpio \
    -monitor /dev/null \
    -append "root=/dev/ram rdinit=/sbin/init console=ttyS0 oops=panic panic=1 loglevel=3 quiet nokaslr" \
    -cpu kvm64,+smep \
    -smp cores=2,threads=1 \
    -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
    -nographic \
    -s

参数说明:

-m:虚拟机内存大小
-kernel:内存镜像路径
-initrd:磁盘镜像路径
-append:附加参数
nokalsr:关闭内核地址随机化,方便调试
rdinit:指定初始启动进程,/sbin/init进程默认以/etc/init.d/rcS作为启动脚本
loglevel=3&quiet:不输出log
consolve=tty50:指定终端为/dev/tty50,启动就能进入终端界面
-monitor:将监视器重定向到主机设备/dev/null==>防止CTF被人偷了qemu拿flag
-cpu:设置CPU安全选项,在这里开启了smep保护
-s:相当于-gdb tcp::1234简写,可以通过gdb连接本地端口进行调试

运行boot.sh,启动

编译内核模块

在载入/卸载时通过printk()在内核缓冲区进行输出

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init kernel_module_init(void)
{
    printk("<1>Hello the Linux kernel world!\n");
    return 0;
}

static void __exit kernel_module_exit(void)
{
    printk("<1>Good bye the Linux kernel world! See you again!\n");
}

module_init(kernel_module_init);
module_exit(kernel_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("arttnba3");

头文件介绍

linux/module.h:对于LKM是一个必须的头文件

linux/kernel,h:载入内核相关信息

linux/init.h:包含一些有用的宏

入口点/出口点:

内核模块的入口在module_init(),出口在module_exit()

在内核载入/卸载内核模块时调用这两个函数

编译内核模块

Makefile

本地:

obj-m += clo.o
CURRENT_PATH := $(shell pwd)
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)
all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

//模拟kernel

obj-m += clo.o

KDIR =/home/closure/kernel/linux-5.4.98

all:
    $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
    rm -rf *.o *.ko *.mod.* *.symvers *.order

Makefile简介:

obj-m:指定编译的结果为.ko文件==》可装载内核模块

CURRENT_PATH & LINUX_KERNEL & LINUX_KERNEL_PATH:

三个自定义变量==》

通过shell命令获得当前路径,内核版本,内核源码路径

KDIR:标识内核源码目录,提供驱动编译所需要的环境

all:编译指令

clean:清理指令

编译:
make

编译成功:

测试内核模块:
sudo insmod clo.ko
sudo rmmod clo
dmesg

本地实现:

模拟实现: