内核漏洞挖掘技术系列(5)——KernelFuzzer
字数 1954 2025-08-05 08:19:22
内核漏洞挖掘技术:KernelFuzzer 工具详解
1. KernelFuzzer 概述
KernelFuzzer 是 MWRLabs 在 Defcon24 开源的一款内核模糊测试工具,宣称支持 Windows 7/10、OS X 和 QNX 系统,但实际上主要支持 Windows 系统。
项目地址:https://github.com/mwrlabs/KernelFuzzer
2. 工具架构
2.1 目录结构
crash_processing/ # 处理崩溃
crashes/ # 存放发现的崩溃
library_calls/ # 要模糊测试的库调用
reproducer/ # 崩溃复现(功能有限)
worker_setup/ # 环境设置和启动
bughunt.c # 主程序,启动模糊测试线程
bughunt.h # 提供随机字符/数组生成函数
bughunt_build_*_*.bat # 编译脚本
bughunt_loop.py # 处理发现的崩溃
bughunt_syscall_x64/ # 64位系统调用实现
bughunt_syscall.asm # 32位系统调用实现
bughunt_syscalls.h # 要模糊测试的系统调用定义
bughunt_thread.h # 模糊测试线程实现
handles_database.h # 生成各种句柄
helpers.h # 辅助函数
hooking.h # 设置和取消hook
library_calls.h # 库调用定义
logger.h # 日志功能
2.2 工作流程
- 在主机上使用 VS 环境编译可执行文件
- 将所有文件拷贝到目标虚拟机系统
- 在虚拟机安装 Python 环境
- 运行 worker_setup.py 设置环境
- 启动模糊测试
3. 核心组件分析
3.1 worker_setup.py
该脚本负责环境准备:
- 安装 WinDbg
- 执行注册表脚本(禁用 UAC、自动更新、锁屏、错误报告)
- 安装 Python couchdb 模块
- 配置 dump 文件路径
- 设置登录时运行 bughunt_loop.py 的计划任务
- 为 win32k.sys 启用 special pool
- 开启内核调试
- 重启系统
3.2 bughunt_loop.py
崩溃处理流程:
- 监控 C:/Dumps/ 目录下的 .dmp 文件
- 发现崩溃后将文件拷贝到新文件夹
- 使用 kd.exe 分析 dump 文件,输出到 windbg.log
- 将日志文件与 dump 文件放在同一目录
- 调用 couchdb_submit.py 提交崩溃信息到远程数据库
- 运行 bughunt.exe(线程数=1,系统调用=350000,seed=1,超时=10分钟)
- 超时后执行清理并重启
3.3 bughunt_thread.h
模糊测试线程主要逻辑:
- 随机调用要测试的库调用(library_calls.h 中定义)
- 随机进行系统调用(根据 bughunt_syscalls.h 提供的参数类型信息生成随机参数)
- 调用 bughunt_syscall 函数执行系统调用
3.4 系统调用实现
32位实现 (bughunt_syscall.asm):
- 参数通过栈传递
- 提取系统调用号和参数压栈
mov edx, 7FFE0300hcall dword ptr [edx]
64位实现 (bughunt_syscall_x64.asm):
- 前四个参数通过 RCX,RDX,R8,R9 传递
- 系统调用号 RCX 放入 RAX
- 参数依次前移:RCX←RDX, RDX←R8, R8←R9, R9←[rbp + 30h]
- 执行
syscall指令
4. 功能扩展
4.1 增加系统调用
工具默认提供的系统调用有限,可以通过以下方法扩展:
-
获取系统调用表:
- 下载 j00ru 提供的系统调用表:https://github.com/j00ru/windows-syscalls
- 下载 ReactOS 代码:https://github.com/reactos/reactos
-
使用脚本分析 ReactOS 头文件,提取系统调用信息:
import os
import csv
import shutil
# 查找包含系统调用的头文件
def findstring(pathfile, syscallname):
file = open(pathfile, "r")
string = file.read()
if string.find(syscallname) != -1:
global allfiles
if pathfile not in allfiles:
allfiles.append(pathfile)
# 遍历目录查找.h文件
def readFilename(file_dir, syscallname):
for root, dirs, files in os.walk(file_dir):
for file in files:
if file.endswith(".h"):
full_path = os.path.join(root, file)
findstring(full_path, syscallname)
# 主程序
file_path = "F:\\windows-fuzzing\\ReactOS-0.4.11"
fscv = csv.reader(open("F:\\windows-fuzzing\\KernelFuzzer\\nt.csv", 'r'))
for row in fscv:
syscallname = row[0]
readFilename(file_path, syscallname)
fscv = csv.reader(open("F:\\windows-fuzzing\\KernelFuzzer\\win32k.csv", 'r'))
for row in fscv:
syscallname = row[0]
readFilename(file_path, syscallname)
print(allfiles)
- 将提取的信息转换为 KernelFuzzer 需要的格式:
import re
import csv
# 从CSV中查找系统调用信息
def search_csv(str, fscv, f):
for row in fscv:
if str in row:
if row[26]: # win10 1903:27, win10 1809:26, win7 SP1:15, XP SP2:2
syscallname = row[0]
syscallnumber = row[15]
arguments = "{"
while True:
str = f.readline()
flag = 0
# 匹配各种参数类型
matchobj = re.search(r'(\s)+DWORD|int\s', str)
if matchobj:
flag = 1
arguments += " _INT32,"
# 其他类型匹配...
matchobj = re.search(r'\);', str)
if matchobj:
arguments += " NIL }, NIL },"
break
finalstr = " { ((DWORD)" + syscallnumber + "), " + arguments + " //" + syscallname
print(finalstr)
return True
return False
# 定位要搜索的函数
def locate_function_to_search(f):
while True:
str = f.readline()
if not str:
break
matchobj = re.search(r'Nt(.*)\(', str)
if matchobj:
index = str.find("(")
str = str[:index]
# 搜索NT系统调用
ntfile = open("F:\\windows-fuzzing\\KernelFuzzer\\nt.csv", 'r')
fntscv = csv.reader(ntfile)
if search_csv(str, fntscv, f) == True:
continue
# 搜索win32k系统调用
win32kfile = open("F:\\windows-fuzzing\\KernelFuzzer\\win32k.csv", 'r')
fwin32kscv = csv.reader(win32kfile)
if search_csv(str, fwin32kscv, f) == True:
continue
# 主程序
if __name__ == '__main__':
files = ('F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\lpcfuncs.h',
# 其他头文件列表...
)
for file in files:
f = open(file)
locate_function_to_search(f)
- 将转换后的结果添加到 bughunt_syscalls.h 中
4.2 相关工具:NtCall64
另一个 Windows 系统调用模糊测试工具:https://github.com/hfiref0x/NtCall64
主要特点:
- 支持 fuzz W32pServiceTable (win32k.sys) 或 KiServiceTable (ntoskrnl.exe)
- 通过特征码定位未导出的系统调用表
- 随机生成系统调用参数
工作流程:
- 解析命令行参数
- FuzzInitPhase1 进行准备工作
- FuzzInitPhase2 根据参数选择 fuzz 目标
- FuzzRunThreadWithWait -> FuzzThreadProc -> DoSystemCall -> ntSyscallGate
- 在 syscall.asm 中完成实际系统调用
5. 总结
KernelFuzzer 是一个基础的 Windows 内核模糊测试框架,虽然功能相对简单,但通过扩展可以支持更多系统调用。关键点包括:
- 理解工具的整体架构和工作流程
- 掌握系统调用的实现机制(32位和64位不同)
- 学会扩展系统调用列表的方法
- 了解相关工具如 NtCall64 的不同实现方式
工具局限性:
- 缺乏反馈驱动机制,发现漏洞效率有限
- 默认提供的系统调用和库调用较少
- 需要手动扩展功能
通过结合 ReactOS 代码和系统调用表,可以显著增强工具的测试范围,提高发现内核漏洞的可能性。