[kernel]linux内核基础: 版本、源码、编译与调试

linux kernel 安全基础(版本、源码、编译与调试)

内核基础知识

Linux最早是由芬兰 Linus Torvalds为尝试在英特尔x86架构上提供自由的类Unix操作系统而开发的。该计划开始于1991年,在计划的早期有一些 Minix黑客提供了协助,而如今全球无数程序员正在为该计划无偿提供帮助。

linux 内核官网:https://www.kernel.org/

源码下载地址:https://cdn.kernel.org/pub/linux/kernel/

内核版本号与各发行版版本管理

linux内核版本

linux 版本号由三个数字组成A.B.C,如5.15.5,分别含义:

A:目前发布的主版本,增长很缓慢,通常后面的数字比较大了的时候该数字会增长。

B:次版本号,表示稳定的版本号。

C:修订版本号,代表改版本补丁次数,在下一个稳定版本发布之前出现补丁和修复会更新该版本号。非长期维护版本一般20多个。

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

长期维护版:linux 会长期维护几个版本,每次出现重要的错误修复都会对该版本打补丁,无论是否有更新的版本:

VersionMaintainerReleasedProjected EOL
5.15Greg Kroah-Hartman & Sasha Levin2021-10-31Oct, 2023
5.10Greg Kroah-Hartman & Sasha Levin2020-12-13Dec, 2026
5.4Greg Kroah-Hartman & Sasha Levin2019-11-24Dec, 2025
4.19Greg Kroah-Hartman & Sasha Levin2018-10-22Dec, 2024
4.14Greg Kroah-Hartman & Sasha Levin2017-11-12Jan, 2024
4.9Greg Kroah-Hartman & Sasha Levin2016-12-11Jan, 2023

linux发行商也会维护自己的长期维护版内核。不同发行商的内核版本号含义有细微不同,下面介绍一些ubuntu 的。

参考:https://www.kernel.org/category/releases.html

查看任意commit 所属内核版本

任意一个kernel commit链接,如:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4ff2980b6bd2aa6b4ded3ce3b7c0ccfab29980af

可以直接查看tree 中Makefile:

在这里插入图片描述

文件开头就是版本号:

在这里插入图片描述

ubuntu版本

镜像下载与老镜像下载

镜像下载地址:https://releases.ubuntu.com/

老镜像下载地址:https://old-releases.ubuntu.com/releases

版本号查看与含义

查看ubuntu 本身的版本:

cat /etc/issue
# result
Ubuntu 20.04.2 LTS \n \l
# or
lsb_release -a
# result
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.2 LTS
Release:        20.04
Codename:       focal

ubuntu 版本代号:

代号版本
trusty (Trusty Tahr 可靠的塔尔羊)14.04
xenial (Xenial Xerus 好客的非洲地松鼠)16.04
bionic (Bionic Beaver仿生海狸)18.04
focal (Focal Fossa 专注狸猫??)20.04
hirsute (Hirsute Hippo 长毛河马)21.04
impish (Impish Indri 顽皮狐猴)21.10

查看ubuntu 内核版本:

uname -r
# result
5.13.0-35-generic

ubuntu内核版本号格式形如5.13.0-35-generic,其中:

5.13.0:代表linux 内核稳定版本号5.13,一般ubuntu中最后小修订号都是0,因为ubuntu会自己合入补丁。

35:由ubuntu进行的第35次修订(合入补丁)。

generic:通用版本,除此之外还可能有服务器版server或老式处理器的i386版等。

ubuntu内核和linux 官方内核区别

ubuntu 会对上游内核的特定稳定版本进行rebase,并对该版本进行补丁管理,但由于上游linux 内核版本过一阵就会进入下一个版本,而ubuntu 通常不会,ubuntu 通常会自己去合入补丁。换句话说,ubuntu 只是松散的机遇上游稳定版本维护ubuntu 版本,必须查看更新日志来确定ubuntu 更新的功能。

参考:https://wiki.ubuntu.com/Kernel/FAQ#Kernel.2FFAQ.2FGeneralVersionMeaning.What_does_a_specific_Ubuntu_kernel_version_number_mean.3F

ubuntu 更换内核以及源码下载

更换内核

apt-get install linux-image-5.11.0-44-generic # 版本号根据需求更改
#下载新版本内核一般直接生效,不生效如下操作:
grep menuentry /boot/grub/grub.cfg
vim /etc/default/grub
#修改 GRUB_DEFAULT 选项为上面结果中想要启动内核的下标
update-grub
#如果不生效的话(一般是下载旧版本内核)则直接进入/boot 目录将之前的内核相关文件(带之前内核编号的文件)全部删掉,然后启动时候报找不到内核,然后手动选择内核启动也可以

获得ubuntu内核源码

可以直接apt source 获得:

apt source linux-image-unsigned-5.11.0-44-generic # 版本号根据需求更改

或前往ubuntu 内核git:https://kernel.ubuntu.com/git/ubuntu/

git下载对应版本:

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

网很卡的话,使用clash:

clash 订阅&下载:https://portal.wallless.xyz/

clash 配置TAP虚拟网卡:https://uzbox.com/tech/clash-atp.html

ubuntu 常见版本对应内核版本

老镜像下载地址:http://old-releases.ubuntu.com/releases/

镜像名默认内核版本md5后6
ubuntu-20.04-desktop-amd64.iso5.4.0-26-generic35f5a0
ubuntu-20.04-live-server-amd64.iso5.4.0-26-generic35f5a0
ubuntu-20.04.1-desktop-amd64.iso5.4.0-42-generic36a5aa
ubuntu-20.04.1-live-server-amd64.iso5.4.0-42-generic36a5aa
ubuntu-20.04.2.0-desktop-amd64.iso5.8.0-43-generic5f0820
ubuntu-20.04.2-live-server-amd64.iso5.4.0-65-genericd388b4
ubuntu-20.04.3-desktop-amd64.iso5.11.0-27-generic
ubuntu-20.04.3-live-server-amd64.iso5.4.0-81-generic33be0a
ubuntu-21.04-beta-desktop-amd64.iso5.11.0-13-generic4687e5
ubuntu-21.04-beta-live-server-amd64.iso5.11.0-13-generic4687e5
ubuntu-21.10-beta-desktop-amd64.iso5.13.0-16-generic4442a0
ubuntu-21.10-beta-live-server-amd64.iso5.13.0-16-generic4442a0

kernel 编译

编译准备

先下载源码,上面已经说过了,不多说。

有很多依赖项,如下命令安装依赖:

apt-get build-dep linux linux-image-5.11.0-44-generic

安装之后也不一定全,make 的时候根据报错依次安装就行。

如下docker 环境 可以编译kernel 5.x 版本内核

https://registry.hub.docker.com/r/chenaotian/kernelcompile

docker run -ti --rm -h kc --name kc -v D:/share:/work chenaotian/kernelcompile:latest /bin/bash
docker exec -it kc /bin/bash
查看已有操作系统的编译选项
cat /usr/src/linux-headers-`uname -r`/.config 
#或
cat /boot/config-`uname -r`

配置和编译

debian 体系的内核编译可以参考:https://wiki.ubuntu.com/Kernel/BuildYourOwnKernel

改文章的方法是编译内核deb包,也就是跟apt-get 安装的内核同款的编译方式。我们不需要完整编译出他的东西,我们只对内核本身感兴趣。所以按照他提供的部分方法进行就可以(需要按照上面“获得ubuntu内核源码”中的git方法下载代码):

LANG=C fakeroot debian/rules clean
# 下面这一步我们只需要构建binary-generic,因为内核在这里,不需要其他的
LANG=C fakeroot debian/rules binary-generic

开始编译之后,我们不需要编译出完整的deb包,只需要一个内核就行,所以看到如下输出可以直接ctrl+c结束:

在这里插入图片描述

拷贝图中红框的命令bzImage 前的部分:

make ARCH=x86 CROSS_COMPILE= KERNELVERSION=5.13.0-35-generic CONFIG_DEBUG_SECTION_MISMATCH=y KBUILD_BUILD_VERSION="40~20.04.1" LOCALVERSION= localver-extra= CFLAGS_MODULE="-DPKG_ABI=35" PYTHON=/usr/bin/python3 O=/tmp/aa/ubuntu-focal/debian/build/build-generic -j4 

接下来使用menuconfig 来编辑我们自定义的编译选项,选项设置为<*>代表编译进内核,设置为<M>代表编译成内核模块,**这里要设置成<*>。**一般要开启调试符号,除此之外根据要分析的漏洞开启必要的编译选项。/ 是搜索编译选项关键字。menuconfig结束之后,通过修改.config文件也可以修改编译选项。如果有互相依赖的情况,后续编译的时候会询问,问题不大,menuconfig 命令如下(上面拷贝的命令后加menuconfig 即可):

make ARCH=x86 CROSS_COMPILE= KERNELVERSION=5.13.0-35-generic CONFIG_DEBUG_SECTION_MISMATCH=y KBUILD_BUILD_VERSION="40~20.04.1" LOCALVERSION= localver-extra= CFLAGS_MODULE="-DPKG_ABI=35" PYTHON=/usr/bin/python3 O=/tmp/aa/ubuntu-focal/debian/build/build-generic -j4 menuconfig

添加调试符号:

Kernel hacking ---> Compile-time checks and compiler options  
[*] Compile the kernel with debug info             

一些可能需要的编译选项(踩过坑的):

# 设置调试符号
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 系列函数

最后编译出内核(上面拷贝的命令后加bzImage):

make ARCH=x86 CROSS_COMPILE= KERNELVERSION=5.13.0-35-generic CONFIG_DEBUG_SECTION_MISMATCH=y KBUILD_BUILD_VERSION="40~20.04.1" LOCALVERSION= localver-extra= CFLAGS_MODULE="-DPKG_ABI=35" PYTHON=/usr/bin/python3 O=/tmp/aa/ubuntu-focal/debian/build/build-generic -j4 bzImage

O= 后面的目录是编译结果目录,.config 和编译结果 和System.map 都在这个目录之中。

menuconfig 依赖选项

menuconfig 中如果有的选项无法配置成*,只能配置成M的话,可能是依赖没有满足,如:

在这里插入图片描述

depends 代表的是依赖,如果想要将某个编译选项配置成y(而不是m),那么根据这个依赖的逻辑表达式他所有依赖的选项都要配置成y。建议在menuconfig 上操作,如果直接改.config 中为y,大概率会编译失败。

这里我直接把NF_CONNTRACK 取消配置,则直接可以将OPENVSWITCH 配置成y,但问题是我需要NF_CONNTRACK ,所以就要满足整个表达式,也就是后面的所有都要配置成y。而他们自己还是可能有依赖的东西,都要配置成y 才行。

如果这种配置选项,无法通过按数字键跳转:

在这里插入图片描述

那么说明现在他是自动配置成m 的,看到Selected by[m],说明下面选项决定了它被配置成m:

在这里插入图片描述

一般要把下面的选项改成y 才行,而他们自己可能也有依赖,所以,都一起改成y。

检查编译结果

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

cat System.map |grep function_name

在这里插入图片描述

strings 查看一些字符串

strings vmlinux |grep function_name

在这里插入图片描述

gdb 打开查看符号,断点或list

gdb vmlinux
b function_name
list function_name

在这里插入图片描述

kernel调试

qemu 调试

安装qemu(自己编译)

qemu 源码 : https://www.qemu.org/download/#source

wget https://download.qemu.org/qemu-6.2.0.tar.xz
tar xvJf qemu-6.2.0.tar.xz
cd qemu-6.2.0
./configure
make

可能的一些依赖:

apt-get install ninja-build
apt-get install libpixman-1-dev
制作启动组件initrd

可以直接去别人github 拷一个initrd过来用,也可以自己做。做一个简易版initrd的主要工作就是静态编译一个busybox。这种initrd 里面没有lib 啥的,分析漏洞的exp 和poc 只能静态编译来测试,如果想要做一个比较全的initrd 比较麻烦。

建议随便找一个别人分析漏洞的环境把里面的initrd 拷贝过来就行。比如:

https://github.com/chenaotian/CVE-2022-0185/tree/main/qemu

解包和打包cpio:

cpio -idmv < ../rootfs.img #解包cpio
find . | cpio -o --format=newc > ../rootfs.img #打包cpio

如果一定需要动态链接的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

在这里插入图片描述

kill 命令

ps -ef |grep qemu | grep -v grep | awk '{print $2}' | xargs kill -9
启动脚本

qemu 的启动脚本boot.sh 如下,方便每次修改exp直接启动,直接在boot.sh中生成initrd.img:

#! /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

vmware 调试内核

普通调试

在虚拟机配置文件(.vmx)后加:

debugStub.listen.guest64="1"

然后直接

gdb
target remote :8864

反正我是没成功,据说在开了Hyper-V的机器上会失败,但我关了Hyper-V也没成功(具体表现为,能gdb挂上,一设置断点就崩溃,有时候还会损坏虚拟机),所以虽然看起来方便,但着实没用。

参考:https://www.cnblogs.com/yxysuanfa/p/6844459.html

双机调试
  • 被调试机:服务机

  • 调试机:客户机

先在虚拟机设备里删除打印机,然后添加串行端口。使用命名串行端口,服务器和客户端都写这个:

//./pipe/com_1

设置波特率和测试串口通信:

#服务端设置波特率
stty -F /dev/ttyS0 115200
#测试 客户端输入,服务端接受
echo "haha" > /dev/ttyS0
#如果ttyS0不行就试试ttyS1啥的

然后服务器配置调试相关参数:

  1. 先修改/etc/default/grub:

    GRUB_CMDLINE_LINUX_DEFAULT="quiet 3 kgdbwait kgdboc=ttyS0,115200"
    
  2. 然后更新grub,执行:

    update-grub
    

    然后重启,之后就可以尝试调试了。

gdb挂载命令:

set arch i386:x86-64:intel 
set remotebaud 115200
target remote /dev/ttyS0

如果跑起来需要中断的话,在服务机执行:

echo g > /proc/sysrq-trigger

参考:http://taowusheng.cn/2020/03/30/20200330%20%E5%8F%8C%E6%9C%BA%E8%B0%83%E8%AF%95Linux%E5%86%85%E6%A0%B8/

常用gdb插件

pwndbg+pwngdb

pwndbg:https://github.com/pwndbg/pwndbg

pwngdb:https://github.com/scwuaptx/Pwngdb

先安装pwndbg,直接下载之后运行./setup.sh即可,中途可能会因为git 断开,执行git init,然后继续执行就行。

然后安装pwngdb,直接下载解压,然后编辑~/.gdbinit:

source /root/pwndbg-dev/gdbinit.py
source /root/Pwngdb-master/pwngdb.py
source /root/Pwngdb-master/angelheap/gdbinit.py

define hook-run
python
import angelheap
angelheap.init_angelheap()
end
end
peda

peda:https://github.com/longld/peda

直接下载执行:

echo "source ~/peda/peda.py" >> ~/.gdbinit

一些gdb命令

#显示xxx 结构体的成员大小和偏移(需要符号)
pt/o struct xxx 
#跳过断点1 117次,用来断正好溢出的fsconfig
ignore 1 117

参考

linux kernel官网:https://www.kernel.org/

linux kernel 发行简介:https://www.kernel.org/category/releases.html

ubuntu kernel FAQ:https://wiki.ubuntu.com/Kernel/FAQ#Kernel.2FFAQ.2FGeneralVersionMeaning.What_does_a_specific_Ubuntu_kernel_version_number_mean.3F

内核编译:https://wiki.ubuntu.com/Kernel/BuildYourOwnKernel

普通vmware 调试:https://www.cnblogs.com/yxysuanfa/p/6844459.html

双机调试:http://taowusheng.cn/2020/03/30/20200330%20%E5%8F%8C%E6%9C%BA%E8%B0%83%E8%AF%95Linux%E5%86%85%E6%A0%B8/

问了韬神一堆问题:https://github.com/veritas501

编译 CentOS 8.5 的 Linux 内核源码需要以下步骤: 1. 下载内核源码: 在 CentOS 官方网站或者 Linux 内核官方网站上下载 CentOS 8.5 对应的内核源码包。你可以使用 `wget` 命令来下载,例如: ``` wget https://www.kernel.org/pub/linux/kernel/v5.x/linux-5.15.tar.gz ``` 2. 解压源码包: 使用 `tar` 命令将下载的源码包解压,例如: ``` tar -xzvf linux-5.15.tar.gz ``` 3. 安装编译依赖: 安装编译内核所需的依赖包,例如: ``` sudo yum install gcc make ncurses-devel openssl-devel elfutils-libelf-devel ``` 4. 配置内核选项: 进入解压后的源码目录,并执行以下命令以配置内核选项: ``` cd linux-5.15 make menuconfig ``` 在文本界面的配置界面中,可以修改内核选项,例如启用或禁用某些功能、添加或删除驱动程序等。完成后保存并退出。 5. 编译内核: 执行以下命令开始编译内核: ``` make ``` 这将花费一些时间进行编译,可以使用 `-j` 参数指定并发任务数量,例如 `make -j4`。 6. 安装内核编译完成后,执行以下命令安装内核: ``` sudo make install ``` 7. 配置引导管理器: 根据你使用的引导管理器(如 GRUB 或 LILO),更新引导配置文件以引导新内核。 8. 重新启动: 完成上述步骤后,重新启动计算机,选择新编译内核进行启动。 请注意,编译内核需要一定的编译知识和经验,如果你对此不太熟悉,建议在测试环境中进行操作,并备份重要数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值