Syzkaller实战教程2:运行环境配置+实例运行

发布于:2025-07-31 ⋅ 阅读:(22) ⋅ 点赞:(0)

一.环境配置流程

笔者的宿主机是一台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 now

sudo 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

各参数的含义:


qemu-system-x86_64:
这是 QEMU 的主程序,用于启动一个 x86_64 架构的虚拟机。根据目标架构使用不同的 QEMU 可执行文件,比如 qemu-system-arm 对应 ARM 架构。
-m 2G:
为虚拟机分配 2GB 的内存。
-smp 2:
为虚拟机分配 2 个 CPU 核心。
-kernel $KERNEL/arch/x86/boot/bzImage:
使用指定路径下的 bzImage 作为虚拟机的内核。$KERNEL 代表内核源码或编译输出路径,bzImage 是内核的可启动镜像。
-append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0":
传递给内核的启动参数:
console=ttyS0:将控制台输出重定向到串口 ttyS0,用于调试。
root=/dev/sda:指定根文件系统所在的设备 /dev/sda,通常是虚拟硬盘。
earlyprintk=serial:启用早期打印功能,将启动时的调试信息输出到串口。
net.ifnames=0:禁用网络接口的名称更改,使用传统的 eth0 这样的名称。
-drive file=$IMAGE/stretch.img,format=raw:
指定虚拟机的硬盘映像文件路径。$IMAGE/stretch.img 是硬盘映像文件的位置,format=raw 表示硬盘映像文件的格式为原始二进制格式(不压缩)。
-net user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22:
配置用户模式网络:
host=10.0.2.10:指定虚拟机中的主机 IP。
hostfwd=tcp:127.0.0.1:10021-:22:将虚拟机中的端口 22 (SSH) 转发到宿主机的 127.0.0.1:10021,使得可以通过宿主机的端口 10021 访问虚拟机的 SSH 服务。
-net nic,model=e1000:
使用 e1000 以太网卡模型配置虚拟机的网络接口卡 (NIC)。
-enable-kvm:
启用 KVM(Kernel-based Virtual Machine),使用硬件虚拟化技术(如 Intel VT-x 或 AMD-V),提高虚拟机的性能。
-nographic:
禁用 QEMU 的图形界面,虚拟机将以无图形的方式启动,所有输出都重定向到终端。
-pidfile vm.pid:
将虚拟机进程的 PID 写入文件 vm.pid,便于后续管理虚拟机进程,如杀死或管理虚拟机。
2>&1 | tee vm.log:
2>&1:将标准错误重定向到标准输出。
| tee vm.log:将所有输出同时写入终端和 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-managersyz-fuzzer以及syz-executor

  • 编写配置文件,可参考 configuration.md,运行 syz-manager --config my.cfg,syz-manager 会根据配置文件,将 syz-fuzzersyz-executorpush 到目标设备,并开始 fuzzing。

2.syscall&syzlang

syz-fuzzer进程根据描述的 syscalls 生成由syz-executor执行的程序。

2.1 syscallsyzlangsyscall 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]

这个示例定义了 openatwrite 系统调用,其中详细描述了每个系统调用的参数及其类型。

syzkaller自己定义了一套描述系统调用模版的声明式语言(syzlang),有人称之为描述文件/声明文件。为了提高fuzz效率,我们必须为目标系统量身定制这种声明文件。通常一个设备节点对应一个声明文件。

2.2 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,描述如何与新的内核模块进行交互(openreadwrite等操作)。然后通过syz-extract工具生成相应的常量文件,通过syz-sysgen将常量文件生成go文件,并重新编译syzkaller,最后执行syzkaller。流程如下:

①编写并添加有漏洞的内核模块。
②修改Kconfig和Makefile,编译并加载模块。
③编写syscall description。
④生成相关文件并编译syzkaller。
⑤通过syzkaller测试模块接口,触发漏洞。

定制:description(即对新的内核接口,增加系统调用描述文件)是一个比较繁琐的过程,官方给了如下文档用作参考:

整个定制过程分为4步,但为了触发bug需要提前将内核模块编译进运行内核:

  1. 根据目标内核模块的信息,撰写符合syzlang语法的txt声明文件,即syscall description

  2. syz-extract根据txt及linux源码,提取符号常量的值,生成中间文件***.const文件

  3. syz-sysgen根据const文件生成syzkaller执行时使用的go文件

  4. 重新编译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])
...

可以对比如下参考文件,学习基本使用方法:

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_MODULE
        tristate "heap overflow test"
        default y
        help
          This file is to test a buffer overflow
          
 # 也可以选用此版本,版本更容易在之后的操作页面中找到该模块 ,用户进入 menuconfig 后,可以看到 "Device Drivers下的Character devices" 这一层级,所有字符设备相关的配置选项都会显示在这个菜单中。        
menu "Character devices"      
config TESTXY_MODULE
        tristate "heap overflow test" #在menuconfig中显示的名字
        default y   
        help
          This 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 modules

make 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 的运行过程中,虚拟机的状态会根据不同的测试阶段和条件进行自动调整。所看到的状态变化是正常的,以下是各个状态的含义:

  1. running: handshake:虚拟机正在进行初始握手,与 Syzkaller 的管理器建立连接。

  2. running: fuzzing is stopped:这表示模糊测试暂时停止,可能是由于某种原因需要进行状态更新或调整。

  3. running: executing:虚拟机正在执行系统调用,进行模糊测试。

  4. booting:虚拟机正在启动中。

  5. 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中安装 GCCGo 编译器,然后在虚拟机A中创建虚拟机B。

1. GCC 和 Go 编译器的作用:

  • Go 编译器:用于在虚拟机A中编译 syzkaller 工具。syzkaller 是用 Go 语言编写的,所以需要在虚拟机A中安装 Go 编译器来构建 syzkaller 二进制文件(如 syz-managersyz-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。

两者关系总结如下:

  1. 虚拟机A中的 GCC 和 Go 编译器是有意义的,因为你需要在虚拟机A中编译 syzkaller 工具和 Linux 内核。

  2. 虚拟机B 不需要重新安装 GCC 和 Go 编译器,它只是用来执行测试的环境,使用的是在虚拟机A中准备好的工具和内核。

即虚拟机A用于构建和管理环境,而虚拟机B仅用于测试运行,因此虚拟机A中的编译器在整个流程中是必要的。

2.bzImage和bullseye.img(stretch.img)两个镜像文件的区别

  • bzImage 是 Linux 内核的压缩启动镜像,主要负责启动内核。它不包含完整的操作系统文件,仅限于内核和引导相关的代码。

  • qemu-img/stretch.img 则是一个虚拟机磁盘镜像,包含完整的操作系统文件和文件系统,虚拟机从该镜像中加载操作系统并运行。

在实际使用中,bzImageqemu-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


网站公告

今日签到

点亮在社区的每一天
去签到