在这个阶段,作者通过利用LLM分析驱动程序的源代码生成 ioctl
的规约。为了提高LLM的性能,作者将该过程分为三个阶段:推断命令值,识别参数类型和类型定义。
这种结构化的方法使LLM能够在每个阶段集中精力在一个特定的方面,从而提高效率和专注力。与设备名称推断过程类似,作者在规约生成的每个阶段都使用few-shot prompt。
这一步在实现时需要考虑两方面因素:
- 1.尽管像GPT-4这样的最新LLM支持128K的上下文大小,但向LLM提供所有与驱动程序相关的代码仍然是不切实际的。
- 2.这一步目标是推断
ioctl
处理程序的命令值和参数类型,不是所有代码或辅助函数都与这一目标相关。
3.2.1.Command Value
推断命令值示例如图6所示。prompt包括与推断任务相关的函数的源代码,涵盖 ioctl
处理程序函数和与命令值分析相关的任何辅助函数。比如之前 _ctl_fops
结构体的 unlocked_ioctl
字段被赋值为 dm_ctl_ioctl
函数,那么推断命令值就需要分析 dm_ctl_ioctl
函数的源代码。
LLM的输出是成功推断的命令值集合。如果推测命令值还需要分析其它函数,作者要求LLM列出“缺失”分派函数的名称和调用详细信息(分析结果 UNKNOWN
字段)。此外,还包括使用命令值变量的代码片段。如果LLMs识别出任何未知的命令值,KernelGPT将继续分析新识别出的分派函数,并整合其在上一步中的使用信息。前一步 的UNKNOWN
字段的输出在引导后续步骤时作为参考。
比如在示例中分析 dm_ctl_ioctl
时LLM反馈需要继续分析 ctl_ioctl
函数,继续分析 ctl_ioctl
时LLM反馈需要分析 lookup_ioctl
函数,不过在分析 ctl_ioctl
时,LLM已经确定参数值 DM_VERSION
。(这可能意味着推断命令值设计多轮分析多个结果),到目前为止,LLM已经确定命令值 DM_VERSION
的处理函数为 ctl_ioctl
。
3.2.2.Argument Type
在确定了命令值之后,接下来便要分析该命令对应的参数类型,如图7 prompt所示。这一步骤的输入为命令推断的输出,包括相关函数和展示参数用法的代码片段。KernelGPT随后提取这些相关函数的源代码,并将其输入LLM,由它们来识别参数类型。如果涉及其它函数,那么这个类型仍然会被标记为 UNKNOWN
。在这种情况下,KernelGPT会利用LLMs提供的新信息继续其分析工作。
图7展示了设备映射器驱动程序中针对 DM_REMOVE_ALL
命令值的参数推断的实际例子。在之前的步骤中,LLM识别与该命令值相关的函数为 remove_all
,以及引用该函数的代码。因此,KernelGPT向LLM提供了 remove_all
的源代码,以指导参数类型推断。通过分析其签名,LLM推断出该参数应该是指向 struct dm_ioctl
结构的指针。此外,LLM将 struct dm_ioctl
放置在 TYPES
字段中,为下一阶段的类型定义分析做好准备。
注:这一步SyzDescribe通过规则进行推断,SyzDescribe只分析 ioctl
调用分析对应的命令值及对应的参数类型。比如下面代码片段中,SyzDescribe通过对 if
和 switch
的条件分析判断命令值为 cmd_1
、cmd_2
、cmd_3
之一,当命令值为 cmd_1
时,参数 arg
会被转换为 struct xx_type
类型。因此这里需要复杂的启发式规则识别参数类型。因此推测用LLM是为了更好地识别这类转换操作识别真正的参数类型。
long xx_ioctl(struct file *file, int cmd, long arg) {
switch (cmd) {
case cmd_1:
struct xx_type xx_arg;
copy_from_user(&xx_arg, arg, sizeof(xx_arg));
…
break;
case cmd_2:
fd = get_unused_fd_flags(…);
file = anon_inode_getfile(…, &no_fops, …);
fd_install(fd, file);
return fd;
default:
xx = file->private_date;
if (xx.ops->ioctl)
xx.ops->ioctl(file, cmd, arg);
}
if (cmd == cmd_3) {
…
}
…
3.2.3.Type Definition
在识别了参数类型之后,KernelGPT继续为这些类型生成规约。图8展示了在这一阶段使用的提示。本质上,KernelGPT从Linux内核代码库中检索类型定义源代码。然后将这个源代码呈现给LLM,由它们创建相应的Syzkaller规约。在类型定义中引用了嵌套类型的情况下,LLM被指示在它们的输出中将它标记为 UNKNOWN
。这些被标记的类型将在后续步骤中进一步分析。
图8中展示的类型定义推断例子涉及一个示例:PhysDevAddr_struct
结构体中的一个字段是一个由 SCSI3Addr_struct
结构体类型组成的数组。由于这个新引用的结构体 SCSI3Addr_struct
的源代码没有包含在提供的信息中,因此预期LLM会将其标识为 UNKNOWN
。这种标识表明SCSI3Addr_struct
需要在后续步骤中进一步分析。(疑问:这一步好像可以直接用解析工具层次化生成,无需LLM)
3.3.Specification Validation and Repair
在这一阶段,受到最新基于LLM的程序修复研究启发,作者旨在验证由KernelGPT生成的规约,并自动更正任何无效的部分。这一步骤极为关键,因为LLM在规约生成过程中可能出错。为了解决这一问题,作者采用了Syzkaller的工具syz-extract来验证规约的有效性,该工具能够识别语法错误,如未定义的变量和类型。首先,KernelGPT通过syz-extract提供的错误信息定位规约中的错误之处,并将错误信息与相应规约匹配。接着,针对检测到错误的规约,KernelGPT通过提供few-shot prompt向LLM反馈。
如图9所示,这个过程包括向LLM提供错误的规约、相应的错误信息以及内核代码库中的相关源代码以供修复。之后,LLM预期将输出修正后的正确规约。
这里 vfio_pci_hot_reset_info
的类型描述最初是错误的。这是因为syzlang要求数组 [type, length]
规约 中的长度必须是一个常数,而g中使用了可变长度的数组。在syzlang中,可变长度数组的正确格式仅为 array[type]
。通过分析syz-extract生成的错误信息 “count is unsupported on all arches”
,LLMs修复了这个问题,通过将 devices
重新定义为可变长度数组,并将count指定为类型 len[devices]
,从而使得规约与syzlang的要求保持一致。
4.Implementation
1.Source Code Extractor
作者采用LLVM工具链实现了Linux内核源代码提取器。该提取器通过模式匹配解析内核代码库,识别设备操作处理程序,并准备输入数据供KernelGPT使用。此外,它还提取操作处理程序内的 ioctl
处理程序及其引用位置,并编译内核中的所有函数、结构体、联合和枚举定义。这些定义在LLMs需要时,为规约生成和修复提供指导。
2.LLM
KernelGPT基于最先进的LLM GPT4构建。在每一步分析中,作者通过OpenAI API访问GPT4,并设置低温度参数为0.1。分析的最大迭代次数 MAX_ITER
默认设置为5。
3.Few-shot prompting
- 为了推断设备名称和命令值,作者采用了3-shot prompt来适应LLM的上下文大小限制。这是因为在这两个步骤中涉及到的函数源代码通常较长,这需要限制少量示例的数量。
- 相比之下,对于其他阶段,如参数类型推断和类型定义分析,作者选择了6次提示。
- 在修复阶段,考虑到潜在的错误多样性以及必要的修复信息和错误消息通常的简洁性,作者选择了9次提示的策略。这种方法旨在为LLM提供对修复过程的更广泛理解。
4.Driver Selection
在初步评估中,作者专注于为Syzkaller中未曾描述的驱动程序或处理程序创建规约。这一重点极为重要,因为缺少规约的驱动程序通常未经过彻底测试。因此,这些驱动程序成为作者的关注焦点。在设备名称分析之后,作者首先确认Syzkaller是否已存在该设备的规约。如无现有规约,作者便着手生成新的规约。目前,作者的主要工作集中在常规驱动程序,如字符和块设备,而网络和USB设备的规约生成则计划在未来进行。
5.Evaluation
- RQ1:KernelGPT为未描述的内核驱动生成的规约数量及其质量如何?质量主要通过覆盖率衡量。
- RQ2:与baseline相比,KernelGPT生成的系统调用规约的质量如何?主要也是通过覆盖率比较。
- RQ3:KernelGPT生成的规约能否检测到内核中的real-world bug?
作者选择了Linux内核版本6.7作为target,该版本于2023年11月5日发布,hash标识为d2f51b。此外,为了规约生成和评估,作者使用了Linux内核的syzbot配置。这个选择为谷歌在通过QEMU对Linux内核进行模糊测试时的决策。
作者的Fuzzing配置遵循默认的Syzkaller设置,使用4个QEMU实例,每个实例利用2个CPU核心。为了确保公平比较并减轻崩溃重现的影响,作者在收集和比较覆盖范围结果时禁用了reproduce
特性。为了展示KernelGPT生成的规约的质量,作者选择了SyzDescribe (当前SOTA系统调用规约生成方法)和由人类专家制定的现有Syzkaller规约作为基准。
5.1.Overall Results
在syzbot内核配置下检测到132个 ioctl
处理程序,不包括网络和USB驱动程序处理程序。在这些设备中,有50个(占37.9%)根据KernelGPT推断的设备名称在Syzkaller中被识别为缺少规约。然后,作者为这些未描述的驱动程序生成规约,成功推断出其中的39个,如表1所示。
对于其余设备未能生成规约的失败主要归因于几个原因。
- 首先,它们代码的复杂逻辑使得LLMs难以理解,特别是当
ioctl
函数的源代码过长时,阻碍了GPT4的理解能力。 - 此外,当要分析的源代码超过GPT4的上下文限制时,它就无法正确推断。
在KernelGPT成功生成的39个规约中,有24个直接通过验证,另外8个通过KernelGPT成功修复,使它们能够通过验证检查。然而,KernelGPT无法修复其余的7个规约。对于32个有效的规约,作者使用这些经过验证的规约运行Syzkaller,并其中有17个可以执行。设备无法执行的一个潜在原因是它们需要复杂的初始化步骤,这些步骤无法在规约中定义(例如,像 syz_open_dev
这样的辅助函数),或者无法被GPT4准确推断。另一个可能的因素是这些设备在syzbot内核配置中没有启用。
作者分别对每个新检测到的驱动程序进行了8小时的测试,使用了默认设置。结果显示在表2中,# Descriptions
列显示每个设备的系统调用规约数量,Cov
列是作者为每个设备独立生成的规约进行8小时模糊测试时覆盖的行数。 Unique Cov
列反映了与标准Syzkaller设置相比的唯一覆盖情况,在相同条件下激活了所有3,912个系统调用。在这个标准设置中,覆盖了143,838行。由Kernel GPT生成的先前未解决的驱动程序的规约增加了129个(3.3%)规约,并有助于覆盖额外的6,668个(5%)唯一行。这些数字突显了为先前未解决的驱动程序提供规约的重要性,并展示了作者生成的规约的有效性。
除了单独运行每个驱动程序的规约外,作者将所有新驱动程序的规约与原始的Syzkaller规约集成在一起。这意味着在一次运行中,同时执行由KernelGPT生成的新规约和Syzkaller中的现有规约。鉴于系统调用数量庞大,约为四千个左右,作者将这个组合运行的模糊测试时长延长到了24小时。随后,将此组合运行的覆盖结果与在相同设置下仅运行现有的Syzkaller规约的结果进行比较。集成KernelGPT为未描述的驱动程序合成的规约已被证明是有效的,触发的崩溃数量比仅使用现有的Syzkaller规约多28.6%(28 vs. 36)。就代码覆盖率而言,由于实验的初步性质和规模较小,结果相对较为适度。由于KernelGPT引入的新系统调用规约数量相对较小(129),与总数(3912)相比,集成后的代码覆盖率增加适度,额外覆盖了1.5K行。尽管如此,即使是这个有限规模的实验也突显了使用KernelGPT推断更多系统调用的规约的巨大潜力,这可能在未来的应用中实现更为有效的内核模糊测试。
5.2.Comparison with Baselines
为评估KernelGPT生成的规约质量,作者选择了10个由baseline方法(Syzkaller和SyzDescribe )规约的“现有”设备。作者使用KernelGPT为这些设备生成规约,并在独立的运行中比较了每个规约的覆盖结果,仅启用了规约中包含的系统调用。
表3显示了KernelGPT、SyzDescribe和Syzkaller生成的规约的结果,在8小时的模糊测试后详细列出了系统调用数量、定义的类型和行覆盖情况。KernelGPT实现了最高的行覆盖,超过基线方法21.3%以上,突显了其生成高质量规约以提升模糊测试性能的效果。此外,KernelGPT为驱动程序定义了最多的类型,展示了其在类型分析方面的强大能力。对于SyzDescribe,两个规约未能产生覆盖,其中一个问题源于SyzDescribe对设备名称的错误推断。总体而言,比较结果表明KernelGPT在描述的驱动程序上甚至超过了Syzkaller,预示着将KernelGPT应用于更多驱动程序可能取得更显著的效果。
5.3.Kernel Bug Detection
表4展示了KernelGPT在未描述的驱动程序中检测到的漏洞。到目前为止,KernelGPT已经检测到8个独特的漏洞,其中7个以前是未知的。值得注意的是,所有这些漏洞都无法通过默认配置的Syzkaller检测到。
KernelGPT检测到的7个新漏洞均位于两个新描述的驱动程序中,分别是device mapper和CEC。
第一个漏洞是在设备映射器驱动程序中的 ctl_ioctl
函数中发现的 kmalloc
漏洞,而第二个漏洞是在 dm_table_create
函数中发现的 kmalloc
漏洞。
它们的成因不同但相似:驱动程序忽略了对 kvmalloc
分配大小的检查,导致可能分配过大的内存大小。具体而言:
- bug1崩溃与
dm_ioctl
结构中的data_size
字段有关。该字段在copy_param
过程中的数据结构准备阶段中分配内存起着关键作用。 - bug2问题集中在
DM_TABLE_LOAD_CMD
命令值和target_count
字段。在执行dm_table_create
时,这些元素对于分配目标至关重要。值得注意的是,尽管SyzDescribe为此驱动程序生成了一个规约,但其中包含有误的设备文件名、错误的命令值和不精确的类型,因此未能检测到这两个漏洞。第一个漏洞已经得到了内核开发人员的确认。
其余5个的新发现的漏洞都是在CEC驱动程序中发现的,这是一个用于HDMI CEC硬件的标准化内核接口。
- bug3: 图10包含与
KASAN: slab-use-after-free Read in cec_queue_msg_fh
漏洞相关的代码片段,其中CEC驱动程序从已被kfree(fh)
释放的变量中读取数据。 - bug4: 在
cec_transmit_msg_fh
中发生的 ODEBUG漏洞是由于驱动程序试图使用kfree(data)
释放活动对象。 - bug5:
cec_data_cancel
中的WARNING是由CEC驱动程序内部检查触发的,该检查期望变量处于当前或挂起状态。 - bug6:
INFO: task hung in cec_claim_log_addrs
中的问题是由于内核挂起,因为CEC设备等待配置任务的完成。 -
- bug7: 在
cec_transmit_done_ts
中发生的一般保护错误是由于CEC设备尝试引用一个非规范地址,导致内核崩溃。
- bug7: 在
6.Conclusion
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数网络安全工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注网络安全获取)
给大家的福利
零基础入门
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
同时每个成长路线对应的板块都有配套的视频提供:
因篇幅有限,仅展示部分资料
网络安全面试题
绿盟护网行动
还有大家最喜欢的黑客技术
网络安全源码合集+工具包
所有资料共282G,朋友们如果有需要全套《网络安全入门+黑客进阶学习资源包》,可以扫描下方二维码领取(如遇扫码问题,可以在评论区留言领取哦)~
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!**](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算