内核调试环境搭建

您所在的位置:网站首页 ubuntu中下载安装binwalk 内核调试环境搭建

内核调试环境搭建

#内核调试环境搭建| 来源: 网络整理| 查看: 265

内核调试环境搭建 目录 经过测试好用的内核调试环境搭建过程ubuntu和linux版本 查看commit所属的内核版本查看Ubuntu版本号等信息 下载与安装内核 下载内核ubuntu更换内核手动下载并切换到指定源码用apt下载源码使用git下载对应版本 编译并安装linux内核(使用linux-5.15.10版本) 得到Ubuntu内核需要的编译选项常用的内核编译选项查看已有操作系统的编译选项源码编译Ubuntu内核得到有调试信息的源码编译结果查看编译结果 qemu的安装和使用 安装qemu使用 创建文件系统 文件系统的操作编译文件系统用busybox创建文件系统用别人创建的initrd(文件系统) 用文件系统启动内核 启动内核启动调试文件共享 调试技巧 关于符号信息的坑关于源代码安装vmlinux调试信息 调试qemu的Ubuntu虚拟机 创建qemu虚拟机qemu虚拟机和主机互通关闭kaslr(添加内核启动选项)(否则断点断不下来) 调试vmware虚拟机 启用调试问题vmware双机调试(用串口) 编译内核模块其他

参考

利用qemu+gdb在ubuntu下搭建调试kernel的环境 - EwanHai - 博客园 (cnblogs.com)

经过测试好用的内核调试环境搭建过程 1、使用qemu创建Ubuntu虚拟机2、使用git下载对应版本 获取源码3、源码编译Ubuntu内核 得到deb包4、qemu虚拟机和主机互通 传输文件5、Ubuntu更换内核 安装deb包6、关闭kaslr7、调试技巧

在很多情况下,只使用linux内核加文件系统不能得到漏洞依赖的环境

VMware虚拟机调试方便,但是在进行测试时虚拟机会崩溃关机。

用make方式编译安装Ubuntu内核可能会有一些内核选项没有开启,导致Ubuntu黑屏,用官方提供的方式编译成deb包是很稳定的。

用apt安装内核调试信息的方法,调试信息和源码很可能对应不上,自己编译安装是最稳妥的方式。

ubuntu和linux版本

https://blog.csdn.net/Breeze_CAT/article/details/123787636

举例:linux版本5.15.5

目前发布的主版本,增长很缓慢,通常后面的数字比较大了的时候该数字会增长。次版本号,表示稳定的版本号。修订版本号,代表改版本补丁次数,在下一个稳定版本发布之前出现补丁和修复会更新该版本号。非长期维护版本一般20多个。

很久之前内核通过版本号中的第二个数字即B的奇偶来表示稳定版和预发布版。但现在已经取消这个规则,现在预发布版用-rcX来表示如5.17-rc3,X为数字,一般不超过rc8

长期维护版本:5.15、5.10、5.4、4.19、4.14、4.9

查看commit所属的内核版本

kernel/git/torvalds/linux.git - Linux kernel source tree

在这里插入图片描述

查看tree中根目录下的Makefile文件可以看到对应的版本号

查看Ubuntu版本号等信息 cat /etc/issue Ubuntu 20.04.2 LTS \n \l 或 lsb_release -a 命令 No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.2 LTS Release: 20.04 版本 Codename: focal 别名 查看Ubuntu信息 ➜ uname -a Linux tower-virtual-machine 5.15.0-71-generic #78~20.04.1-Ubuntu SMP Wed Apr 19 11:26:48 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux ➜ uname -r 5.15.0-71-generic 内核版本 5.15表示内核稳定版本号,一般ubuntu中最后小修订号都是0,因为ubuntu会自己合入补丁。 71表示由ubuntu进行的第35次修订(合入补丁)。 generic:通用版本,除此之外还可能有服务器版server或老式处理器的i386版等。

Ubuntu对应的别名

在这里插入图片描述

Ubuntu使用的内核 在这里插入图片描述

经过测试,其他版本的内核是可以安装的(如 focal系列的Ubuntu内核代码编译之后可以安装到Ubuntu18主机上)(没有经过更多测试)

下载与安装内核 下载内核

下载ubuntu

https://mirrors.aliyun.com/oldubuntu-releases/releases/20.04.0/?spm=a2c6h.25603864.0.0.10467ff3XF6iMS 阿里云镜像

https://old-releases.ubuntu.com/releases/ Ubuntu官方镜像

下载Ubuntu内核

https://kernel.ubuntu.com/~kernel-ppa/mainline/v5.15.10/ 较粗的deb包

下载linux内核源码

https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/ 版本比较粗

kernel/git/torvalds/linux.git - Linux kernel source tree Linux源码

Kernel/Dev/KernelGitGuide - Ubuntu Wiki Ubuntu内核源码

找到对应的源码:

git tag | grep 4.10.0-19 git checkout ubuntu-4.10.0-19........ ubuntu更换内核 下载对应image和modulessudo dpkg -i *.deb重启

或使用apt更换内核

sudo apt search 'linux-image-5.4.0-81-generic' sudo apt install 'linux-image-5.4.0-81-generic' 更新并重启 sudo update-initramfs -u -k all sudo update-grub reboot 安装新版本内核头文件,用于开发编译 sudo apt install linux-headers-$(uname -r)

开机按esc选择选高级,找到对应版本内核启动

如果进不去需要修改引导启动信息:

修改文件/etc/default/grub

注释掉GRUB_TIMEOUT_STYLE=hidden

修改GRUB_TIMEOUT=0为GRUB_TIMEOUT=10

执行sudo update-grub启用修改

reboot 重启时会进入引导界面等待选择内核

手动下载并切换到指定源码

https://bbs.kanxue.com/thread-249192.htm

在https://wiki.ubuntu.com/Kernel/Dev/KernelGitGuide选择对应的Ubuntu系列

查看主机的Ubuntu系列(这里需要下载被调试主机的源码)

➜ CVE-2021-3493 git:(main) ✗ cat /etc/apt/sources.list deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse

如Ubuntu20可以下载

git clone https://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/focal git tag | grep 4.10.0-19 git checkout ubuntu-4.10.0-19........

gdb中指定源代码路径,或把source文件直接拷贝到gdb检测源码的目录(如/build/linux-kcrkKx/linux-5.4.0)

set substitute-path PATH1 PATH2,PATH1是vmlinux中的路径信息,PATH2是source code存放的真实路径。 用apt下载源码 apt source linux-image-unsigned-5.11.0-44-generic # 版本号根据需求更改,可以用apt search搜索

ubuntu 内核git:https://kernel.ubuntu.com/git/ubuntu/

使用git下载对应版本

可以查看分支名https://kernel.ubuntu.com/git/ubuntu/ubuntu-focal.git/refs/tags

git clone git://kernel.ubuntu.com/ubuntu/ubuntu-focal.git -b Ubuntu-hwe-5.13-5.13.0-35.40_20.04.1 --depth 1 编译并安装linux内核(使用linux-5.15.10版本) make ARCH=x86_64 x86_64_defconfig make ARCH=x86_64 menuconfig 配置 Kernel-hacking -> Compile-time checks and compiler options Compile the kernel with debug info —> Provide GDB scripts for kernel debugging 关闭 Reduce debugging information make -j8 sudo make modules_install 安装模块 sudo make install 安装 sudo update-initramfs -c -k 5.3.10 启用(数字为版本号) sudo update-grub 更新grub reboot

也可以使用docker编译https://registry.hub.docker.com/r/chenaotian/kernelcompile(编译5.x)

docker run -ti --rm -h kc --name kc -v D:/share:/work chenaotian/kernelcompile:latest /bin/bash docker exec -it kc /bin/bash 得到Ubuntu内核需要的编译选项 常用的内核编译选项 # 设置调试符号 CONFIG_DEBUG_INFO=y # fuse 开启,一些漏洞利用会用到 CONFIG_FUSE_FS=y # VIPC 开启,可以使用msg系列 CONFIG_SYSVIPC=y CONFIG_SYSVIPC_SYSCTL=y CONFIG_SYSVIPC_COMPAT=y CONFIG_CHECKPOINT_RESTORE=y # 设置这个才能正确调用msg 里的copy 系列函数 查看已有操作系统的编译选项 cat /usr/src/linux-headers-`uname -r`/.config 或 cat /boot/config-`uname -r` 源码编译Ubuntu内核

Kernel/BuildYourOwnKernel - Ubuntu Wiki

安装依赖

sudo apt-get build-dep linux sudo apt-get install libncurses-dev gawk flex bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf llvm

编译

LANG=C fakeroot debian/rules clean LANG=C fakeroot debian/rules binary-headers binary-generic binary-perarch 快速编译 # if you need linux-tools or lowlatency kernel, run instead: 会多出来更多东西 LANG=C fakeroot debian/rules binary 得到有调试信息的源码编译结果 sudo apt-get install pkg-config-dbgsym LANG=C fakeroot debian/rules clean LANG=C fakeroot debian/rules binary-headers binary-generic binary-perarch skipdbg=false 查看编译结果

System.map 文件可以查看一些关键函数有没有

cat System.map |grep function_name

查看字符串

strings vmlinux |grep function_name qemu的安装和使用 安装qemu sudo apt-get install qemu qemu-system 使用

常用的启动脚本

#! /bin/sh cd ./rootfs find . | cpio -o --format=newc > ../rootfs.img cd ../ qemu-system-x86_64 \ -m 512M \ -kernel ./bzImage \ -initrd ./rootfs.img \ -nographic \ -append "console=ttyS0 root=/dev/sda rw nokaslr quiet" \ -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \ -cpu kvm64,+smep,+smap \ -gdb tcp::10086 创建文件系统 文件系统的操作

解包和打包

cpio -idmv ../rootfs.img #打包cpio

有可能在外层更进行了一次gzip压缩,使用binwalk可以可以解包

binwalk -Me ./rootfs.img

如果一定需要动态链接的exp,偷懒方法就是,ldd 查看exp 需要的动态库,然后将ld-linux-x86-64.so.2 和其他依赖的so全部拷贝到文件系统中,qemu 启动后,用LD_LIBRARY_PATH 来运行

将其依赖的so全部拷贝到一个文件夹内然后放入rootfs中制作成initrd.img,然后启动qemu,按照如下方法运行

cp ...so ./rootfs/exp #将so 拷贝到制作initrd.img的目录中 cd ./rootfs find . | cpio -o --format=newc > ../rootfs.img #制作initrd.img cd ../ ./boot.sh #启动qemu # qemu 启动后 cd /expdir export LD_LIBRARY_PATH=`pwd` ./ld-linux-x86-64.so.2 ./exploit 编译文件系统

使用busybox构建文件系统比较复杂,用buildroot相对简单

git clone git://git.buildroot.net/buildroot cd buildroot make menuconfig 在跳出的UI界面依次选择

Target options

Target Architecture (x86_64)

在x86_64上按空格键,以选择该选项

再返回与Target options同一级的界面

选择Filesystem images

在ext2/3/4 root filesystem按Y,并进入ext2/3/4 variant (ext4)选择ext4

make -j4 用busybox创建文件系统 wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2 tar -jxvf busybox-1.35.0.tar.bz2 cd busybox-1.35.0 make defconfig make menuconfig

选择settings中选择静态链接

在这里插入图片描述

make -j4 sudo make install

make install会把文件拷贝到当前目录下的 _install目录

拷贝文件,创建init

mkdir ramdisk cd ramdisk cp -r ../busy-1.25.0/_install/* . cd ramdisk ln -s bin/busybox init

设置启动程序,init程序首先会访问etc/inittab文件,以获取开机要启动的程序列表,因此我们得编写一个inittab

cd etc vim inittab ::sysinit:/etc/init.d/rcS ::askfirst:-/bin/sh ::restart:/sbin/init ::ctrlaltdel:/sbin/reboot ::shutdown:/bin/umount -a -r ::shutdown:/sbin/swapoff -a chmod +x etc/inittab

系统初始化寻找etc/init.d/rcS,需要创建,内容添加

#!/bin/sh mount proc mount -o remount,rw / mount -a clear echo "My Tiny Linux Start :D ......"

并加权限

chmod +x rcS

rcS中,mount -a 是自动挂载 /etc/fstab 中的内容,因此我们需要一个fstab文件,以设置需要挂载的系统, 创建文件并添加内容

proc /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 devtmpfs /dev devtmpfs defaults 0 0

最后把文件压缩成镜像

cd ramdisk find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img cpio --null或-0接受新增列控制字符,通常配合find指令的"-print0"参数使用。 -o或--create  执行copy-out模式,建立备份档。 -v或--verbose  详细显示指令的执行过程。 newc 新型 (SVR4) 跨平台格式, 支持大于 65536 i节点的文件系统,一般 制作 ramdisk 就用 这个 格式 -9 表示压缩率 用别人创建的initrd(文件系统) 用文件系统启动内核 启动内核

使用buildroot编译的镜像

qemu-system-x86_64 -kernel bzImage -boot c -m 1024 -hda rootfs.ext2 -append "root=/dev/sda rw console=ttyS0, 115200 acpi=off nokaslr" -serial stdio -display none -s -S

使用busybox构建的镜像启动命令

# 在A Terminal中运行以下命令 qemu-system-x86_64 -kernel bzImage -boot c -m 1024 -initrd initramfs.img -append "root=/dev/sda rw console=ttyS0, 115200 acpi=off nokaslr" -serial stdio -display none -s -S

如果需要调试内核的kvm模块,则向上述命令添加 -enable-kvm

启动调试 gdb vmlinux target remote localhost:1234 文件共享

把自己的文件放入文件系统

如果使用busybox创建文件系统,直接把文件拷贝到对应目录,执行命令把文件打包成镜像 find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img 如果使用buildroot构建镜像,添加自定义文件到文件系统System configuration菜单下选择 Custom scripts to run before creating filesystem images写入命令,把文件拷贝到对应目录output/target目录下 ,在测试的时候报错命令出错。编译过之后,直接用手动拷贝文件到output/target然后再执行一次make -j4 得到的镜像内部有需要的文件 调试技巧 关于符号信息的坑

vmlinux文件可能不包含一些符号,如图

在这里插入图片描述

原因是ovlerlay文件系统被编译成了内核模块文件,需要手动添加

gef➤ add-symbol-file ./fs/overlayfs/overlay.ko 0xffffffffc04ee000

查看内核模块加载地址(内核模块被加载了之后才能看到,可能需要先执行一次相关的命令或执行相关的poc)

cat /proc/modules 关于源代码

用docker编译的内核会从docker中的路径寻找二进制源码,在调试时会出现找不到源码的情况

gef➤ list 46 in /work/ubuntu-focal/arch/x86/include/asm/irqflags.h

用set命令重定位源码目录即可

set substitute-path /work/ubuntu-focal /home/tower/aiwencode/vul/CVE-2023-0386/ubuntu-focal 安装vmlinux调试信息

Ubuntu内核默认不包含调试信息

安装内核调试信息

https://developer.aliyun.com/article/899339

获取Ubuntu内核调试信息

https://bbs.kanxue.com/thread-249192.htm

cat /boot/config-uname -r| grep -i "GDB"查看当前内核是否支持KGDB 增加符号对应的源文件 codename=$(lsb_release -c | awk '{print $2}') sudo tee /etc/apt/sources.list.d/ddebs.list void *device_buf; size_t device_buf_len; }babydev_struct; module_init(kernel_module_init); module_exit(kernel_module_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("arttnba3"); static int __init kernel_module_init(void) { spin_lock_init(&spin); printk(KERN_INFO "[arttnba3_TestModule:] Module loaded. Start to register device...\n"); major_num = register_chrdev(0, DEVICE_NAME, &a3_module_fo); if(major_num unregister_chrdev(major_num, DEVICE_NAME); printk(KERN_INFO "[arttnba3_TestModule:] Failed to register class device!\n"); return PTR_ERR(module_class); } printk(KERN_INFO "[arttnba3_TestModule:] Class device register complete.\n"); module_device = device_create(module_class, NULL, MKDEV(major_num, 0), NULL, DEVICE_NAME); if(IS_ERR(module_device)) { class_destroy(module_class); unregister_chrdev(major_num, DEVICE_NAME); printk(KERN_INFO "[arttnba3_TestModule:] Failed to create the device!\n"); return PTR_ERR(module_device); } printk(KERN_INFO "[arttnba3_TestModule:] Module register complete.\n"); return 0; } static void __exit kernel_module_exit(void) { printk(KERN_INFO "[arttnba3_TestModule:] Start to clean up the module.\n"); device_destroy(module_class, MKDEV(major_num, 0)); class_destroy(module_class); unregister_chrdev(major_num, DEVICE_NAME); printk(KERN_INFO "[arttnba3_TestModule:] Module clean up complete. See you next time.\n"); } static long a3_module_ioctl(struct file * __file, unsigned int cmd, long unsigned int param) { if (cmd == 65537) { kfree(babydev_struct.device_buf); babydev_struct.device_buf = kmalloc(param, GFP_ATOMIC); babydev_struct.device_buf_len = param; printk(KERN_INFO "alloc done\n"); return 0; } else { printk(KERN_INFO "default arg is %ld\n", param); return -22; } } static int a3_module_open(struct inode * __inode, struct file * __file) { babydev_struct.device_buf = kmalloc(0x40, GFP_ATOMIC); babydev_struct.device_buf_len = 0x40; printk(KERN_INFO "device open\n"); return 0; } static int a3_module_release(struct inode * __inode, struct file * __file) { kfree(babydev_struct.device_buf); printk(KERN_INFO "device release\n"); return 0; } static ssize_t a3_module_read(struct file * __file, char __user * user_buf, size_t size, loff_t * __loff) { size_t result; if (!babydev_struct.device_buf) return -1LL; result = -2LL; if (babydev_struct.device_buf_len > size) { copy_to_user(user_buf, babydev_struct.device_buf, size); result = size; } return result; } static ssize_t a3_module_write(struct file * __file, const char __user * user_buf, size_t size, loff_t * __loff) { size_t result; if (!babydev_struct.device_buf ) return -1LL; result = -2LL; if ( babydev_struct.device_buf_len > size) { copy_from_user(babydev_struct.device_buf, user_buf, size); result = size; } return result; } 其他

查看内核支持的选项

grep CONFIG_PID_NS /boot/config-$(uname -r)

查看内核崩溃日志

/proc/last_kmsg /sys/fs/pstore/console-ramoops


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3