Syzkaller实战教程2:运行环境配置+实例运行
一.环境配置流程
笔者的宿主机是一台windows的情况,我需要先建立一个ubantu的虚拟机A,并在这个虚拟机A中完成相关的测试准备,如编译Linux内核,编译syzkaller生成可执行的二进制文件,配置syzkaller等。再在虚拟机A中使用QEMU建立一个虚拟机B,用来生成syzkaller的测试用例并进行模糊测试。
1.配置syzkaller环境
创建虚拟机:安装ubuntu20.04
-
vmware虚拟机:Ubuntu 20.04
-
请确保有足够的空间(40 G 空间可以,但是不要放在机械硬盘里面,虚拟机会很慢)
1.流程参考韩顺平linux教程里的安装流程,桌面版的镜像文件也在其中。通过VM ware 创建即可。
2.以上全部为:ubuntu2004
3.处理器数量参考宿主机数量,选最大即可
4.ubuntu默认没有创建root用户,用以下命令创建root
sudo passwd root
“输入当前用户密码以验证”
“输入root用户的密码”
5.虚拟机ubuntu和windows主机之间无法复制粘贴问题
5.1安装ubuntu相关常用依赖
sudo apt update# 安装VMware tools
sudo apt-get autoremove open-vm-tools #卸载已有的工具
sudo apt-get install open-vm-tools #安装open-vm-tools
sudo apt-get install open-vm-tools-desktop #安装open-vm-tools-desktop
# 重启
shutdown -r nowsudo apt install make gcc flex bison libncurses-dev libelf-dev libssl-dev
6.可选操作:
①修改apt的源文件,将美国服务器源地址修改为国内镜像源
②安装ssh
sudo apt-get install openssh-server #安装ssh服务端和客户端
service sshd restart #启动ssh
netstat -anp | more #查看端口连接,此时22端口处于“listen”,即成功启动,
#注意ubuntu也未安装netstat指令,需要提前安装"apt install net-tools"
1.1 安装清单
-
Go 编译器和 syzkaller 本身:syzkaller 采用GO语言编写,因此需要安装对应版本GO编译器对其进行编译;
-
具有覆盖率支持的 C 编译器:有版本要求;
-
开启了覆盖率收集功能的 Linux 内核:在虚拟机A中对内核完成编译
-
虚拟机或物理设备:宿主机(windows上)利用VM ware创建虚拟机A
1.2 创建目录
在用户ubuntu2004下的/home/ubuntu2004/路径下创建go_projects文件夹
cd ~
mkdir -p go_projects cd go_projects
# 安装常用依赖,g++对于编译很重要,用于生成可执行文件
sudo apt-get install debootstrap
sudo apt install qemu-kvm
sudo apt-get install subversion
sudo apt-get install git
sudo apt-get install make
sudo apt-get install qemu
sudo apt install libssl-dev libelf-dev
sudo apt-get install flex bison libc6-dev libc6-dev-i386 linux-libc-dev linux-libc-dev:i386 libgmp3-dev libmpfr-dev libmpc-dev
sudo apt-get install g++
sudo apt-get install build-essential
sudo apt install golang-go
sudo apt install gcc
sudo apt-get install tree
然后在 go_projects
目录中执行下载、解压和配置 Go 的命令。
1.3 安装GO语言编译器
1.方法一
sudo snap install go
go version
2.另一种方法,配置全局环境(若在上一步已经能够成功查看go版本,且满足24-9月版本syzkaller需要的版本,则不需要再在此处重复安装go)
wget https://dl.google.com/go/go1.22.1.linux-amd64.tar.gz
tar -xf go1.22.1.linux-amd64.tar.gz
mv go goroot
# 将解压自动创建的go文件夹修改为goroot# 后面这两条命令建议设置到~/.bashrc文件中,并通过source ~/.bashrc命令更新配置
# 但此处仍需要在go_project路径下运行一次
vim ~/.bashrc
export GOROOT=`pwd`/goroot
export PATH=$GOROOT/bin:$PATH
更新配置如下:
#编辑 ~/.bashrc:
nano ~/.bashrc#打开后ctrl+v翻页到末尾,在文件末尾添加:
export GOROOT=/home/user/go1.22.1_projects/go1.22.1root
export GOPATH=$GOROOT/bin:$PATH
export PATH=$GOROOT/bin:/home/user/anaconda3/bin:$PATH#ctrl+X保存并退出,然后通过以下命令使其生效:
source ~/.bashrc
1.4 syzkaller编译
#安装git并查看是否安装成功
sudo apt install git
git version git clone https://github.com/google/syzkaller
cd syzkaller
make
# 如果fuzz目标是arm 64位,则需指定交叉编译器,如下
# make CC=aarch64-linux-gnu-g++ TARGETARCH=arm64
“git clone https://github.com/google/syzkaller”这个命令会在你执行命令的当前路径下创建一个名为 syzkaller
的文件夹,并将 syzkaller
项目的所有代码和相关文件克隆到这个文件夹中。
编译时间一般几分钟,红色字体并不意味着报错,需查看具体内容。
1.5 Linux内核编译
1.下载Linux内核代码,采用浅拷贝方式,仅拷贝源码,不拷贝整个项目
# 5.14版本内核
git clone --branch v5.14 --depth 1 git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
git clone --branch v5.1 --depth 1 git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
下载完成后会自动在当前路径(go_project文件夹)下创建新目录linux用于存放内核编译结果,也可以在末尾指定存放的路径文件夹,需采用以下语句提前建立文件夹。
cd /home/ubuntu2004/go_projects/
mkdir Linux
2.生成默认配置文件
cd linux #进入自动创建的linux内核源码文件夹下
make defconfig # 生成默认配置文件.config,该文件为隐藏文件,ls指令查看不到,需要ls -a查看
make kvm_guest.config # kvm配置
3.开启必要的内核配置选项以启动syzkaller,在.config
文件末尾添加如下内容
注意编译内核之前添加这些选项,并且要把下面那些选项对应的注释删掉!!(比如:# CONFIG_KCOV is not set)要不然会被rewrite掉。如右侧:
nano .config #编辑config文件
gedit .config
# 开启代码覆盖收集
CONFIG_KCOV=y # 必须开启
# 调试符号化信息,用于更好地定位错误
CONFIG_DEBUG_INFO=y # 必须开启
# 开启内存错误检测
CONFIG_KASAN=y # 内存检测功能
CONFIG_KASAN_INLINE=y # 内联的 KASAN 检测
# 禁用地址空间布局随机化 (KASLR) 以提高代码覆盖效果
# KASLR 会影响 fuzz 效率,因此需要禁用
# CONFIG_RANDOMIZE_BASE is not set# 可选项,进一步提升 fuzz 效率
CONFIG_KCOV_INSTRUMENT_ALL=y # 使 KCOV 覆盖所有代码
CONFIG_KCOV_ENABLE_COMPARISONS=y # 启用比较操作的覆盖
# 调试文件系统,便于调试和检查内核状态
CONFIG_DEBUG_FS=y
# 开启内存泄漏检测
CONFIG_DEBUG_KMEMLEAK=y
4.重新生成配置文件并编译(耗时较久,约20分钟)
make olddefconfig # 将当前内核源码的新特性加入.config中去
make -j4 #4线程并行编译,可依据配置缩小
5.编译成功后,就能在目录下看到vmlinux
(kernel binary) 和 bzImage
(packed kernel image)
ls linux/vmlinux
linux/vmlinux #输出结果
ls linux/arch/x86/boot/bzImage
#ls arch/x86/boot/bzImage
linux/arch/x86/boot/bzImage #输出结果
1.6 创建虚拟机B使用的镜像
1.安装debootstrap
工具,可以用来构建最基本的系统,即构建相应的文件路径等。
sudo apt-get install debootstrap
2.创建 Debian Stretch Linux 镜像,$IMAGE
替换为你想要的目录,执行以下命令
mkdir $IMAGE
cd $IMAGE/
# 从 GitHub 下载 syzkaller 提供的 create-image.sh 脚本,并将其保存为 create-image.sh 文件。这个脚本用于创建一个适用于 QEMU 的虚拟机镜像。
# -O create-image.sh 选项告诉 wget 将下载的文件保存为 create-image.sh
wget https://raw.githubusercontent.com/google/syzkaller/master/tools/create-image.sh -O create-image.sh
chmod +x create-image.sh # 给 create-image.sh 添加可执行权限,使它可以作为脚本运行。
./create-image.sh # 执行 create-image.sh 脚本。该脚本会自动创建一个适合虚拟机启动的 Linux 镜像。
笔者采用流程:
①在go_project/路径下建立一个image文件夹,cd进去
mkdir image
cd imgae/
②下载相应组件
wget https://raw.githubusercontent.com/google/syzkaller/master/tools/create-image.sh -O create-image.sh
chmod +x create-image.sh
./create-image.sh
3.完成后可以找到$IMAGE/stretch.img
这个文件就是构建好的磁盘镜像文件,可以看到出现了:stretch.id_rsa、stretch.id_rsa.pub、stretch.img 这几个文件。
PS:实际生成的镜像文件名与多个教程中的镜像文件名不同,这是因为笔者使用的是 Debian Bullseye
版本,而官方示例中使用的是 Debian Stretch
版本。实际上两者的内容是相似的,只是命名不同。
Stretch: 这是 Debian 9 的代号,于 2017 年发布。
Bullseye: 这是 Debian 11 的代号,于 2021 年发布。
1.7 QEMU创建虚拟机B
注意此处的QEMU测试启动命令与syzkaller默认的QEMU虚拟机启动命令不同,若之后syzkaller启动时报错,请参考本文相关报错解决方案
1.开启虚拟化
若在宿主机 VMware 上使用虚拟机A( Linux )则应当在设置中把 虚拟化 Intel VT-x/EPT 或 AMD-V/RVI(V)
打开
右键虚拟机A,打开设置 在cpu界面的右下角“把 虚拟化 Intel VT-x/EPT 或 AMD-V/RVI(V) 打开”
此步骤常见报错:虚拟机A启动失败
解决方案:WIn10系统里有自带的hyper-v 系统虚拟机,它的一些服务跟配置是跟VM的配置服务起冲突的,所以会导致VM虚拟机进行报错,那么解决的方法如下:
①.在windows(硬件机)打开 启用或关闭windows功能-----并按下图中红色箭头所述勾选或取消勾选对应服务
②.接着去关闭Hyper—v的系统服务(这个很关键!),右键属性,手动设置为禁用
③.关闭完成后:在windows(硬件机)以管理员身份去运行 CMD 并输入以下命令:
bcdedit /set hypervisorlaunchtype off #关闭系统的Hyper-V的服务
④.重启电脑
以管理员身份打开CMD 运行 bcdedit /enum 命令 注意查看末尾返回信息:
⑤.重启虚拟机,实测能够打开ubuntu2004虚拟机A
2.安装QEMU
无需指定安装位置,apt会从 Ubuntu 的软件仓库下载 QEMU 的预编译包以及相关依赖
sudo apt install qemu-system-x86
qemu-system-x86_64 --version #查看是否安装成功
如果安装过 windows docker 或者 wsl2 的需要在windows的管理员模式的命令台中关闭hype-v,这会导致 docker 和 wsl 2 不能正常工作:
bcdedit /set hypervisorlaunchtype off # 以管理员身份运行命令提示符执行命令,彻底关闭hype-v
3.启动虚拟机B
需要指定内核位置$KERNEL和镜像文件位置$IMAGE,可使用find ~/ -name "bullseye.img"查找对应文件路径
模板启动命令如下(复制进终端执行时,需保证最后一行为空格,否则无法顺利执行):
qemu-system-x86_64 \-m 2G \-smp 2 \-kernel $KERNEL/arch/x86/boot/bzImage \-append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" \-drive file=$IMAGE/stretch.img,format=raw \-net user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22 \-net nic,model=e1000 \-enable-kvm \-nographic \-pidfile vm.pid \2>&1 | tee vm.log
本机启动命令为:
qemu-system-x86_64 \-m 2G \-smp 2 \-kernel /home/ubuntu2004/go_projects/linux/arch/x86/boot/bzImage \-append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" \-drive file=/home/ubuntu2004/go_projects/image/bullseye.img,format=raw \-net user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22 \-net nic,model=e1000 \-enable-kvm \-nographic \-pidfile vm.pid \2>&1 | tee vm.log
各参数的含义:
|
4.登录虚拟机B
5.测试SSH连接情况
需要注意的一点是,使用QEMU创建虚拟机B和syzkaller自动创建虚拟机B所采用的网卡号是不同的,因此这两个允许存在一者无法使用ssh,即他们占用同一个网卡号。所以这里ssh连接失败并不意味着syzkaller无法启动,可直接尝试启动syzkaller,如果把这两种虚拟机分开指定不同的网卡号,则可以避免这种情况发生,但本教程没有进行尝试,不影响syzkaller的正常启动
# 在虚拟机A的终端中运行以下命令,而不是在虚拟机B中运行
# 可直接右键虚拟机A的桌面打开新的终端
ssh -i /home/ubuntu2004/go_projects/image/bullseye.id_rsa -p 10021 -o "StrictHostKeyChecking no" root@localhost
6.关闭虚拟机
# 在宿主机关闭qemu虚拟机,注意要在打开QEMU的路径下运行该命令
在console环境下,先按Ctrl + a,释放之后再按x键。
kill $(cat vm.pid)# 或者直接关闭终端以关闭qemu虚拟机,关闭了启动 QEMU 虚拟机的终端时,虚拟机的进程也被关闭了。默认情况下,QEMU 进程与启动它的终端是绑定在一起的,当终端被关闭时,所有与之关联的进程(包括 QEMU)也会被终止。# 查询是否有虚拟机运行
ps aux | grep qemu
# 如果 QEMU 虚拟机在运行,应该会有类似于 qemu-system-x86_64 的进程条目。
2.syzkaller启动
2.1 创建配置文件
syzkaller config参数详解
为了使syzkaller运行起来,在syzkaller目录下新建一个workdir目录,并在syzkaller目录下新建一个config文件用于配置运行所需参数(本机命名为my.cfg)。
mkdir workdir
nano my.cfg #在 SyzVegas目录中创建配置文件,用来配置 syzkaller 运行所需的参数
配置cfg文件的模板格式如下,这里需要注意替换环境变量为你自己的路径:(默认选择左边配置,kvm报错时在vm字段内增加 "qemu_args": "-enable-kvm" )
本机配置如下:
{"target": "linux/amd64","http": "127.0.0.1:56741","workdir": "/home/ubuntu2004/go_projects/syzkaller/workdir","kernel_obj": "/home/ubuntu2004/go_projects/linux","image": "/home/ubuntu2004/go_projects/image/bullseye.img","sshkey": "/home/ubuntu2004/go_projects/image/bullseye.id_rsa","syzkaller": "/home/ubuntu2004/go_projects/syzkaller/","procs": 8,"type": "qemu","vm": {"count": 4,"kernel": "/home/ubuntu2004/go_projects/linux/arch/x86/boot/bzImage","cpu": 4,"mem": 2048 }
}
启动syzkaller
./bin/syz-manager -config=my.cfg
一些重要的参数:
沙箱类型说明:
2.2 检查网络接口配置
1.查看syzkaller启动日志,此时能观察到报错和采用的QEMU启动命令
通过debug模式查看虚拟机的启动命令:
./bin/syz-manager -config=my614.cfg -debug
./bin/syz-manager -config=my.cfg -debug
#多次启动syzkaller时,syz-manage会占用端口号,需要手动释放
lsof -i :56741 # 检查当前占用该端口的进程,注意端口号和报错相符
kill -9 3630 #强制关闭
显示网络接口号存在异常,Syzkaller无法正常开始fuzzing
2.从上图中观察到syzkaller默认调用的QEMU启动命令如下:
qemu-system-x86_64 \-m 2048 \-smp 2 \-chardev socket,id=SOCKSYZ,server=on,wait=off,host=localhost,port=58010 \-mon chardev=SOCKSYZ,mode=control \-display none \-serial stdio \-no-reboot \-name VM-0 \-device virtio-rng-pci \-enable-kvm \-cpu host,migratable=off \-device e1000,netdev=net0 \-netdev user,id=net0,restrict=on,hostfwd=tcp:127.0.0.1:48016-:22 \-hda /home/ubuntu2004/go_projects/image/bullseye.img \-snapshot \-kernel /home/ubuntu2004/go_projects/linux/arch/x86/boot/bzImage \-append "root=/dev/sda console=ttyS0"
3.采用上述命令通过QEMU登录虚拟机后,查看网络服务状态:
systemctl status networking.service
4.查看当前网络接口
ip address
发现eth0
不存在而是 enp0s4
2.3 启动QEMU虚拟机释放网卡
第一种解决方案:(自测无效,仅供参考)
为了避免出现[FAILED] Failed to start Raise network interfaces.的错误,要在下述配置文件加入”cmdline”: “net.ifnames=0”或者在.config中追加CMDLINE相关的两条配置项
Bash
# 在syzkaller目录下创建workdir
cd syzkaller
mkdir workdir# 在syzkaller目录下创建my.cfg,各选项均采用绝对路径
{"target": "linux/amd64","http": "127.0.0.1:56741","workdir": "/home/zzy/kernel-fuzz/syzkaller/workdir","kernel_obj": "/home/zzy/kernel-fuzz/kernels/linux-6.1.12","image": "/home/zzy/kernel-fuzz/image/bullseye.img","sshkey": "/home/zzy/kernel-fuzz/image/bullseye.id_rsa","syzkaller": "/home/zzy/kernel-fuzz/syzkaller","procs": 8,"type": "qemu","vm": {"count": 8,"kernel": "/home/zzy/kernel-fuzz/kernels/linux-6.1.12/arch/x86/boot/bzImage","cmdline": "net.ifnames=0","cpu": 2,"mem": 2048}
}
第二种解决方案:本机采用该方案(推荐)
由于上一步测试虚拟机ssh连接时的虚拟机网卡eth0跟实际qemu虚拟机中运行的网卡名称一样,导致syzkaller启动的虚拟机网卡没有分配IP地址,因此需要修改测试虚拟机的网卡配置,释放网卡eth0。
1.QEMU采用”测试指令“启动虚拟机,即以非快照“snapshot”形式启动
qemu-system-x86_64 \-m 2G \-smp 2 \-kernel /home/ubuntu2004/go_projects/linux/arch/x86/boot/bzImage \-append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" \-drive file=/home/ubuntu2004/go_projects/image/bullseye.img,format=raw \-net user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22 \-net nic,model=e1000 \-enable-kvm \-nographic \-pidfile vm.pid \2>&1 | tee vm.log
2.安装相关包
apt update
apt install iproute2
apt install net-tools
3.编辑网络配置文件: 打开测试QEMU虚拟机的(注意是之前采用之前测试ssh连接的那个命令打开的虚拟机,而不是syzkaller启动QEMU的命令)/etc/network/interfaces
文件:
nano /etc/network/interfaces
cat /etc/network/interfaces
4.重启网络服务: 修改后,运行以下命令以重启网络服务:
systemctl restart networking.service
5.检查网络状态: 使用以下命令查看网络接口状态:
ip address systemctl status networking.service
6.成功运行:
二.实例运行
1.syzkaller运行机制
-
syz-manager
:控制整个 syzkaller 模糊测试系统-
启动、监视并且重启多个虚拟机,并在这些虚拟机里面通过
sshd
调用syz-fuzzer
-
使用
RPC
与虚拟机中的syz-fuzzer
交流实现的覆盖率和fuzzing过程中的任何跟踪信息,比如crash和corpus.。 -
将得到的信息存储在本地
workdir
目录下 -
通过设置
http
公开一个基于web的界面,在这个界面可以浏览存储的信息
-
-
syz-fuzzer
:负责引导整个 fuzz 的过程:-
输入的生成,变异,语料最小化等。
-
启动
syz-executor
进程进行 fuzz -
从被 fuzz 的 kernel 的
/sys/kernel/debug/kcov
获得覆盖的相关信息 -
通过
RPC
将触发新代码覆盖的输入发送回syz-manager
-
-
syz-executor
:负责执行单个输入(一系列 syscalls)-
从
syz-fuzzer
接收程序并执行,然后返回结果 -
它被设计为尽可能简单(为了不干扰 fuzzing 进程),用C++编写,编译为静态二进制,并使用共享内存进行通信。
-
测试步骤流程图如下:
-
编写待测试驱动对应的 txt 文件(即系统调用描述)。可参考官方文档:syscall_descriptions_syntax.md,syzkaller 源码对应的
sys
目录也给出了大量示例 。 -
编译系统调用描述文件,使用
syz-extract
将其转换为 .const 文件,使用syz-sysgen
生成 .go 文件。 -
编译 syzkaller,生成
syz-manager
、syz-fuzzer
以及syz-executor
。 -
编写配置文件,可参考 configuration.md,运行
syz-manager --config my.cfg
,syz-manager 会根据配置文件,将syz-fuzzer
和syz-executor
push 到目标设备,并开始 fuzzing。
2.syscall&syzlang
syz-fuzzer
进程根据描述的 syscalls 生成由syz-executor
执行的程序。
2.1 syscall
、syzlang
、syscall description
之间的关系
1.
syscall
是操作系统提供的接口,允许应用程序与内核交互。
2.
syscall description
是对系统调用的描述,用于明确系统调用的输入输出及其行为。这是模糊测试时生成有效调用的基础。
3.
syzlang
是定义这些系统调用描述的语言,通过 syzlang
,用户可以编写模糊测试程序,定义测试数据和序列,用于 syzkaller
来生成大量系统调用组合。
格式: txt
文件
内容: syzlang
文件是对系统调用(syscalls)的定义和描述。它包括系统调用的名字、参数类型、操作模式、以及一些辅助的常量和标识符等。syzlang
文件以文本格式编写,定义如何调用这些系统调用。
示例 (syscall description
)
openat(fd fd, path ptr[in, string], flags flags[open_flags], mode flags[open_mode]) fd
write(fd fd, buf buffer[in], count len[buf]) len[buf]
这个示例定义了 openat
和 write
系统调用,其中详细描述了每个系统调用的参数及其类型。
syzkaller自己定义了一套描述系统调用模版的声明式语言(syzlang),有人称之为描述文件/声明文件。为了提高fuzz效率,我们必须为目标系统量身定制这种声明文件。通常一个设备节点对应一个声明文件。
2.2 syscall description
-
txt声明文件的语法: syscall description语法
-
一些linux下的syscall description:现有可参考的声明文件
syscall description的伪形式语法:
syscallname "(" [arg ["," arg]*] ")" [type] ["(" attribute* ")"]
arg = argname type
argname = identifier
type = typename [ "[" type-options "]" ]
typename = "const" | "intN" | "intptr" | "flags" | "array" | "ptr" |"string" | "filename" | "glob" | "len" |"bytesize" | "bytesizeN" | "bitsize" | "vma" | "proc" |"compressed_image"
type-options = [type-opt ["," type-opt]]
一个例子是:
中国厨师(fuzzer) 使用中文对菜品的描述(syzlang)构造菜谱(syscall descriptions) 来准备和描述 菜品(syscall),并且为顾客(操作系统内核) 提供一系列不同的菜品组合,目标是发现哪些组合会让顾客出现不良反应(系统漏洞)。
中国厨师(fuzzer):厨师(fuzzer)通过不断创新和尝试不同的菜品组合(系统调用序列)来满足顾客(操作系统内核)的口味。厨师的目标是在有限的条件下,探索出未被尝试过的独特组合(潜在的漏洞)。
操作系统内核(顾客):操作系统内核就是最终的顾客,它“品尝”这些菜品(syscalls),而厨师(fuzzer)的目的是让顾客“吃”出一些问题(发现漏洞)。
菜品(syscall):厨师(fuzzer)通过系统调用(菜品)与操作系统内核(顾客)进行交互。
中文对菜品的描述(syzlang):syzlang不仅是系统调用的语言表达方式,它也是描述如何执行这些调用的工具,就像菜谱不仅是中文,它更是中文中对如何烹饪菜品的详细描述。尽管syzkaller
本身是用 Go 语言编写的,但 syzlang
不是 Go 语言的一部分。
菜谱(syscall description):菜谱是关于如何准备每道菜品的详细说明,这对应于通过syzlang定义的测试用例的具体实现,详细描述了如何调用操作系统的特定系统调用。即系统调用描述,明确系统调用的参数、顺序和执行方式。
3.Fuzzing 实例
3.1运行实例1-原始syscall
修改现有Linux Kernel(内核代码)中的chmod
函数,该函数专门针对chmod
系统调用。因此本测试不涉及创建新的系统调用,只是测试现有的chmod
系统调用。syzkaller配置文件bugLabel1中的enable_syscalls
字段用来限制只测试chmod
,加速漏洞的触发。具体流程分为三步:
①修改内核中的chmod
函数。
②编译内核。
③通过syzkaller
测试chmod
调用,触发漏洞。
以下为详细的实施步骤:
1.编译有漏洞的驱动到内核中
1.在Linux内核源码中,找到linux/fs/open.c
文件
cd home/ubuntu2004/go_projects/linux/fs/
nano open.c
nano打开后,ctrl+W进入搜索模式,输入chmod_common搜索到此处进行插入
添加以下代码片段来模拟漏洞:
static umode_t old_mode = 0xffff;
if (old_mode == 0 && mode == 0) {path = NULL;
}
old_mode = mode;
图片中的+号部分为新增的bug代码部分
bug代码解析:连续两次chmod
调用的mode入参为0时,产生空指针解引用的bug,这个函数的执行路径是chmod() -> do_fchmodat() -> chmod_common()
2.编译受修改的内核和镜像
在内核源码目录中运行以下命令生成新的内核镜像,不需要使用 make clean
,只修改了 open.c
这样一个单一的文件,并且其他依赖没有发生变化,make
可以自动检测到所修改的文件,并且只重新编译受影响的部分。这种情况下,不需要使用 make clean
,因为这会导致重新编译整个内核,从而增加编译时间。
编译内核得到新的vmlinux
(kernel binary) 和 bzImage
(packed kernel image)。
修改内核代码测试bug时,只需要重新编译内核得到最新的镜像bzImage,不需要重新编译虚拟机磁盘镜像bullseye.img(stretch.img)
cd /home/ubuntu2004/go_projects/linux/
make -j4 #如果运行崩溃就减少并行编译的数量,过程约5分钟
2.修改配置文件运行syzkaller
1.到syzkaller源码目录下,新增配置文件bugLaberl1.cfg(也可以修改旧的配置文件,新增方便分类管理),增加enable_syscalls
,只测试chmod
,以加快fuzz速度:
cd /home/ubuntu2004/go_projects/syzkaller/
nano bugLabel1.cfg
配置文件内容为:
{"target": "linux/amd64","http": "127.0.0.1:56741","workdir": "/home/ubuntu2004/go_projects/syzkaller/workdir","kernel_obj": "/home/ubuntu2004/go_projects/linux","image": "/home/ubuntu2004/go_projects/image/bullseye.img","sshkey": "/home/ubuntu2004/go_projects/image/bullseye.id_rsa","syzkaller": "/home/ubuntu2004/go_projects/syzkaller","procs": 8,"type": "qemu","enable_syscalls": ["chmod"],"vm": {"count": 4,"kernel": "/home/ubuntu2004/go_projects/linux/arch/x86/boot/bzImage","cpu": 4,"mem": 2048}
}
2.运行syzkaller,配置文件选择bugLabel1
启动syzkaller
./bin/syz-manager -config=bugLabel1.cfg
运行40分钟后,产生第一次crash,EOF对应的crash为前一次启动的报错,非本次运行报错。
3.3运行实例2-定制syscall
向运行在虚拟机中的Linux内核植入模块之后,手动针对目标的系统调用编写syscall description。例如proc_testxy.txt
,描述如何与新的内核模块进行交互(open
、read
、write
等操作)。然后通过syz-extract
工具生成相应的常量文件,通过syz-sysgen将常量文件生成go文件,并重新编译syzkaller
,最后执行syzkaller。流程如下:
①编写并添加有漏洞的内核模块。
②修改Kconfig和Makefile,编译并加载模块。
③编写syscall description。
④生成相关文件并编译syzkaller。
⑤通过syzkaller测试模块接口,触发漏洞。
定制:description(即对新的内核接口,增加系统调用描述文件)是一个比较繁琐的过程,官方给了如下文档用作参考:
-
txt声明文件的语法: syscall description语法
-
一些linux下的syscall description:现有可参考的声明文件
整个定制过程分为4步,但为了触发bug需要提前将内核模块编译进运行内核:
-
根据目标内核模块的信息,撰写符合syzlang语法的txt声明文件,即syscall description
-
syz-extract根据txt及linux源码,提取符号常量的值,生成中间文件***.const文件
-
syz-sysgen根据const文件生成syzkaller执行时使用的go文件
-
重新编译syzkaller
0.新增syscall description
此节仅作原理分析
1.针对定制的目标代码,本例针对系统调用open()函数,它的函数定义如下:
int open(const char *pathname, int flags, mode_t mode);
-
flags:
O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, FASYNC, O_CLOEXEC, O_CREAT, O_DIRECT, O_DIRECTORY, O_EXCL, O_LARGEFILE, O_NOATIME, O_NOCTTY, O_NOFOLLOW, O_NONBLOCK, O_PATH, O_SYNC, O_TRUNC, __O_TMPFILE
-
mode:
S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH
2.采用szlang描述该系统调用
resource fd_test[fd]
open(file ptr[in, filename], flags flags[open_flags], mode flags[open_mode]) fd_test
# 或
# open(file ptr[in, string["/dev/xxx"]], flags flags[open_flags], mode flags[open_mode]) fd_test
注:
ioctl(input/output control)通常承载更多更复杂的功能,syzkaller针对ioctl提供了一种通用的描述方法:
ioctl(fd_1 fd_test, cmd intptr, arg buffer[in])
为了更精确地触发ioclt中各个case分支,通常需要做额外的适配工作:
ioctl$DRM_IOCTL_VERSION(fd fd_dri, cmd const[DRM_IOCTL_VERSION], arg ptr[in, drm_version])
ioctl$VIDIOC_QUERYCAP(fd fd_video, cmd const[VIDIOC_QUERYCAP], arg ptr[out, v4l2_capability])
...
可以对比如下参考文件,学习基本使用方法:
-
/dev/random字符设备
-
random源码路径:linux/drivers/char/random.c
-
random描述文件:https://github.com/google/syzkaller/blob/master/sys/linux/dev_random.txt
-
-
/dev/ptmx字符设备
-
ptmx源码路径:linux/drivers/tty/tty_ioctl.c
-
ptmx描述文件:https://github.com/google/syzkaller/blob/master/sys/linux/dev_ptmx.txt
-
1.编译有漏洞的驱动到内核中
对Linux内核代码进行修改,编译漏洞和定制syscall进入syzkaller这两个步骤可以交换顺序
1.在/linux/drivers/char/目录下,新建一个testxy.c。这是一个有漏洞的内核模块,test.c中有一个堆溢出的demo,将他编译然后insmod上去。
cd /home/ubuntu2004/go_projects/linux/drivers/char/
nano testxy.c
testxy.c文件内容如下:采用原github的代码会导致linux编译内核时失败,需要采用修改后的以下代码
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>#define MY_DEV_NAME "testxy"static struct proc_dir_entry *test_entry;static ssize_t proc_write(struct file *proc_file, const char __user *proc_user, size_t n, loff_t *loff)
{char *c = kmalloc(512, GFP_KERNEL);ssize_t not_copied;if (!c) {return -ENOMEM;}not_copied = copy_from_user(c, proc_user, 4096);if (not_copied) {printk("Failed to copy data from user space, %zd bytes not copied\n", not_copied);kfree(c);return -EFAULT;}printk(":into write!\n");kfree(c);return n;
}static ssize_t proc_read(struct file *proc_file, char __user *buffer, size_t count, loff_t *off)
{return 0;
}// 使用 proc_ops 代替 file_operations
static const struct proc_ops proc_fops = {.proc_write = proc_write,.proc_read = proc_read,
};static int __init mod_init(void)
{test_entry = proc_create(MY_DEV_NAME, S_IRUGO | S_IWUGO, NULL, &proc_fops);if (!test_entry) {return -ENOMEM;}printk("Test module loaded\n");return 0;
}static void __exit mod_exit(void)
{remove_proc_entry(MY_DEV_NAME, NULL);printk("Test module unloaded\n");
}module_init(mod_init);
module_exit(mod_exit);
MODULE_LICENSE("GPL");
bug片段为:
static ssize_t proc_write (struct file *proc_file, const char __user *proc_user, size_t n, loff_t *loff)
{char *c = kmalloc(512, GFP_KERNEL);copy_from_user(c, proc_user, 4096);printk(":into write!\n");return 0;
}
2.打开/linux/drivers/char/目录下的Kconfig文件,添加:
config TESTXY_MODULEtristate "heap overflow test"default yhelpThis file is to test a buffer overflow# 也可以选用此版本,版本更容易在之后的操作页面中找到该模块 ,用户进入 menuconfig 后,可以看到 "Device Drivers下的Character devices" 这一层级,所有字符设备相关的配置选项都会显示在这个菜单中。
menu "Character devices"
config TESTXY_MODULEtristate "heap overflow test" #在menuconfig中显示的名字default y helpThis file is to test a buffer overflow
endmenu
3.打开char/目录下的Makefile文件,添加:
obj-$(CONFIG_TESTXY_MODULE) += testxy.o
若/linux/drivers/char/是新目录,还需修改/linux/drivers/Kconfig(加上source “drivers/char/Kconfig”);修改/linux/drivers/Makefile(加上obj-$(CONFIG_TEST_MODULE) += char/)。
4.模块编译进内核
make menuconfig时编入的模块名“heap overflow test”在menuconfig可视化页面下的Device Drivers 下的 Heap Overflow Test
(*表示直接编入内核,M表示模块形式) 处看到刚刚添加的测试模块。
# 1.清理特定模块,避免清理整个内核源码树
cd /home/ubuntu2004/go_projects/linux/
make M=drivers/char clean# 选择模块
make menuconfig# 3. 使用并行编译,仅编译所需模块,并匹配系统CPU核心数
make -j8 M=drivers/char modulesmake clean
make menuconfig
make -j8
在menuconfig中按 Y
表示将该模块直接编译进内核,显示为“*”;或者按 M
表示将该模块作为可加载模块(即 .ko
文件),显示为“M”。 如果是后者,则需要采用insmod的方式将编译好的模块打入到linux内核中,前者则直接已经编译进入内核。
5.用QEMU采用新的内核启动虚拟机,在虚拟机中查看模块是否加载成功
启动虚拟机:
qemu-system-x86_64 \-m 2G \-smp 2 \-kernel /home/ubuntu2004/go_projects/linux/arch/x86/boot/bzImage \-append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" \-drive file=/home/ubuntu2004/go_projects/image/bullseye.img,format=raw \-net user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22 \-net nic,model=e1000 \-enable-kvm \-nographic \-pidfile vm.pid \2>&1 | tee vm.log
在虚拟机中查看模块是否加载成功
# 查看模块对应设备节点是否存在
cd /root
ls /proc/testxy
# 查看模块加载时的log信息
dmesg | grep "proc init"
2.定制syscall description
对syzkaller进行修改
1.syzkaller源码中,在syzkaller/sys/linux/目录下,新建针对目标内核模块的txt声明文件(本例中将其命名为proc_testxy.txt
)
cd home/ubuntu2004/go_projects/syzkaller/sys/linux/
nano proc_testxy.txt
proc_testxy.txt中新增如下内容:注意read$proc和write$proc语句后面没有返回值,很多博客文档给出的txt文件都带了返回值,是错误的
include <linux/fs.h>open$proc(file ptr[in, string["/proc/testxy"]], flags flags[proc_open_flags], mode flags[proc_open_mode]) fd
read$proc(fd fd, buf buffer[out], count len[buf])
write$proc(fd fd, buf buffer[in], count len[buf])
close$proc(fd fd)proc_open_flags = O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, FASYNC, O_CLOEXEC, O_CREAT, O_DIRECT, O_DIRECTORY, O_EXCL, O_LARGEFILE, O_NOATIME, O_NOCTTY, O_NOFOLLOW, O_NONBLOCK, O_PATH, O_SYNC, O_TRUNC, __O_TMPFILE
proc_open_mode = S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH
open$proc()参数:
file ptr[in, string["/proc/testxy"]]: 输入参数,指定要打开的文件路径,固定为 /proc/testxy。
flags flags[proc_open_flags]: 输入参数,表示打开文件时的标志,类型为 proc_open_flags。
mode flags[proc_open_mode]: 输入参数,表示文件的访问权限,类型为 proc_open_mode。read$proc()参数:
fd fd: 输入参数,表示要读取的文件描述符。
buf buffer[out]: 输出参数,读取的数据将存储在此缓冲区中。
count len[buf]: 输入参数,表示要读取的字节数。write$proc参数:
fd fd: 输入参数,表示要写入的文件描述符。
buf buffer[in]: 输入参数,包含要写入的数据。
count len[buf]: 输入参数,表示要写入的字节数。close$proc参数:
fd fd: 输入参数,表示要关闭的文件描述符。这些是打开文件时可以使用的标志,定义了文件的访问模式和行为:
O_RDONLY: 只读模式。
O_WRONLY: 只写模式。
O_RDWR: 读写模式。
其他标志如 O_APPEND, O_CREAT, O_EXCL 等,定义了文件打开时的特定行为。
2.首次执行时,如果syzkaller/bin目录下,没有syz-extract和syz-sysgen这两个文件的话,需要执行如下命令编译:
make -j2 bin/syz-extract # 耗时5分钟起步
make -j2 bin/syz-sysgen # 新版 syzkaller 默认会编译生成 syz-sysgen
针对某个驱动接口编写的配置文件 xxx.txt,syz-extract 二进制根据配置文件和内核源码生成 const 文件,syz-sysgen 会根据 txt 配置文件和 const生成一个 .go(因为 Syzkaller 是用 go 语言进行编写的)。可在syzkaller/sys/linux/gen/amd64.go和executor/syscalls.h中看到结果。
报错1:/snap/go/10711/pkg/tool/linux_amd64/compile: signal: killed make: *** [Makefile:224: bin/syz-extract] Error 1 ubuntu2004@ubuntu:~/go_projects/syzkaller$
解决措施:1.降低并行编译进程数,采用-j2或者-j1 2.重启虚拟机释放内存,仅运行此终端进行编译
3.执行syz-extract
在syzkaller/目录下调用bin/下的syz-extract生成const文件。
bin/syz-extract -os linux -arch amd64 -sourcedir "/home/ubuntu2004/go_projects/linux" proc_testxy.txt#检查是否成功生成const文件
cd sys/linux/
-sourcedir:linux内核代码路径
bin/syz-extract会自动去搜索proc_testxy.txt,可生成该文件对应的const文件。
4.执行syz-sysgen
将 .txt
文件中的系统调用描述转换为 Go 代码
bin/syz-sysgen
该步骤将更新syzkaller/sys/linux/gen/amd64.go,自动添加上新定义的系统调用,生成如下片段:
推荐在ubuntu的软件商店下载vs code,然后ctrl+f搜索amd64.go文件的内容
{NR:2,Name:"open$proc",CallName:"open",Args:[]Field{
{Name:"file",Type:Ref(14245)},
{Name:"flags",Type:Ref(6737)},
{Name:"mode",Type:Ref(7357)},{Name:"read$proc",CallName:"read",Args:[]Field{
{Name:"fd",Type:Ref(15194)},
{Name:"buf",Type:Ref(12742)},
{Name:"count",Type:Ref(8360)},
}},{NR:1,Name:"write$proc",CallName:"write",Args:[]Field{
{Name:"fd",Type:Ref(15194)},
{Name:"buf",Type:Ref(12744)},
{Name:"count",Type:Ref(8360)},
}},{NR:3,Name:"close$proc",CallName:"close",Args:[]Field{
{Name:"fd",Type:Ref(15194)},
}},
1.NR 代表系统调用号(Syscall Number),即内核中每个系统调用的唯一标识符。在示例中,open$proc 对应的系统调用号是 2,write$proc 是 1,close$proc 是 3。
2.Name:表示在 Syzkaller 中标识这个系统调用的名称。
3.CallName:表示实际的系统调用名称。在这个例子中,所有调用的 CallName 都是标准的 open、read、write、close,表明这些是标准系统调用的变体。
4.Args 字段: Args 定义了系统调用的参数列表,每个参数都有 Name 和 Type。参数类型使用了 Ref 来引用预定义的类型(通过类型 ID 进行引用)。
make generate和syz-sysgen区别总结:
首先,两者都能基于syz-extract对txt转换得到的const文件生成go代码
-
make generate
:是一个更全面的命令,除了调用syz-sysgen
生成 Go 代码文件外,还会执行其他生成任务,比如系统调用描述文件的生成和更新。 -
syz-sysgen
:只生成 Go 代码,基于现有的系统调用描述文件。它不会生成或更新.txt
描述文件。
什么时候用哪个?
-
如果你只需要根据现有的
.txt
文件生成系统调用的 Go 代码,运行syz-sysgen
就足够了。 -
如果你对描述文件(或者其他部分)做了更改,需要确保所有生成的文件都最新,运行
make generate
会更加全面。
5.重新编译syzkaller
在syzkaller/下执行:
# 失败案例,不建议采用 make generate的方式增量更新,本机尝试失败,会提示clang-format等报错
make -j4 generate # 自动生成 syzkaller 的系统调用处理代码
make -j4 # 重新编译syzkaller,耗时约7分钟,成功后能找到vmlinux和bzImage,参考之前的配置流程
# 成功案例
make clean
make -j1 # -内存不足时j2都将导致编译崩溃,花费1分钟左右
3.修改配置文件运行syzkaller
在syzkaller的配置文件bugLabel2.cfg中指定针对目标系统调用的syscall,重新运行syzkaller
为了更快看到crash结果,增加了“enable_syscalls”项,只允许某些系统调用,能更快地触发漏洞。
{"target": "linux/amd64","http": "127.0.0.1:56741","workdir": "/home/ubuntu2004/go_projects/syzkaller/workdir","kernel_obj": "/home/ubuntu2004/go_projects/linux","image": "/home/ubuntu2004/go_projects/image/bullseye.img","sshkey": "/home/ubuntu2004/go_projects/image/bullseye.id_rsa","syzkaller": "/home/ubuntu2004/go_projects/syzkaller","procs": 8,"type": "qemu","enable_syscalls":["open$proc","read$proc","write$proc","close$proc"], "vm": {"count": 4,"kernel": "/home/ubuntu2004/go_projects/linux/arch/x86/boot/bzImage","cpu": 4,"mem": 2048}
}
启动syzkaller,开始fuzz。
./bin/syz-manager -config=bugLabel2.cfg
./bin/syz-manager -config=bugLabel2.cfg -debug
./bin/syz-manager -config=my.cfg
得到结果如下:
终止syzkaller后,需要kill syz-manage这个进程才能再次启动syzkaller
lsof -i :56741 # 检查当前占用该端口的进程
kill -9 3305 #强制关闭
在 Syzkaller 的运行过程中,虚拟机的状态会根据不同的测试阶段和条件进行自动调整。所看到的状态变化是正常的,以下是各个状态的含义:
-
running: handshake:虚拟机正在进行初始握手,与 Syzkaller 的管理器建立连接。
-
running: fuzzing is stopped:这表示模糊测试暂时停止,可能是由于某种原因需要进行状态更新或调整。
-
running: executing:虚拟机正在执行系统调用,进行模糊测试。
-
booting:虚拟机正在启动中。
-
running: reproducing:如果发现了潜在的漏洞,Syzkaller 会将虚拟机置于重现模式,尝试重现该漏洞
三.常见报错
报错1:编译syzkaller对于gcc的版本有要求,根据提示选择对应版本的gcc重新安装或者它会自动进行更新。
报错2:编译syzkaller进程被中断
ompile: signal: killed
make: *** [Makefile:156: manager] Error 1
make: *** Waiting for unfinished jobs....
#使用单进程编译
make -j1
报错3:g++ is missing (exec: "g++": executable file not found in PATH)
系统中缺少 g++ 编译器,这是 GNU 编译器套件的一部分,通常用于编译 C++ 代码。syzkaller 的 executor 模块依赖于 C++ 编译器来生成执行器。# 1. 安装 g++,然后再次运行 make 命令:
sudo apt update
sudo apt install g++
g++ --version# 2. 重新运行make
make -j1
报错4:linux源码下载速度太慢
方案:①浅拷贝(采用并解决)
只需要最新的 Linux 5.14 版本进行 syzkaller 的 fuzz 测试和开发,浅克隆是可行的,并且可以提高效率,减少下载的数据量。而如果你有需求对历史版本进行分析或追踪补丁和提交记录,则需要完整克隆。
git clone --branch v5.14 --depth 1 git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
②更换清华源
报错5:安装时显示进程占用,另一个进程正在使用 dpkg
解决方案:
# 1.结束所有apt进程
sudo killall apt apt-get# 2.若提示没有apt进程,如下:
apt: no process found
apt-get: no process found#3.则依次执行:
sudo rm /var/lib/apt/lists/lock
sudo rm /var/cache/apt/archives/lock
sudo rm /var/lib/dpkg/lock*
sudo dpkg --configure -a
sudo apt update
报错6:生成虚拟机B使用的运行镜像时,在这一步因为空间不够导致报错
如果想要去删除这个镜像,重新安装,在$IMAGE
目录下面不能成功删除chroot/
,需要在/mnt/
目录下面删除。
报错7(终端报错):Syzkaller:broken programs in the corpus: 0, broken seeds: 1
(报错7的另一种形式,采用另一终端ssh连接虚拟机时出现的报错):ssh连接报错:kex_exchange_identification: read: Connection reset by peer
(报错7的另二形式:debug模式下提示[FAILED] Failed to start Raise network interfaces
终其原因,是网络接口的配置问题,已在上面提供了详细的解决方案。
参考文献
源码分析
说明机制+详细安装
Github仓库_带安装文档说明
中文博主安装文档1
中文博主视频1
中文博主安装文档2
中文博主安装文档3
中文博主安装文档4_详细踩坑经验
文档5
重要报错解决方案
第二种解决网络端口配置问题的重要方案
最原始github仓库,带c代码,txt文件
例2.对上一个仓库的执行的CSDN中文文档
模仿例2的例3,步骤明确,但没给文件
例4,步骤不明,没给文件
入门知识大全_syzkaller学习路线
备注
1.虚拟机A中使用QEMU创建虚拟机B的意义
在虚拟机A中安装 GCC 和 Go 编译器,然后在虚拟机A中创建虚拟机B。
1. GCC 和 Go 编译器的作用:
-
Go 编译器:用于在虚拟机A中编译 syzkaller 工具。syzkaller 是用 Go 语言编写的,所以需要在虚拟机A中安装 Go 编译器来构建 syzkaller 二进制文件(如
syz-manager
和syz-executor
)。 -
GCC:用于编译 Linux 内核,并且需要开启覆盖率支持(
CONFIG_KCOV
)。GCC 编译器也在虚拟机A中被用来编译测试所需的 Linux 内核。
2. 虚拟机A的作用:
虚拟机A的主要目的是作为宿主环境,将在这个虚拟机中安装 syzkaller,配置内核,以及管理后续在虚拟机B中的模糊测试任务。
-
在虚拟机A中,Go 编译器用于编译 syzkaller 的相关工具,这些工具随后会在虚拟机B中执行模糊测试。
-
虚拟机A中的 GCC 用于编译启用了覆盖率支持的 Linux 内核,这个内核也会被安装到虚拟机B中。
3. 虚拟机B的作用:
-
虚拟机B 主要用于运行模糊测试(即 syzkaller 将生成测试用例并在虚拟机B中运行),并捕获测试结果和覆盖率信息。
-
虚拟机B 不需要 Go 编译器,因为它仅用于执行已在虚拟机A中编译好的二进制文件和测试用例。
-
虚拟机B 也不需要 GCC 编译器,除非你在虚拟机B中重新编译内核。通常情况下,虚拟机B使用的是已经在虚拟机A中编译好的内核。
4. 虚拟机A中安装编译器的意义:
-
虚拟机A是用户的主要开发和管理环境,所有的编译工作(包括 syzkaller 和内核)都在虚拟机A中完成。
-
虚拟机B只是执行测试的环境,虚拟机A 负责创建和管理虚拟机B,包括将已经编译好的内核和 syzkaller 工具传递给虚拟机B。
两者关系总结如下:
-
虚拟机A中的 GCC 和 Go 编译器是有意义的,因为你需要在虚拟机A中编译 syzkaller 工具和 Linux 内核。
-
虚拟机B 不需要重新安装 GCC 和 Go 编译器,它只是用来执行测试的环境,使用的是在虚拟机A中准备好的工具和内核。
即虚拟机A用于构建和管理环境,而虚拟机B仅用于测试运行,因此虚拟机A中的编译器在整个流程中是必要的。
2.bzImage和bullseye.img(stretch.img)两个镜像文件的区别
-
bzImage 是 Linux 内核的压缩启动镜像,主要负责启动内核。它不包含完整的操作系统文件,仅限于内核和引导相关的代码。
-
qemu-img/stretch.img 则是一个虚拟机磁盘镜像,包含完整的操作系统文件和文件系统,虚拟机从该镜像中加载操作系统并运行。
在实际使用中,bzImage
和 qemu-img/stretch.img
都会被 QEMU 使用:
-
bzImage 作为虚拟机的内核文件来引导虚拟机。
-
qemu-img/bullseye.img(stretch.img) 则是虚拟机的文件系统和操作系统所在的磁盘映像。
在 syzkaller
中,bzImage
用来启动 Linux 内核,stretch.img
或其他 .img
文件作为虚拟机的根文件系统(虚拟机的硬盘)。
因此修改内核代码测试bug时,只需要重新编译内核得到最新的镜像bzImage,不需要重新编译虚拟机磁盘镜像bullseye.img(stretch.img)
3. Syzbot:官方bug/crash登记网站
详细的内容将在专栏中单独用一篇文章进行介绍。
可以在syzbot中找发现的bug,有crash的日志和复现程序(syz和C),把bin/linux_amd64/
复制到要测试的虚拟机中,按以下步骤复现。
echo 1 > /proc/sys/kernel/panic_on_oops 注意不能用 vim 编辑
cat /proc/sys/kernel/panic_on_oops 确认是否生效
./linux_amd64/syz-execprog -executor=./linux_amd64/syz-executor -repeat=0 -procs=16 -cover=0 crash-log
./linux_amd64/syz-execprog -executor=./linux_amd64/syz-executor -repeat=0 -procs=16 -cover=0 file-with-a-single-program