0x2 内核的简单食用异闻录
0x0 依赖
1 |
|
虽然但是,我记得即使这里装了qemu,后面还要自己装个x86-64的来着
pwn相关:
hxd写了个一键安装脚本
不过好像有bug,要自己手动装一下pwndbg
好像是因为太新了
0x1 获取内核镜像
大概有如下三种方式:
- 下载内核源码后编译
- 直接下载现成的的内核镜像,不过这样我们就不能自己魔改内核了2333
- 直接使用自己系统的镜像
arttnba3师傅介绍了三种,但是我只用了第一种
I.获取内核源码
前往Linux Kernel Archive下载对应版本的内核源码
II.配置编译选项
解压我们下载来的内核源码
1 |
|
完成后进入文件夹内,执行如下命令开始配置编译选项
1 |
|
进入如下配置界面
保证勾选如下配置(默认都是勾选了的):
- Kernel hacking —> Kernel debugging
- Kernel hacking —> Compile-time checks and compiler options —> Compile the kernel with debug info
- Kernel hacking —> Generic Kernel Debugging Instruments –> KGDB: kernel debugger
- kernel hacking —> Compile the kernel with frame pointers
最后这个 Compile the kernel with frame pointers,可能会找不到,需要修改配置文件lib/Kconfig.debug
:
1 |
|
把这个符号给添加上一行(默认是没有这行的):
添加上了之后,就能找到:kernel hacking —> Compile the kernel with frame pointers,应该会是开启的状态
通常保存的路径在当前目录下的 .config
文件中,如果你在生成配置文件后才想起来忘了改某个选项也可以直接编辑这个文件
III.开始编译
运行如下命令开始编译,生成内核镜像
1 |
|
IV.可能出现的错误
我编译时也出现过这个错误:
1 |
|
只需要在 .config
文件中找到 CONFIG_SYSTEM_TRUSTED_KEYS
,等于号后面的值改为 ""
V.编译结果
完成之后会出现如下信息:
1 |
|
我们主要关注生成的这两个文件:
vmlinux:原始内核文件
在当前目录下提取到vmlinux
,为编译出来的原始内核文件
1 |
|
bzImage:压缩内核镜像
在当前目录下的arch/x86/boot/
目录下提取到bzImage
,为压缩后的内核文件,适用于大内核
1 |
|
顺带一提,我是在wsl里面编译的,但是在/mnt/的挂载路径中好像会出现编译问题,但是换到用户路径下就一起正常了。
↓看起来是有用的情报
zImage && bzImage
zImage–是vmlinux经过gzip压缩后的文件。
bzImage–bz表示“big zImage”,不是用bzip2压缩的,而是要偏移到一个位置,使用gzip压缩的。两者的不同之处在于,zImage解压缩内核到低端内存(第一个 640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么采用zImage或bzImage都行,如果比较大应该用bzImage。https://blog.csdn.net/xiaotengyi2012/article/details/8582886
EXTRA.添加系统调用
1.分配系统调用号
在arch/x86/entry/syscalls/syscall_64.tbl
中添加我们自己的系统调用号,这里用CTF常见数字114514
1 |
|
2.声明系统调用
在include/linux/syscalls.h
中添加如下函数声明:
1 |
|
3.添加系统调用函数定义
在kernel/sys.c
中添加如下代码(放置于最后一行的#endif /* CONFIG_COMPAT */
之前):
1 |
|
这里的SYSCALL_DEFINE0()
本质上是一个宏,意为接收0个参数的系统调用,其第一个参数为系统调用名
这里定义了一个简单的输出一句话的系统调用,在这里使用了内核态的printk()
函数,输出的信息可以使用dmesg
进行查看
4.重新编译内核
这一步参照之前的步骤即可,通过这一步我们要将我们自己的系统调用编译到内核当中
5.测试系统调用
我们使用如下的例程测试我们的新系统调用
1 |
|
编译,放入磁盘镜像中后重新打包,qemu起内核后尝试运行我们的例程,结果如下:
因为dmesg输出的东西太多,这里还附加用了grep命令过滤
。。。?为啥会no found
破案了,因为缺少libc的库,在编译的时候补上-static参数即可
0x2 使用 busybox 构建文件系统
BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件,包含了例如ls、cat和echo等一些简单的工具,我们将用 busybox 为我们的内核提供一个基本的用户环境
没啥好说的,全抄!
一、编译busybox
I.获取busybox源码
在busybox.net下载自己想要的版本,笔者这里选用busybox-1.33.0.tar.bz2
这个版本
1 |
|
外网下载的速度可能会比较慢,可以在前面下载Linux源码的时候一起下载,也可以选择去国内的镜像站下载
解压
1 |
|
II.编译busybox源码
进入配置界面
1 |
|
勾选 Settings
—> Build static binary file (no shared lib)
若是不勾选则需要单独配置 libc,比较麻烦
接下来就是编译了,速度会比编译内核快很多
1 |
|
编译完成后会生成一个_install
目录,接下来我们将会用它来构建我们的磁盘镜像
二、建立文件系统
I.初始化文件系统
一些简单的初始化操作…
1 |
|
II.配置初始化脚本
首先配置 etc/inttab
,写入如下内容:
1 |
|
在上面的文件中指定了系统初始化脚本,因此接下来配置 etc/init.d/rcS
,写入如下内容,主要是挂载各种文件系统:
1 |
|
也可以在根目录下创建 init
文件,写入如下内容:
1 |
|
别忘了添加可执行权限:
1 |
|
III.配置用户组
1 |
|
在这里建立了两个用户组root
和ctf
,以及两个用户root
和ctf
IV.配置glibc库
将需要的动态链接库拷到相应位置即可
为了方便笔者这里就先不弄了,直接快进到下一步,以后有时间再补充(咕咕咕
听hxd说有个配好的,看看,等他有点时间发出来
三、打包文件系统为镜像文件
I. 打包为 cpio 文件
使用如下命令打包文件系统为 cpio 格式
1 |
|
也可以这么写
1 |
|
II. 打包为 ext4 镜像
这里也可以将文件系统打包为 ext4 镜像格式,首先创建空白 ext4 镜像文件,这里 bs
表示块大小,count
表示块的数量:
1 |
|
之后将其格式化为 ext4 格式:
1 |
|
挂载镜像,将文件拷贝进去即可:
1 |
|
四、向文件系统中添加文件
若是我们后续需要向文件系统中补充一些其他的文件,可以选择在原先的_install
文件夹中添加(不过这样的话若是配置多个文件系统则会变得很混乱),也可以解压文件系统镜像后添加文件再重新进行打包
cpio 文件
I.解压磁盘镜像
1 |
|
该命令会将磁盘镜像中的所有文件解压到当前目录下
II.重打包磁盘镜像
和打包磁盘镜像的命令一样
1 |
|
ext4 镜像
直接 mount 后再 umount jike:
1 |
|
0x3 使用qemu运行内核
终于到了最激动人心的时候了:我们即将要将这个Linux内核跑起来——用我们自己配置的文件系统与内核
安全起见,我们并不直接在真机上运行这个内核,而是使用qemu在虚拟机里运行
配置启动脚本
I. 使用 cpio 文件作为文件系统
首先将先前的bzImage
和rootfs.cpio
放到同一个目录下
接下来编写启动脚本
1 |
|
写入如下内容:
1 |
|
部分参数说明如下:
-
-m
:虚拟机内存大小 -
-kernel
:内存镜像路径 -
-initrd
:磁盘镜像路径 -
append
:附加参数选项nokalsr
:关闭内核地址随机化,方便我们进行调试rdinit
:指定初始启动进程,/sbin/init
进程会默认以/etc/init.d/rcS
作为启动脚本loglevel=3
&quiet
:不输出logconsole=ttyS0
:指定终端为/dev/ttyS0
,这样一启动就能进入终端界面
-
-monitor
:将监视器重定向到主机设备/dev/null
,这里重定向至null主要是防止CTF中被人给偷了qemu拿flag -
-cpu
:设置CPU安全选项,在这里开启了smep保护 -
-s
:相当于-gdb tcp::1234
的简写(也可以直接这么写),后续我们可以通过gdb连接本地端口进行调试
运行boot.sh
,成功启动~撒花~🌸🌸🌸
II. 使用 ext4 镜像作为文件系统
编写如下启动脚本即可,实际上只是将 -initrd
换成了 -hda
,这里也可以写成 -drive file=./rootfs.img,format=raw
:
1 |
|