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

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

文章目录

  • linux kernel 安全基础(版本、源码、编译与调试)
    • 内核基础知识
    • 内核版本号与各发行版版本管理
      • linux内核版本
        • 查看任意commit 所属内核版本
      • ubuntu版本
        • 镜像下载与老镜像下载
        • 版本号查看与含义
        • ubuntu内核和linux 官方内核区别
        • ubuntu 更换内核以及源码下载
        • ubuntu 常见版本对应内核版本
    • kernel 编译
      • 编译准备
        • 查看已有操作系统的编译选项
      • 配置和编译
        • menuconfig 依赖选项
      • 检查编译结果
    • kernel调试
      • qemu 调试
        • 安装qemu(自己编译)
        • 制作启动组件initrd
        • 启动脚本
      • vmware 调试内核
        • 普通调试
        • 双机调试
      • 常用gdb插件
        • pwndbg+pwngdb
        • peda
      • 一些gdb命令
    • 参考

内核基础知识

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 会长期维护几个版本,每次出现重要的错误修复都会对该版本打补丁,无论是否有更新的版本:

Version Maintainer Released Projected EOL
5.15 Greg Kroah-Hartman & Sasha Levin 2021-10-31 Oct, 2023
5.10 Greg Kroah-Hartman & Sasha Levin 2020-12-13 Dec, 2026
5.4 Greg Kroah-Hartman & Sasha Levin 2019-11-24 Dec, 2025
4.19 Greg Kroah-Hartman & Sasha Levin 2018-10-22 Dec, 2024
4.14 Greg Kroah-Hartman & Sasha Levin 2017-11-12 Jan, 2024
4.9 Greg Kroah-Hartman & Sasha Levin 2016-12-11 Jan, 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:

[kernel]linux内核基础: 版本、源码、编译与调试_第1张图片

文件开头就是版本号:

[kernel]linux内核基础: 版本、源码、编译与调试_第2张图片

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.iso 5.4.0-26-generic 35f5a0
ubuntu-20.04-live-server-amd64.iso 5.4.0-26-generic 35f5a0
ubuntu-20.04.1-desktop-amd64.iso 5.4.0-42-generic 36a5aa
ubuntu-20.04.1-live-server-amd64.iso 5.4.0-42-generic 36a5aa
ubuntu-20.04.2.0-desktop-amd64.iso 5.8.0-43-generic 5f0820
ubuntu-20.04.2-live-server-amd64.iso 5.4.0-65-generic d388b4
ubuntu-20.04.3-desktop-amd64.iso 5.11.0-27-generic
ubuntu-20.04.3-live-server-amd64.iso 5.4.0-81-generic 33be0a
ubuntu-21.04-beta-desktop-amd64.iso 5.11.0-13-generic 4687e5
ubuntu-21.04-beta-live-server-amd64.iso 5.11.0-13-generic 4687e5
ubuntu-21.10-beta-desktop-amd64.iso 5.13.0-16-generic 4442a0
ubuntu-21.10-beta-live-server-amd64.iso 5.13.0-16-generic 4442a0

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结束:

[kernel]linux内核基础: 版本、源码、编译与调试_第3张图片

拷贝图中红框的命令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 来编辑我们自定义的编译选项,选项设置为<*>代表编译进内核,设置为代表编译成内核模块,**这里要设置成<*>。**一般要开启调试符号,除此之外根据要分析的漏洞开启必要的编译选项。/ 是搜索编译选项关键字。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 才行。

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

[kernel]linux内核基础: 版本、源码、编译与调试_第4张图片

那么说明现在他是自动配置成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]linux内核基础: 版本、源码、编译与调试_第5张图片

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 来运行:

[kernel]linux内核基础: 版本、源码、编译与调试_第6张图片

将上面那些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

[kernel]linux内核基础: 版本、源码、编译与调试_第7张图片

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

你可能感兴趣的:(#,linux,kernel,网络安全,linux,内核,kernel)