Linux下kernel调试环境搭建
前言
环境搭建在虚拟机ubuntu16.04
下进行(vm配置开启cpu虚拟化)
一般内核调试需要的东西就是内核镜像
和磁盘镜像
,不同版本的内核就用不同版本的内核镜像。而需要什么文件就调整磁盘镜像。
安装依赖
内核镜像
下载内核源码:
linux各个版本内核源码可以从这下载:https://www.kernel.org/
这里用这个版本:https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.15.tar.gz
解压进入
设置编译选项
勾选以下项目:
- Kernel debugging
- Compile-time checks and compiler options —> Compile the kernel with debug info和Compile the kernel with frame pointers
- KGDB
然后保存退出
开始编译
成功信息类似这样:
从源码根目录取到vmlinux
,从arch/x86/boot/
取到bzImage
磁盘镜像
编译busybox
BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件。BusyBox 包含了一些简单的工具,例如ls、cat和echo等等,还包含了一些更大、更复杂的工具,例grep、find、mount以及telnet。有些人将 BusyBox 称为 Linux 工具里的瑞士军刀。简单的说BusyBox就好像是个大工具箱,它集成压缩了 Linux 的许多工具和命令,也包含了 Android 系统的自带的shell。
这里busybox的作用主要是搭建一个简易的initranfs
下载源码:https://busybox.net/
用1.28.4测试:http://busybox.net/downloads/busybox-1.28.4.tar.bz2
解压进入目录:
设置编译选项:
选中:Build static binary (no shared libs)
开始编译:
打包出rootfs.img磁盘镜像
busybox编译完成后,进入源码目录下新增的_install
目录
先建立好文件系统:
运行:vim etc/inittab
添加以下内容:
运行:mkdir etc/init.d;vim etc/init.d/rcS
添加以下内容:
还可以在fs根目录创建init
文件,写入初始化指令,并添加执行权限:
这一步主要配置各种目录的挂载
添加执行权限:chmod +x ./etc/init.d/rcS
打包出rootfs.img
在_install
目录下执行:
文件系统镜像被打包存放在了/home/{username}/core/
目录下
用qemu启动
配置启动参数
创建一个新的目录将准备好的bzImage
和rootfs.img
放入,然后编写一个boot.sh
boot.sh
的编写可以参考qemu的各个参数:
部分参数解释:
- -m 指定内存大小
- -kernel 指定内核镜像路径
- -initrd 指定磁盘镜像路径
- -s 是GDB调试参数,默认会开启1234端口便于remote调试
- cpu 该选项可以指定保护模式
运行boot.sh
即可启动系统
几种常见的保护
canary, dep, PIE, RELRO 等保护与用户态原理和作用相同
smep
: Supervisor Mode Execution Protection,当处理器处于 ring0 模式,执行 用户空间 的代码会触发页错误。(在 arm 中该保护称为 PXN)smap
: Superivisor Mode Access Protection,类似于 smep,通常是在访问数据时。mmap_min_addr
如何向其中添加文件?
方法1
解压磁盘镜像:
cpio -idv < ./initramfs.img
重打包:
find . | cpio -o --format=newc > ../new_rootfs.img
方法2
借助base64
编码从shell中直接写入(适用于写exp等)
GDB调试
一般只需要设置好架构然后
remote
一下就行,如果是非x86的架构可能要用gdb-multiarch
查看函数地址
需要先设置
init
文件获得root权限,如下:这里重点在于利用
setuidgid 0
创建一个root shell
然后同样boot后输入
cat /proc/kallsyms
可以显示出内核中所有的函数符号和对应地址,在gdb中下断即可例如可以断在这个函数:
cat /proc/kallsyms | grep get_user_pages
,下断后尝试执行ls
就可以停住了加载第三方ko
CTF比赛中经常需要加载内核模块
*.ko
,其实很简单,只需要运行insmod xxx.ko
就行关键在于有的ko需要指定内核版本
可以使用apt download 相应内核的deb包,然后解包得到bzImage
例如:
apt download linux-image-4.15.0-22-generic
然后在fs中的
init
脚本加上insmod xxx.ko
即可载入系统后可以使用
lsmod
来查看载入的ko以及他的所在的内核地址调试ko
关闭内核模块地址随机化:
nokaslr
写个脚本用来快速启动gdb并设置相应参数,节省时间:
qemu pci设备相关
查看PCI设备信息
qemu逃逸常常是因为加载了自定义的PCI设备,可以在qemu启动参数参数的
-device
项中看出。进入qemu-system环境后,执行如下命令来获取pci设备信息:
- lspci: 显示当前主机的所有PCI总线信息,以及所有已连接的PCI设备基本信息;
Q: 如何确定哪个是我们要分析的
Device
?最右边的值如1234:11e9
是vendor_id:device
,可以在IDA中查看xxxx_class_init
函数来确定设备的vendor_id:device
。然后进入系统中使用lspci
,就可以对应上了。
注意
xx
:yy
:z
的格式为总线
:设备
:功能
的格式!也可以通过
-t
和-v
参数以树状显示:其中
[0000]
表示pci的域, PCI域最多可以承载256条总线。 每条总线最多可以有32个设备,每个设备最多可以有8个功能。VendorIDs
、DeviceIDs
、以及Class Codes
字段区分出不同的设备,可以用以下参数查看:通过
-x
参数可以查看设备的内存空间:sudo cat /proc/iomem: 查看各种设备占用的地址空间(包括内存和reversed区域);
sudo cat /sys/devices/pci0000:00/[设备编号]/resource: 查看设备配置空间,其中设备编号可以在lspci
中看到,例如:sudo cat /sys/devices/pci0000:00/0000:00:07.1/resource
.
每行分别表示相应空间的起始地址(start-address)、结束地址(end-address)以及标识位(flags)。
配置空间中的数据起始就是记录设备相关信息的数据,如上面提到的VendorIDs
、DeviceIDs
、和Class Codes
字段等...
除了resource
文件,还有resource0
(MMIO空间)以及resource1
(PMIO空间)
引用博客:
https://veritas501.space/2018/06/03/kernel%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/#more
https://eternalsakura13.com/2020/07/11/kernel_qemu/#more
https://eternalsakura13.com/2018/04/13/qemu/