前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux下kernel调试环境搭建

Linux下kernel调试环境搭建

作者头像
赤道企鹅
发布2022-08-01 14:06:32
2.3K0
发布2022-08-01 14:06:32
举报

前言

环境搭建在虚拟机ubuntu16.04下进行(vm配置开启cpu虚拟化)

一般内核调试需要的东西就是内核镜像磁盘镜像,不同版本的内核就用不同版本的内核镜像。而需要什么文件就调整磁盘镜像。

安装依赖

代码语言:javascript
复制
sudo apt-get update
sudo apt-get install qemu git libncurses5-dev fakeroot build-essential ncurses-dev xz-utils libssl-dev bc

内核镜像

下载内核源码:

linux各个版本内核源码可以从这下载:https://www.kernel.org/

这里用这个版本:https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.15.tar.gz

解压进入

代码语言:javascript
复制
tar -xzvf linux-4.15.tar.gz
cd linux-4.15

设置编译选项

代码语言:javascript
复制
make menuconfig

勾选以下项目:

  1. Kernel debugging
  2. Compile-time checks and compiler options —> Compile the kernel with debug info和Compile the kernel with frame pointers
  3. KGDB

然后保存退出

开始编译

代码语言:javascript
复制
make bzImage

成功信息类似这样:

代码语言:javascript
复制
Setup is 17244 bytes (padded to 17408 bytes).
System is 7666 kB
CRC 5c77cbfe
Kernel: arch/x86/boot/bzImage is ready  (#1)

从源码根目录取到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

解压进入目录:

代码语言:javascript
复制
tar jxvf busybox-1.28.4.tar.bz2
cd busybox-1.28.4

设置编译选项:

选中:Build static binary (no shared libs)

开始编译:

代码语言:javascript
复制
make install -j4

打包出rootfs.img磁盘镜像

busybox编译完成后,进入源码目录下新增的_install目录

先建立好文件系统:
代码语言:javascript
复制
cd _install
mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}

运行:vim etc/inittab

添加以下内容:

代码语言:javascript
复制
::sysinit:/etc/init.d/rcS
::askfirst:/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init

运行:mkdir etc/init.d;vim etc/init.d/rcS

添加以下内容:

代码语言:javascript
复制
#!/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

还可以在fs根目录创建init文件,写入初始化指令,并添加执行权限:

代码语言:javascript
复制
#!/bin/sh
echo "{==DBG==} INIT SCRIPT"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp
# insmod /xxx.ko # load ko
mdev -s # We need this to find /dev/sda later
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
setsid /bin/cttyhack setuidgid 1000 /bin/sh #normal user
# exec /bin/sh #root

这一步主要配置各种目录的挂载

添加执行权限:chmod +x ./etc/init.d/rcS

打包出rootfs.img

_install目录下执行:

代码语言:javascript
复制
find . | cpio -o --format=newc > ~/core/rootfs.img
gzip -c ~/core/rootfs.img > ~/core/rootfs.img.gz

文件系统镜像被打包存放在了/home/{username}/core/目录下

用qemu启动

配置启动参数

创建一个新的目录将准备好的bzImagerootfs.img放入,然后编写一个boot.sh

boot.sh的编写可以参考qemu的各个参数:

代码语言:javascript
复制
qemu-system-x86_64 \
-m 256M \
-kernel ./bzImage \
-initrd  ./rootfs.img \
-smp 1 \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 nokaslr quiet" \
-s  \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \

部分参数解释:

  • -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
  1. 解压磁盘镜像:cpio -idv < ./initramfs.img
  2. 重打包:find . | cpio -o --format=newc > ../new_rootfs.img
方法2

借助base64编码从shell中直接写入(适用于写exp等)

GDB调试

一般只需要设置好架构然后remote一下就行,如果是非x86的架构可能要用gdb-multiarch

代码语言:javascript
复制
gdb
pwndbg> set arch i386:x86-64
pwndbg> target remote localhost:1234

查看函数地址

需要先设置init文件获得root权限,如下:

代码语言:javascript
复制
#!/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 /bin/cttyhack setuidgid 0 /bin/sh
umount /proc
umount /sys
poweroff -d 0  -f

这里重点在于利用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并设置相应参数,节省时间:

代码语言:javascript
复制
#!/bin/sh
gdb \
-ex "target remote localhost:1234" \
-ex "continue" \
-ex "disconnect" \
-ex "set architecture i386:x86-64:intel" \
-ex "target remote localhost:1234" \
-ex "add-symbol-file ./busybox/baby.ko 0xdeadbeef" \

qemu pci设备相关

查看PCI设备信息

qemu逃逸常常是因为加载了自定义的PCI设备,可以在qemu启动参数参数的-device项中看出。

进入qemu-system环境后,执行如下命令来获取pci设备信息:

  1. lspci: 显示当前主机的所有PCI总线信息,以及所有已连接的PCI设备基本信息;
代码语言:javascript
复制
ubuntu@ubuntu:~$ lspci
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
00:02.0 VGA compatible controller: Device 1234:1111 (rev 02)
00:03.0 Unclassified device [00ff]: Device 1234:11e9 (rev 10)
00:04.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 03)

Q: 如何确定哪个是我们要分析的Device?

最右边的值如1234:11e9vendor_id:device,可以在IDA中查看xxxx_class_init函数来确定设备的vendor_id:device。然后进入系统中使用lspci,就可以对应上了。

注意xx:yy:z的格式为总线:设备:功能的格式!

也可以通过-t-v参数以树状显示:

代码语言:javascript
复制
ubuntu@ubuntu:~$ lspci -t -v
-[0000:00]-+-00.0  Intel Corporation 440FX - 82441FX PMC [Natoma]
           +-01.0  Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
           +-01.1  Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
           +-01.3  Intel Corporation 82371AB/EB/MB PIIX4 ACPI
           +-02.0  Device 1234:1111
           +-03.0  Device 1234:11e9
           \-04.0  Intel Corporation 82540EM Gigabit Ethernet Controller

其中[0000]表示pci的域, PCI域最多可以承载256条总线。 每条总线最多可以有32个设备,每个设备最多可以有8个功能。

VendorIDsDeviceIDs、以及Class Codes字段区分出不同的设备,可以用以下参数查看:

代码语言:javascript
复制
ubuntu@ubuntu:~$ lspci -v -m -n -s 00:03.0
Device: 00:03.0
Class:  00ff
Vendor: 1234
Device: 11e9
SVendor:        1af4
SDevice:        1100
PhySlot:        3
Rev:    10

ubuntu@ubuntu:~$ lspci -v -m -s 00:03.0
Device: 00:03.0
Class:  Unclassified device [00ff]
Vendor: Vendor 1234
Device: Device 11e9
SVendor:        Red Hat, Inc
SDevice:        Device 1100
PhySlot:        3 
Rev:    10

通过-x参数可以查看设备的内存空间:

代码语言:javascript
复制
ubuntu@ubuntu:~$ lspci -v -s 00:03.0 -x
00:03.0 Unclassified device [00ff]: Device 1234:11e9 (rev 10)
        Subsystem: Red Hat, Inc Device 1100
        Physical Slot: 3
        Flags: fast devsel
        /*这里显示的是MMIO空间的基址和大小*/
        Memory at febf1000 (32-bit, non-prefetchable) [size=256]
        /*这里显示的是PMIO空间的基址和大小*/
        I/O ports at c050 [size=8]
00: 34 12 e9 11 03 01 00 00 10 00 ff 00 00 00 00 00
10: 00 10 bf fe 51 c0 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 f4 1a 00 11
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

sudo lshw -businfo: 获取详细设备信息

  • sudo cat /proc/iomem: 查看各种设备占用的地址空间(包括内存和reversed区域);
  • sudo cat /sys/devices/pci0000:00/[设备编号]/resource: 查看设备配置空间,其中设备编号可以在lspci中看到,例如:sudo cat /sys/devices/pci0000:00/0000:00:07.1/resource.
代码语言:javascript
复制
0x00000000febd6000 0x00000000febd6fff 0x0000000000040200
0x00000000febd0000 0x00000000febd3fff 0x0000000000140204
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000

每行分别表示相应空间的起始地址(start-address)、结束地址(end-address)以及标识位(flags)。

配置空间中的数据起始就是记录设备相关信息的数据,如上面提到的VendorIDsDeviceIDs、和Class Codes字段等...

除了resource文件,还有resource0(MMIO空间)以及resource1(PMIO空间)


引用博客:

  1. https://veritas501.space/2018/06/03/kernel%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/#more
  2. https://eternalsakura13.com/2020/07/11/kernel_qemu/#more
  3. https://eternalsakura13.com/2018/04/13/qemu/
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 安装依赖
  • 内核镜像
    • 下载内核源码:
      • 解压进入
        • 开始编译
        • 磁盘镜像
          • 编译busybox
            • 打包出rootfs.img磁盘镜像
              • 先建立好文件系统:
              • 打包出rootfs.img
          • 用qemu启动
            • 配置启动参数
              • 几种常见的保护
                • 如何向其中添加文件?
                  • 方法1
                  • 方法2
              • GDB调试
                • 查看函数地址
                • 加载第三方ko
                  • 调试ko
                  • qemu pci设备相关
                    • 查看PCI设备信息
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
                    http://www.vxiaotou.com