前言

以4.8.13版本为例,记录一下kernel的编译过程以及文件系统的搭建。

正文

编译环境

GCC 7.4

Ubuntu 16.04

内核编译

源码下载

1
2
$ wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.8.13.tar.xz
$ tar -xvf linux-4.8.13.tar.xz

编译过程

1
2
3
4
5
$ cd linux-4.8.13/
$ make menuconfig
$ make -j4 #四核编译,虽然还是要很久
$ make all
$ make modules

编译完成后可以在当前目录下找到vmlinux文件(调试时使用),在arch/x86/boot目录下找到bzImage文件(启动kernel用的)。

1
2
3
4
5
6
Kernel heacking -->
Compile-time checks and compiler options -->
选中Compile the kernel with debug info、Reduce debugging information、Debug Filesystem,其他全部取消。
去除Collect scheduler debugging info、Collect scheduler statistics选项。
Processor type and features -->
取消Linux guest support选项。

PS:编译出来的vmlinux几百MB,大佬们说去掉符号表就行了,但感觉去掉符号表的话要vmlinux就没用了呀。。以后有思路了再研究下怎么缩减吧。

报错

编译时发生下列错误:

1
2
3
4
kernel/built-in.o: In function `update_wall_time':
/home/ubn/linux/linux-4.8.13/kernel/time/timekeeping.c:2079: undefined reference to `____ilog2_NaN'
Makefile:951: recipe for target 'vmlinux' failed
make: *** [vmlinux] Error 1

保存下列代码到patch.diff,然后运行patch -i patch.diff, 提示输入操作文件时, 先后输入include/linux/log2.htools/include/linux/log2.h即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
diff --git a/include/linux/log2.h b/include/linux/log2.h
index ef3d4f67118c..c373295f359f 100644
--- a/include/linux/log2.h
+++ b/include/linux/log2.h
@@ -16,12 +16,6 @@
#include <linux/bitops.h>

/*
- * deal with unrepresentable constant logarithms
- */
-extern __attribute__((const, noreturn))
-int ____ilog2_NaN(void);
-
-/*
* non-constant log of base 2 calculators
* - the arch may override these in asm/bitops.h if they can be implemented
* more efficiently than using fls() and fls64()
@@ -85,7 +79,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n)
#define ilog2(n) \
( \
__builtin_constant_p(n) ? ( \
- (n) < 1 ? ____ilog2_NaN() : \
+ (n) < 2 ? 0 : \
(n) & (1ULL << 63) ? 63 : \
(n) & (1ULL << 62) ? 62 : \
(n) & (1ULL << 61) ? 61 : \
@@ -148,10 +142,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n)
(n) & (1ULL << 4) ? 4 : \
(n) & (1ULL << 3) ? 3 : \
(n) & (1ULL << 2) ? 2 : \
- (n) & (1ULL << 1) ? 1 : \
- (n) & (1ULL << 0) ? 0 : \
- ____ilog2_NaN() \
- ) : \
+ 1 ) : \
(sizeof(n) <= 4) ? \
__ilog2_u32(n) : \
__ilog2_u64(n) \
diff --git a/tools/include/linux/log2.h b/tools/include/linux/log2.h
index 41446668ccce..d5677d39c1e4 100644
--- a/tools/include/linux/log2.h
+++ b/tools/include/linux/log2.h
@@ -13,12 +13,6 @@
#define _TOOLS_LINUX_LOG2_H

/*
- * deal with unrepresentable constant logarithms
- */
-extern __attribute__((const, noreturn))
-int ____ilog2_NaN(void);
-
-/*
* non-constant log of base 2 calculators
* - the arch may override these in asm/bitops.h if they can be implemented
* more efficiently than using fls() and fls64()
@@ -78,7 +72,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n)
#define ilog2(n) \
( \
__builtin_constant_p(n) ? ( \
- (n) < 1 ? ____ilog2_NaN() : \
+ (n) < 2 ? 0 : \
(n) & (1ULL << 63) ? 63 : \
(n) & (1ULL << 62) ? 62 : \
(n) & (1ULL << 61) ? 61 : \
@@ -141,10 +135,7 @@ unsigned long __rounddown_pow_of_two(unsigned long n)
(n) & (1ULL << 4) ? 4 : \
(n) & (1ULL << 3) ? 3 : \
(n) & (1ULL << 2) ? 2 : \
- (n) & (1ULL << 1) ? 1 : \
- (n) & (1ULL << 0) ? 0 : \
- ____ilog2_NaN() \
- ) : \
+ 1 ) : \
(sizeof(n) <= 4) ? \
__ilog2_u32(n) : \
__ilog2_u64(n) \

文件系统构建

下载busybox及编译

1
2
3
4
5
wget https://busybox.net/downloads/busybox-1.31.0.tar.bz2
tar -jxvf busybox-1.19.4.tar.bz2
cd busybox-1.19.4
make menuconfig
make install

make install后在目录下就会发现__install文件夹,里面的东西就可以用来构建文件系统了。

1
2
3
4
5
Busybox Settings -> Build Options -> Build Busybox as a static binary 编译成静态文件

关闭下面两个选项:
Linux System Utilities -> [] Support mounting NFS file system 网络文件系统
Networking Utilities -> [] inetd (Internet超级服务器)

构建文件系统

这里需要先创建一些文件夹:

1
$ mkdir proc sys dev etc

下面添加一些东西,以便更好的使用:

添加用户

busybox里面默认是root用户,并且用户配置等东西不齐全,这里直接上星盟平台扒下kernel的环境。

下载kernel pwn1,解压出来后,将其中的etc/目录复制过来,这样就有了用户pwn,并且也修复了用户文件,进而可以切换用户等操作了。

修复dpkg

dpkg是用来安装deb文件的(linux下一些软件都是提供deb包,可以在各大源网站下载到)。为了方便安装复现用的环境,需要先将dpkg修复

在目录下执行以下命令:

1
2
$ mkdir var\lib\dpkg\info
$ touch var\lib\dpkg\status

这样就可以用dpkg -i [packagename]来安装一些依赖环境了。

初始化文件init

init文件是开机后自动运行的脚本,在其中可以执行一些命令,如挂载模块等,它具有root权限,并且需要通过该文件来执行/bin/sh,这样才能进入到命令行中。

这里给个基本的内容,具体的可以根据需要添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mkdir /tmp
mount -t tmpfs none /tmp
mdev -s

setsid /bin/cttyhack setuidgid 1000 /bin/sh

umount /proc
umount /sys
poweroff -d 0 -f

直接保存到文件系统根目录的init中即可。

最后用命令find . | cpio -o --format=newc > ../rootfs.img运行完毕后就可以得到我们的文件系统rootfs.img了。

启动脚本

紧接着就是用qemu启动我们的kernel了,但是每次都要打一长串才能启动很不方便,所以一般都是将命令保存在shell文件中执行的。类似于下面的脚本:

1
2
3
4
5
6
7
8
9
qemu-system-x86_64  \
-m 256M \
-cpu kvm64 \
-kernel ./bzImage \
-initrd rootfs.img \
-nographic \
-s \
-smp 4,cores=2,threads=2 \
-append "console=ttyS0 quiet nokaslr"

这里qemu-system-x86_64代表是x86的64位架构

-m 256M表示分配256M物理内存给该kernel运行

-cpu kvm64表示采用kvm64的cpu,同时如果要添加保护可以在这里加,如添加smep:-cpu kvm64,smep

-kernel ./bzImage用于使用的内核。

-initrd rootfs.img用于指定选用的文件系统。

-nographic表示不使用图形化界面,只使用串口。

-s表示开启远程gdb调试,端口号为1234.

-smp 4,cores=2,threads=2表示cpu为双核双线程。

-append "console=ttyS0 quiet nokaslr"这里代表内核启动参数,不是很明白,不过nokaslr代表不开启内核地址随机化,如果要开启缓存kaslr即可。

参考链接

Linux kernel Exploit 内核漏洞学习(0)-环境安装 - 先知社区

linux内核漏洞利用初探(1):环境配置

从零使用qemu模拟器搭建arm运行环境_海枫的专栏-CSDN博客_如何模拟运行arm镜像文件