Linux最早是由芬兰 Linus Torvalds为尝试在英特尔x86架构上提供自由的类Unix操作系统而开发的。该计划开始于1991年,在计划的早期有一些 Minix黑客提供了协助,而如今全球无数程序员正在为该计划无偿提供帮助。
linux 内核官网:https://www.kernel.org/
源码下载地址:https://cdn.kernel.org/pub/linux/kernel/
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
任意一个kernel commit链接,如:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4ff2980b6bd2aa6b4ded3ce3b7c0ccfab29980af
可以直接查看tree 中Makefile:
文件开头就是版本号:
镜像下载地址: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 会对上游内核的特定稳定版本进行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
更换内核:
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
老镜像下载地址: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 |
… | … |
先下载源码,上面已经说过了,不多说。
有很多依赖项,如下命令安装依赖:
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 来编辑我们自定义的编译选项,选项设置为<*>
代表编译进内核,设置为
代表编译成内核模块,**这里要设置成<*>
。**一般要开启调试符号,除此之外根据要分析的漏洞开启必要的编译选项。/
是搜索编译选项关键字。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 中如果有的选项无法配置成*
,只能配置成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
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
可以直接去别人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
在虚拟机配置文件(.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啥的
然后服务器配置调试相关参数:
先修改/etc/default/grub:
GRUB_CMDLINE_LINUX_DEFAULT="quiet 3 kgdbwait kgdboc=ttyS0,115200"
然后更新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/
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:https://github.com/longld/peda
直接下载执行:
echo "source ~/peda/peda.py" >> ~/.gdbinit
#显示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