记录一下配置kernel环境过程中遇到的一些问题
内核
内核下载
网上一些文章用的清华源,但是现在好像…
<p lang="zh-cn">我们检测到您所在的子网和/或所使用的客户端存在大量下载某些较大二进制文件的行为,为保证用户的正常使用,我们阻断了此类请求。</p>
所以网上找到了其他下载https://cdn.kernel.org/pub/linux/kernel/v5.x/#/,不过因为感觉下载的好慢,直接在`windows`上下载了拖进虚拟机了
curl -O -L https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.98.tar.xz
unxz linux-5.4.98.tar.xz
tar -xf linux-5.4.98.tar
这样就得到了内核源码,因为长期版稳定便于学习所以选择了5.4.98
版本的内核
内核编译
直接make
报错了
☁ linux-5.4.98 sudo make menuconfig
[sudo] starrysky 的密码:
LEX scripts/kconfig/lexer.lex.c
/bin/sh: 1: flex: not found
make[1]: *** [scripts/Makefile.host:9:scripts/kconfig/lexer.lex.c] 错误 127
make: *** [Makefile:590:menuconfig] 错误 2
需要先安装flex
和bison
sudo apt-get install flex
sudo apt-get install bison
进入菜单,在菜单中选择Kernel hacking -> Compile-time checks and compiler options
并且勾选Compile the kernel with debug info
,全选Kernel debugging
中全部内容
☁ linux-5.4.98 sudo make menuconfig
开始编译内核
sudo make -j 4 bzImage
其中会遇到一些问题,解决方法:
.config
第9868
行置空(不删除此行CONFIG_SYSTEM_TRUSTED_KEYS=""
安装
libelf-dev
和dwarves
sudo apt-get install libelf-dev sudo apt install dwarves
最后显示如下内容表示编译成功
Setup is 18044 bytes (padded to 18432 bytes).
System is 15045 kB
CRC 815d9126
Kernel: arch/x86/boot/bzImage is ready (#2)
在编译成功后,我们一般主要关注于如下的文件
bzImage
:arch/x86/boot/bzImage
vmlinux
:源码所在的根目录下。
常见内核文件的介绍:
- bzImage:目前主流的
kernel
镜像格式,适用于较大的(> 512 KB
)Kernel
。这个镜像会被加载到内存的高地址(高于1MB
)。bzImage
是用gzip
压缩的,不能用gunzip
来解压 - zImage:比较老的
kernel
镜像格式,适用于较小的Kernel
。启动时,这个镜像会被加载到内存的低地址,即内存的前640 KB
。zImage
也不能用gunzip
来解 - vmlinuz:
vmlinuz
不仅包含了压缩后的vmlinux
,还包含了gzip
解压缩的代码。实际上就是zImage
或者bzImage
文件。该文件是bootable
的,即它能够把内核加载到内存中。对于Linux
系统而言,该文件位于/boot
目录下,该目录包含了启动系统时所需要的文件 - vmlinux:静态链接的
Linux kernel
,以可执行文件的形式存在,尚未经过压缩。该文件往往是在生成vmlinuz
的过程中产生的。该文件适合调试,但不是 bootable - vmlinux.bin:也是静态链接的
Linux kernel
,只是以一个可启动的 (bootable
) 二进制文件存在。所有的符号信息和重定位信息都被删除了。生成命令为:objcopy -O binary vmlinux vmlinux.bin
。 - uImage:
uImage
是U-boot
专用的镜像文件,它是在zImage
之前加上了一个长度为0x40
的tag
而构成的。这个tag
说明了这个镜像文件的类型、加载位置、生成时间、大小等信息
编译内核驱动
c
语言源码pwn.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("Dual BSD/GPL");
static int ko_test_init(void) {
printk("This is a test ko!\n");
return 0;
}
static void ko_test_exit(void) {
printk("Bye Bye~\n");
}
module_init(ko_test_init);
module_exit(ko_test_exit);
Makefile
文件
obj-m += pwn.o
KDIR =/home/starrysky/kernel/linux-5.4.98
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *.ko *.mod.* *.symvers *.order
其中obj-m
指定了要声称的模块,后面接c
源码文件名.o
,KDIR
为内核源码路径
$(MAKE) -C $(KDIR) M=$(PWD) modules
-C
表示进入到指定的内核目录
M
指定驱动源码的环境,M
并不是 Makefile
的选项,而是内核根目录下 Makefile
中使用的变量。这会使得该 Makefile
在构造模块之前返回到 M
指定的目录,并在指定的目录中生成驱动模块
最后运行sudo make
编译内核驱动
☁ pwn ls
Makefile modules.order Module.symvers pwn.c pwn.ko pwn.mod pwn.mod.c pwn.mod.o pwn.o
虽然正常人不会这样但是要注意文件名不要用make
(嗯我就是那个非正常人类
Qemu 模拟环境
准备
安装qemu
和busybox
sudo apt install qemu
wget https://busybox.net/downloads/busybox-1.32.1.tar.bz2
tar -jxf busybox-1.32.1.tar.bz2
配置busybox
,在 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 menuconfig
make -j 8
配置文件系统
使用 make install
命令,将生成文件夹_install
,该目录将成为 rootfs
,在该文件夹下创建文件夹
mkdir -p proc sys dev etc/init.d
再创建一个init
文件
#!/bin/sh
echo "INIT SCRIPT"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp
echo -e "Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
setsid /bin/cttyhack setuidgid 0 /bin/sh
打包文件系统
find . | cpio -o --format=newc > ../rootfs.img
# 解包命令
# cpio -idmv < rootfs.img
启动内核
脚本如下,bzImage
就是之前内核里的bzImage
,rootfs.img
是打包文件系统时(上一步)创建的,nographic
关闭了图形界面,console=ttyS0
将输出重定向到了终端
#!/bin/sh
qemu-system-x86_64 \
-nographic \
-kernel ../arch/x86/boot/bzImage \
-initrd ./rootfs.img \
-append "console=ttyS0 kaslr" \
这样就启动好了
INIT SCRIPT
Boot took 6.24 seconds
/ # ls
bin etc linuxrc root sys usr
dev init proc sbin tmp
加载驱动
将之前写的驱动复制到_install
文件夹下,改一下init
文件
#!/bin/sh
echo "INIT SCRIPT"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp
insmod /1.ko
echo -e "Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
setsid /bin/cttyhack setuidgid 0 /bin/sh
重新打包再运行,可以看到加载成功了,显示了This is a test ko!
INIT SCRIPT
[ 6.129377] pwn: loading out-of-tree module taints kernel.
[ 6.133147] pwn: module verification failed: signature and/or required key missing - tainting kernel
[ 6.147188] This is a test ko!
Boot took 6.13 seconds
调试分析
基本操作
查看装载的驱动
lsmod
获取驱动加载的基地址
grep target_module_name /proc/modules
启动调试
在启动脚本里加-s
,表示-gdb tcp::1234
#!/bin/sh
qemu-system-x86_64 \
-nographic \
-kernel ../arch/x86/boot/bzImage \
-initrd ./rootfs.img \
-append "console=ttyS0 kaslr" \
-s
启动之后gdb
连接上去调试
gdb -q -ex "target remote localhost:1234"
连接成功
pwndbg: loaded 147 pwndbg commands and 47 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $ida GDB functions (can be used with print/break)
Remote debugging using localhost:1234
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0xffffffffa1dfea1e in ?? ()
------- tip of the day (disable with set show-tips off) -------
Use GDB's dprintf command to print all calls to given function. E.g. dprintf malloc, "malloc(%p)\n", (void*)$rdi will print all malloc calls
Permission error when attempting to parse page tables with gdb-pt-dump.
Either change the kernel-vmmap setting, re-run GDB as root, or disable `ptrace_scope` (`echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope`)
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
参考文章
https://www.z1r0.top/2021/10/21/%E5%86%85%E6%A0%B8%E4%B8%8B%E8%BD%BD%E4%B8%8E%E7%BC%96%E8%AF%91/#/
https://ctf-wiki.org/pwn/linux/kernel-mode/environment/readme/#/