- Isolate and Detect the Untrusted Driver with a Virtual Box
使用虚拟机隔离和检测不可信驱动程序1 - Abstract1
- CCS CONCEPTS1
- KEYWORDS1
- ACM Reference Format: ACM 引用格式:1
- 1 INTRODUCTION1
- 2 RELATED WORK2
- 3 ASSUMPTIONS AND ATTACK VECTORS3
- 4 MOTIVATION3
- 5 METHODOLOGY3
- 6 IMPLEMENTATION5
- 6.1 Build dynamic spaces 6.1 构建动态空间5
- 6.2 Detect control flows 6.2 检测控制流6
- A. Detect entry_flows A. 检测入口流7
- Algorithm2: The transfer method of legal control flow.
算法 2:法律控制流的转移方法。9 - B. Detect exit_flows9
- 7 EVALUATION9
- 7.1 Security evaluation10
- 7.2 Performance evaluation10
- Skip to where you left off12 / 14
- 8 CONCLUSION12
- 9 ACKNOWLEDGMENTS12
- REFERENCES12
Isolate and Detect the Untrusted Driver with a Virtual Box
使用虚拟机隔离和检测不可信驱动程序
使用虚拟机隔离和检测不可信驱动程序
ABSTRACT
In kernel, the driver code is much more than the core code, thus having a larger attack surface. Especially for the untrusted drivers without source code, they may come from the hot-plug hardware or the user without security knowledge. Traditional isolation methods require analyzing source code to set checkpoints in the driver for control flow protection, which are not available for closed-source drivers. Even worse, the existing isolation methods can only prevent the hijacked control flows entering/existing drivers, while they cannot discover the illegal control flows inside drivers. Although the kernel address space location randomization (KASLR) can defend against control flow hijacking, it can be bypassed by code probes. In response to these issues, this paper proposes a novel method Dbox to isolate and detect the untrusted drivers whose source code is unavailable. Dbox creates a light hypervisor to monitor and analyze the untrusted driver’s behavior without relying on source code. It isolates the untrusted driver in a private space and dynamically changes its virtual space through a sliding space mechanism. Under the protection of Dbox, all control flows jumping to/from untrusted drivers can be detected. Experiments and analysis show that Dbox has good protection against code probes, kernel rootkits and code reuse attacks, and the overhead introduced to the operating system is less than 3.6% in general scenarios.
在内核中,驱动程序代码远多于核心代码,因此具有更大的攻击面。特别是对于没有源代码的不可信驱动程序,它们可能来自即插即用硬件或没有安全知识的用户。传统的隔离方法需要分析源代码以在驱动程序中设置检查点以保护控制流,但这对于闭源驱动程序不可用。更糟糕的是,现有的隔离方法只能防止被劫持的控制流进入/退出驱动程序,而无法发现驱动程序内部的非法控制流。尽管内核地址空间位置随机化(KASLR)可以防御控制流劫持,但它可以通过代码探测被绕过。针对这些问题,本文提出了一种新的方法 Dbox,用于隔离和检测源代码不可用的不可信驱动程序。Dbox 创建了一个轻量级的管理程序来监控和分析不可信驱动程序的行为,而无需依赖源代码。它在私有空间中隔离不可信驱动程序,并通过滑动空间机制动态改变其虚拟空间。 在 Dbox 的保护下,所有跳转到/从不可信驱动程序的控制流都可以被检测到。实验和分析表明,Dbox 对代码探测、内核 rootkit 和代码重用攻击有良好的防护作用,且在一般情况下对操作系统的引入开销小于 3.6%。
在内核中,驱动程序代码远多于核心代码,因此具有更大的攻击面。特别是对于没有源代码的不可信驱动程序,它们可能来自即插即用硬件或没有安全知识的用户。传统的隔离方法需要分析源代码以在驱动程序中设置检查点以保护控制流,但这对于闭源驱动程序不可用。更糟糕的是,现有的隔离方法只能防止被劫持的控制流进入/退出驱动程序,而无法发现驱动程序内部的非法控制流。尽管内核地址空间位置随机化(KASLR)可以防御控制流劫持,但它可以通过代码探测被绕过。针对这些问题,本文提出了一种新的方法 Dbox,用于隔离和检测源代码不可用的不可信驱动程序。Dbox 创建了一个轻量级的管理程序来监控和分析不可信驱动程序的行为,而无需依赖源代码。它在私有空间中隔离不可信驱动程序,并通过滑动空间机制动态改变其虚拟空间。 在 Dbox 的保护下,所有跳转到/从不可信驱动程序的控制流都可以被检测到。实验和分析表明,Dbox 对代码探测、内核 rootkit 和代码重用攻击有良好的防护作用,且在一般情况下对操作系统的引入开销小于 3.6%。
CCS CONCEPTS
• Security and privacy → Virtualization and security.
• 安全与隐私 → 虚拟化与安全。
• 安全与隐私 → 虚拟化与安全。
KEYWORDS
Memory Isolation, Driver Security, Code Reuse Attacks, Rootkits
内存隔离、驱动程序安全、代码重用攻击、根 kit
内存隔离、驱动程序安全、代码重用攻击、根 kit
ACM Reference Format: ACM 引用格式:
YongGang Li, ShunRong Jiang, Yu Bao, PengPeng Chen, Yong Zhou, and YehChing Chung. 2024. Isolate and Detect the Untrusted Driver with a Virtual Box. In Proceedings of the 2024 ACM SIGSAC Conference on Computer and Communications Security (CCS ’24), October 14–18, 2024, Salt Lake City, UT, USA. ACM, New York, NY, USA, 14 pages. https://doi.org/10.1145/3658644. 3670269
李永刚,姜顺荣,包宇,陈鹏鹏,周勇,钟叶青. 2024. 使用虚拟机隔离和检测不可信驱动. 在 2024 年 ACM SIGSAC 计算机和通信安全会议(CCS ’24)论文集,2024 年 10 月 14 日至 18 日,美国犹他州盐湖城. ACM, 纽约, NY, 美国, 14 页. https://doi.org/10.1145/3658644.3670269
李永刚,姜顺荣,包宇,陈鹏鹏,周勇,钟叶青. 2024. 使用虚拟机隔离和检测不可信驱动. 在 2024 年 ACM SIGSAC 计算机和通信安全会议(CCS ’24)论文集,2024 年 10 月 14 日至 18 日,美国犹他州盐湖城. ACM, 纽约, NY, 美国, 14 页. https://doi.org/10.1145/3658644.3670269
1 INTRODUCTION
Linux drivers are loaded into the kernel in the form of loadable kernel modules (LKMs). Drivers typically run with high privilege in the kernel. In a driver, any malicious behavior can lead to catastrophic consequences, such as kernel crash. The control flow hijacking is one of the key threats faced by drivers. Kernel rootkits [19, 39] and code reuse attacks (CRAs) [2, 5, 35, 37] are the main threats to driver control flows. Kernel rootkits can hijack the control flow that should flow to the core kernel into malicious drivers. In contrast, CRAs can exploit vulnerabilities to hijack control flows to any code locations.
Linux 驱动程序以可加载内核模块(LKMs)的形式加载到内核中。驱动程序通常在内核中以高权限运行。在驱动程序中,任何恶意行为都可能导致灾难性的后果,例如内核崩溃。控制流劫持是驱动程序面临的主要威胁之一。内核 rootkit [19, 39] 和代码重用攻击(CRAs)[2, 5, 35, 37] 是对驱动程序控制流的主要威胁。内核 rootkit 可以劫持本应流向核心内核的控制流到恶意驱动程序。相比之下,CRAs 可以利用漏洞劫持控制流到任何代码位置。
Linux 驱动程序以可加载内核模块(LKMs)的形式加载到内核中。驱动程序通常在内核中以高权限运行。在驱动程序中,任何恶意行为都可能导致灾难性的后果,例如内核崩溃。控制流劫持是驱动程序面临的主要威胁之一。内核 rootkit [19, 39] 和代码重用攻击(CRAs)[2, 5, 35, 37] 是对驱动程序控制流的主要威胁。内核 rootkit 可以劫持本应流向核心内核的控制流到恶意驱动程序。相比之下,CRAs 可以利用漏洞劫持控制流到任何代码位置。
To prevent drivers from triggering illegal control flows, they need to be audited before being loaded into the kernel. The drivers that have not been audited are untrusted. In practice, users may directly load non-audited drivers into the kernel without any audit. Such drivers pose serious threats to the operating system (OS). Even worse, not all drivers are open-source. For example, Nvidia did not disclose its driver source code until 2022. For the drivers whose source code is unavailable (called untrusted drivers in this paper), all methods relying on source code or compiler cannot prevent their intentional or unintentional illegal activities. For security reasons, all control flows jumping into untrusted drivers (entry_flow) and all control flows jumping out of untrusted drivers (exit_flow) need to be detected.
为了防止驱动程序触发非法控制流,它们在加载到内核之前需要进行审计。未经审计的驱动程序是不可信的。在实际操作中,用户可能会直接将未经审计的驱动程序加载到内核中,而无需任何审计。这些驱动程序对操作系统(OS)构成严重威胁。更糟糕的是,并非所有驱动程序都是开源的。例如,Nvidia 直到 2022 年才公开其驱动程序源代码。对于源代码不可用的驱动程序(本文中称为不可信驱动程序),所有依赖于源代码或编译器的方法都无法防止其有意或无意的非法行为。出于安全原因,所有跳转到不可信驱动程序的控制流(entry_flow)和所有从不可信驱动程序跳转出来的控制流(exit_flow)都需要被检测。
为了防止驱动程序触发非法控制流,它们在加载到内核之前需要进行审计。未经审计的驱动程序是不可信的。在实际操作中,用户可能会直接将未经审计的驱动程序加载到内核中,而无需任何审计。这些驱动程序对操作系统(OS)构成严重威胁。更糟糕的是,并非所有驱动程序都是开源的。例如,Nvidia 直到 2022 年才公开其驱动程序源代码。对于源代码不可用的驱动程序(本文中称为不可信驱动程序),所有依赖于源代码或编译器的方法都无法防止其有意或无意的非法行为。出于安全原因,所有跳转到不可信驱动程序的控制流(entry_flow)和所有从不可信驱动程序跳转出来的控制流(exit_flow)都需要被检测。
From the perspective of attack principles, the drivers with malicious attributes can launch attacks during the loading and running stages. For example, kernel rootkits [19] can tamper with kernel data structures through their initialization functions at loading stage. Afterwards, the illegal control flow can be triggered under specific conditions, such as a system call. The most direct way to defend against such attacks is to use a standardized control flow graph (CFG) to detect the control flows. Although we can build a CFG for the closed-source driver by analyzing its binary code, the CFG will rapidly expand as the driver size increases. An expanded CFG contains some paths that do not exist in real execution scenarios, which will lead to misjudgment. In theory, ensuring the integrity of all the data that can affect control flows can prevent control flow hijacking launched by malicious drivers. However, such data is distributed throughout the kernel space and is dynamically created. Tracking all the data introduces significant overhead.
从攻击原理的角度来看,具有恶意属性的驱动程序可以在加载和运行阶段发起攻击。例如,内核 rootkit [19] 可以通过其初始化函数在加载阶段篡改内核数据结构。之后,当满足特定条件(如系统调用)时,非法控制流可以被触发。防御此类攻击的最直接方法是使用标准化的控制流图(CFG)来检测控制流。虽然我们可以通过分析其二进制代码为闭源驱动程序构建 CFG,但随着驱动程序大小的增加,CFG 会迅速扩展。扩展的 CFG 包含一些在实际执行场景中不存在的路径,这将导致误判。理论上,确保所有可能影响控制流的数据的完整性可以防止恶意驱动程序发起的控制流劫持。然而,这些数据分布在内核空间中,并且是动态创建的。跟踪所有数据会引入显著的开销。
从攻击原理的角度来看,具有恶意属性的驱动程序可以在加载和运行阶段发起攻击。例如,内核 rootkit [19] 可以通过其初始化函数在加载阶段篡改内核数据结构。之后,当满足特定条件(如系统调用)时,非法控制流可以被触发。防御此类攻击的最直接方法是使用标准化的控制流图(CFG)来检测控制流。虽然我们可以通过分析其二进制代码为闭源驱动程序构建 CFG,但随着驱动程序大小的增加,CFG 会迅速扩展。扩展的 CFG 包含一些在实际执行场景中不存在的路径,这将导致误判。理论上,确保所有可能影响控制流的数据的完整性可以防止恶意驱动程序发起的控制流劫持。然而,这些数据分布在内核空间中,并且是动态创建的。跟踪所有数据会引入显著的开销。
From the perspective of running modes, the drivers without malicious attributes can also be used maliciously. For example, CRAs can launch attacks through memory vulnerabilities in the kernel, and use the instruction ret contained in drivers to chain attack payloads (gadgets) [10, 15, 18]. For drivers, all entry_flows and exit_flows should be transferred through specific interfaces. Entry_flows are transferred to the driver by system calls, interrupts, and operation functions of device files. In contrast, exit_flows will be transferred to the locations outside the driver through the functions in core kernel and the exported functions in other LKMs. In theory, forcing all control flows to enter/exit drivers from specific interfaces can defend against control flow hijacking [11]. However, for the untrusted drivers without source code, the existing methods cannot accurately locate all specific interfaces by analyzing binary code.
从运行模式的角度来看,没有恶意属性的驱动程序也可以被恶意使用。例如,CRAs 可以通过内核中的内存漏洞发起攻击,并利用驱动程序中包含的指令 ret 来链接攻击载荷(gadgets)[10, 15, 18]。对于驱动程序,所有 entry_flows 和 exit_flows 都应通过特定接口进行传输。entry_flows 通过系统调用、中断和设备文件的操作函数传输到驱动程序。相比之下,exit_flows 将通过核心内核中的函数和其他 LKMs 中的导出函数传输到驱动程序外部的位置。理论上,强制所有控制流从特定接口进入/退出驱动程序可以防御控制流劫持[11]。然而,对于没有源代码的不可信驱动程序,现有方法无法通过分析二进制代码准确地定位所有特定接口。
从运行模式的角度来看,没有恶意属性的驱动程序也可以被恶意使用。例如,CRAs 可以通过内核中的内存漏洞发起攻击,并利用驱动程序中包含的指令 ret 来链接攻击载荷(gadgets)[10, 15, 18]。对于驱动程序,所有 entry_flows 和 exit_flows 都应通过特定接口进行传输。entry_flows 通过系统调用、中断和设备文件的操作函数传输到驱动程序。相比之下,exit_flows 将通过核心内核中的函数和其他 LKMs 中的导出函数传输到驱动程序外部的位置。理论上,强制所有控制流从特定接口进入/退出驱动程序可以防御控制流劫持[11]。然而,对于没有源代码的不可信驱动程序,现有方法无法通过分析二进制代码准确地定位所有特定接口。
Moreover, the traditional isolation methods can only restrict the paths that control flows jump to/from drivers, and cannot prevent illegal control flows inside drivers. In real attack scenarios, except for entry_flows and exit_flows, the control flows inside the untrusted driver can also be hijacked. For example, attackers can exploit memory vulnerabilities in the driver to launch CRAs and only use the driver code to build a complete gadget chain. Authough the existing KASLR methods [6, 12, 28] can hide code addresses from attackers to defend against illegal control flows, they need to pre-handle the source code of drivers if they want to re-randomize the running binary code in physical pages, which makes them invalid for the closed-source drivers. In contrast, some other KASLR methods can change the base address of the driver during the loading or linking phase without analyzing source code, they cannot re-randomize the driver code when the diriver is running. Even worse, existing research indicates that code probes [3, 8, 24, 29, 33] can bypass address space location randomization (ASLR).
此外,传统的隔离方法只能限制控制流跳转到/从驱动程序的路径,而无法防止驱动程序内部的非法控制流。在实际攻击场景中,除了入口流和出口流之外,不可信驱动程序内部的控制流也可能被劫持。例如,攻击者可以利用驱动程序中的内存漏洞发起 CRAs,并仅使用驱动程序代码构建完整的工具链。尽管现有的 KASLR 方法[6, 12, 28]可以通过隐藏代码地址来防御非法控制流,但如果要对物理页面中的运行二进制代码进行重新随机化,则需要预先处理驱动程序的源代码,这使得它们对闭源驱动程序无效。相比之下,一些其他 KASLR 方法可以在加载或链接阶段更改驱动程序的基地址而无需分析源代码,但它们无法在驱动程序运行时重新随机化驱动程序代码。更糟糕的是,现有研究表明,代码探测[3, 8, 24, 29, 33]可以绕过地址空间布局随机化(ASLR)。
此外,传统的隔离方法只能限制控制流跳转到/从驱动程序的路径,而无法防止驱动程序内部的非法控制流。在实际攻击场景中,除了入口流和出口流之外,不可信驱动程序内部的控制流也可能被劫持。例如,攻击者可以利用驱动程序中的内存漏洞发起 CRAs,并仅使用驱动程序代码构建完整的工具链。尽管现有的 KASLR 方法[6, 12, 28]可以通过隐藏代码地址来防御非法控制流,但如果要对物理页面中的运行二进制代码进行重新随机化,则需要预先处理驱动程序的源代码,这使得它们对闭源驱动程序无效。相比之下,一些其他 KASLR 方法可以在加载或链接阶段更改驱动程序的基地址而无需分析源代码,但它们无法在驱动程序运行时重新随机化驱动程序代码。更糟糕的是,现有研究表明,代码探测[3, 8, 24, 29, 33]可以绕过地址空间布局随机化(ASLR)。
To solve the above problems, we propose a method Dbox to isolate and detect the untrusted drivers. Dbox detects the control flows and changes the space locations of untrusted drivers at their running stage. Both changing space locations and detecting control flows are based on the execution logic of the untrusted drivers.
为了解决上述问题,我们提出了一种方法 Dbox 来隔离和检测不可信驱动程序。Dbox 检测控制流并在不可信驱动程序运行阶段改变其空间位置。改变空间位置和检测控制流都是基于不可信驱动程序的执行逻辑。
为了解决上述问题,我们提出了一种方法 Dbox 来隔离和检测不可信驱动程序。Dbox 检测控制流并在不可信驱动程序运行阶段改变其空间位置。改变空间位置和检测控制流都是基于不可信驱动程序的执行逻辑。
However, for the untrusted drivers without source code, their execution logic is unknown. Fortunately, the execution logic can be obtained from the context of the running binary code of the driver. Therefore, we can obtain drivers’ execution logic by capturing and analyzing their context. For example, for the indirect control transfer (ICT) instruction call *pointer, we can read the pointer to obtain its accurate jump target when the instruction is being executed. Based on real-time execution logic, we can check the legitimacy of the control flow based on code logic and data logic. And we can also ensure the driver code whose space locations have been changed maintains the original calling relationship. In summary, the contributions of this paper are as follows:
然而,对于没有源代码的不可信驱动程序,其执行逻辑是未知的。幸运的是,可以从驱动程序运行的二进制代码的上下文中获取执行逻辑。因此,我们可以通过捕获和分析其上下文来获取驱动程序的执行逻辑。例如,对于间接控制转移(ICT)指令 call *pointer,我们可以在指令执行时读取指针以获取其准确的跳转目标。基于实时执行逻辑,我们可以根据代码逻辑和数据逻辑检查控制流的合法性。我们还可以确保空间位置已更改的驱动代码保持原有的调用关系。总之,本文的贡献如下:
然而,对于没有源代码的不可信驱动程序,其执行逻辑是未知的。幸运的是,可以从驱动程序运行的二进制代码的上下文中获取执行逻辑。因此,我们可以通过捕获和分析其上下文来获取驱动程序的执行逻辑。例如,对于间接控制转移(ICT)指令 call *pointer,我们可以在指令执行时读取指针以获取其准确的跳转目标。基于实时执行逻辑,我们可以根据代码逻辑和数据逻辑检查控制流的合法性。我们还可以确保空间位置已更改的驱动代码保持原有的调用关系。总之,本文的贡献如下:
1) Create a lightweight hypervisor. Combining hardware-assisted virtualization, we establish a lightweight hypervisor. Unlike existing VMMs, our self-developed hypervisor does not provide complex virtual machine management mechanisms, but tracks and monitors running targets. It can monitor and control the data access and code execution of closed-source drivers, thereby extracting the context during their runtime. By analyzing the running context, we can obtain accurate execution logic without building a complex CFG.
1) 创建一个轻量级的管理程序。结合硬件辅助虚拟化,我们建立了一个轻量级的管理程序。与现有的 VMM 不同,我们自主研发的管理程序不提供复杂的虚拟机管理机制,而是跟踪和监控运行目标。它可以监控和控制闭源驱动程序的数据访问和代码执行,从而提取其运行时的上下文。通过分析运行上下文,我们可以在不构建复杂 CFG 的情况下获得准确的执行逻辑。
1) 创建一个轻量级的管理程序。结合硬件辅助虚拟化,我们建立了一个轻量级的管理程序。与现有的 VMM 不同,我们自主研发的管理程序不提供复杂的虚拟机管理机制,而是跟踪和监控运行目标。它可以监控和控制闭源驱动程序的数据访问和代码执行,从而提取其运行时的上下文。通过分析运行上下文,我们可以在不构建复杂 CFG 的情况下获得准确的执行逻辑。
2) Propose a sliding space mechanism. This mechanism isolates the untrusted driver in a private space. All entry_flows and exit_flows can be captured by adjusting the private space. Moreover, the driver space can be dynamically slid, which can defend against code probes.
2) 提出一种滑动空间机制。该机制将不可信的驱动程序隔离在私有空间中。通过调整私有空间,可以捕获所有入口流和出口流。此外,驱动程序空间可以动态滑动,从而防御代码探测。
2) 提出一种滑动空间机制。该机制将不可信的驱动程序隔离在私有空间中。通过调整私有空间,可以捕获所有入口流和出口流。此外,驱动程序空间可以动态滑动,从而防御代码探测。
3) Propose a detection method for entry_flows and exit_flows. Due to the sliding space, all control flows transferred to the untrusted drivers can be captured. Meanwhile, the control flows inside the driver space triggered by ICT instructions will be transformed into exit_flows and they can be captured. All entry_flows and exit_flows will be tracked and detected. This method can defend against kernel rootkits and CRAs.
3) 提出一种入口流和出口流的检测方法。由于滑动空间的存在,所有转移到不可信驱动程序的控制流都可以被捕获。同时,由 ICT 指令触发的驱动程序空间内的控制流将转换为出口流并被捕获。所有入口流和出口流都将被跟踪和检测。该方法可以防御内核 rootkit 和 CRA。
3) 提出一种入口流和出口流的检测方法。由于滑动空间的存在,所有转移到不可信驱动程序的控制流都可以被捕获。同时,由 ICT 指令触发的驱动程序空间内的控制流将转换为出口流并被捕获。所有入口流和出口流都将被跟踪和检测。该方法可以防御内核 rootkit 和 CRA。
2 RELATED WORK
To prevent drivers from breaking the control flow integrity (CFI) in kernel, researchers have proposed various methods. These methods can be divided into control data protection, memory isolation, and KASLR. In this section, we introduce them separately.
为了防止驱动程序破坏内核中的控制流完整性(CFI),研究人员提出了多种方法。这些方法可以分为控制数据保护、内存隔离和 KASLR。在本节中,我们将分别介绍这些方法。
为了防止驱动程序破坏内核中的控制流完整性(CFI),研究人员提出了多种方法。这些方法可以分为控制数据保护、内存隔离和 KASLR。在本节中,我们将分别介绍这些方法。
Control data protection. Malicious drivers can hijack control flows by tampering with control data. In theory, protecting control data can prevent illegal control flows of malicious drivers. Based on this principle, VTW [19] defend against kernel rootkits by checking control data. However, the control data it can detect is fixed, which results in poor detection on rootkit variants. To cover all legitimate control flow paths, existing methods, such as Ge [7] and FINECFI [16], establish accurate CFGs, which can be used to detect the tampered control data. However, it’s impossible to establish a highprecision CFG for the untrusted driver without source code before it runs. The hardware technology ARM Pointer Authentication can be used to protect the integrity of function pointers when the binary code is running. It has been adopted by existing security methods, such as PATTER[41] and PAL[42]. These methods require analyzing the source code to locate all pointers when deployed, and they are ineffective for closed-source drivers.
控制数据保护。恶意驱动程序可以通过篡改控制数据来劫持控制流。理论上,保护控制数据可以防止恶意驱动程序的非法控制流。基于这一原则,VTW [19] 通过检查控制数据来防御内核 rootkit。然而,它可以检测的控制数据是固定的,这导致对 rootkit 变种的检测效果较差。为了覆盖所有合法的控制流路径,现有的方法,如 Ge [7] 和 FINECFI [16],建立了精确的 CFG,可以用于检测被篡改的控制数据。然而,在没有源代码的情况下,不可能为不可信的驱动程序建立高精度的 CFG。硬件技术 ARM 指针认证可以在二进制代码运行时保护函数指针的完整性。它已被现有的安全方法采用,如 PATTER[41] 和 PAL[42]。这些方法在部署时需要分析源代码以定位所有指针,对于闭源驱动程序无效。
控制数据保护。恶意驱动程序可以通过篡改控制数据来劫持控制流。理论上,保护控制数据可以防止恶意驱动程序的非法控制流。基于这一原则,VTW [19] 通过检查控制数据来防御内核 rootkit。然而,它可以检测的控制数据是固定的,这导致对 rootkit 变种的检测效果较差。为了覆盖所有合法的控制流路径,现有的方法,如 Ge [7] 和 FINECFI [16],建立了精确的 CFG,可以用于检测被篡改的控制数据。然而,在没有源代码的情况下,不可能为不可信的驱动程序建立高精度的 CFG。硬件技术 ARM 指针认证可以在二进制代码运行时保护函数指针的完整性。它已被现有的安全方法采用,如 PATTER[41] 和 PAL[42]。这些方法在部署时需要分析源代码以定位所有指针,对于闭源驱动程序无效。
Memory isolation. Memory isolation constructs an isolation layer between the target driver and other objects, through which only authorized access can pass. Gateway[34] hardens the kernel API to drivers by isolating driver code in a different memory address space. UnderBridge[9] moves the OS components of a microkernel between user space and kernel space at runtime while enforcing consistent isolation. LXFI[22] isolates kernel modules from the core kernel so that vulnerabilities in kernel modules cannot lead to a privilege escalation attack. MemCat[27] separates data based on attacker control to mitigate the exploitation of memory corruption vulnerabilities such as use-after-free and use-after-return. TDI [23] isolates memory objects of different colors in separate memory arenas and uses efficient compiler instrumentation to constrain loads to the arena of the intended color by construction.TZ-RKP[1] provides a complete and feasible isolation layer for kernel using TrustZone. kRXˆ [30] is based on execute-only memory and code diversification, and it can benefit from hardware support to optimize performance. The above methods can detect the control flows jumping to/from the target drivers. However, they have a same limitation. That is they cannot detect the control flows inside the drivers. In addition, compiler-based methods, such as LXFI [22] and TDI [23], cannot construct an effective isolation layer during binary code generation if their protection targets are closed-source drivers.
内存隔离。内存隔离在目标驱动程序和其他对象之间构建了一个隔离层,只有经过授权的访问才能通过。Gateway[34] 通过将驱动程序代码隔离在不同的内存地址空间中来强化内核 API 对驱动程序的访问。UnderBridge[9] 在运行时将微内核的 OS 组件在用户空间和内核空间之间移动,同时强制执行一致的隔离。LXFI[22] 将内核模块与核心内核隔离,以防止内核模块中的漏洞导致权限提升攻击。MemCat[27] 根据攻击者的控制分离数据,以减轻使用后释放和使用后返回等内存损坏漏洞的利用。TDI [23] 将不同颜色的内存对象隔离在单独的内存区域中,并使用高效的编译器插桩来约束加载到预期颜色的区域。TZ-RKP[1] 使用 TrustZone 为内核提供了一个完整且可行的隔离层。kRXˆ [30] 基于只执行内存和代码多样化,并且可以从硬件支持中受益以优化性能。上述方法可以检测跳转到/从目标驱动程序的控制流。 然而,它们有一个共同的限制。那就是它们无法检测驱动程序内部的控制流。此外,基于编译器的方法,如 LXFI [22]和 TDI [23],如果其保护目标是闭源驱动程序,则无法在二进制代码生成期间构建有效的隔离层。
内存隔离。内存隔离在目标驱动程序和其他对象之间构建了一个隔离层,只有经过授权的访问才能通过。Gateway[34] 通过将驱动程序代码隔离在不同的内存地址空间中来强化内核 API 对驱动程序的访问。UnderBridge[9] 在运行时将微内核的 OS 组件在用户空间和内核空间之间移动,同时强制执行一致的隔离。LXFI[22] 将内核模块与核心内核隔离,以防止内核模块中的漏洞导致权限提升攻击。MemCat[27] 根据攻击者的控制分离数据,以减轻使用后释放和使用后返回等内存损坏漏洞的利用。TDI [23] 将不同颜色的内存对象隔离在单独的内存区域中,并使用高效的编译器插桩来约束加载到预期颜色的区域。TZ-RKP[1] 使用 TrustZone 为内核提供了一个完整且可行的隔离层。kRXˆ [30] 基于只执行内存和代码多样化,并且可以从硬件支持中受益以优化性能。上述方法可以检测跳转到/从目标驱动程序的控制流。 然而,它们有一个共同的限制。那就是它们无法检测驱动程序内部的控制流。此外,基于编译器的方法,如 LXFI [22]和 TDI [23],如果其保护目标是闭源驱动程序,则无法在二进制代码生成期间构建有效的隔离层。
KASLR. Traditional ASLR methods, such as fASLR [31], PointerScope [40], and Mardu [14], rely on source code analysis, which makes them unable to be applied to closed-source objects. Renanz [38] and RuntimeASLR [21] can randomize closed-source objects, but they introduce significant overhead. KASLR can hide kernel addresses, thereby preventing CRAs from chaining gadgets [18]. Adelie [28] enables efficient continuous KASLR for LKMs by using the PIC (position-independent code) model to make it hard to inject ROP gadgets through modules regardless of gadget’s origin. KASLR-MT[36] is also a kernel randomization method for multi-tenant cloud systems, which maximizes memory savings rate. However, existing KASLR methods can be bypassed by code probes [17]. Moreover, KASLR needs to be completed between the occurrence of code probes and CRAs. Otherwise, it is invalid and can only incur overhead. How to choose an appropriate randomization time point is a challenge faced by all KASLR methods.
KASLR。传统的 ASLR 方法,如 fASLR [31]、PointerScope [40]和 Mardu [14],依赖于源代码分析,这使得它们无法应用于闭源对象。Renanz [38]和 RuntimeASLR [21]可以随机化闭源对象,但它们引入了显著的开销。KASLR 可以隐藏内核地址,从而防止 CRAs 从链接小工具[18]。Adelie [28]通过使用 PIC(位置独立代码)模型,使无论小工具的来源如何,都难以通过模块注入 ROP 小工具,从而实现 LKMs 的高效连续 KASLR。KASLR-MT[36]也是一种针对多租户云系统的内核随机化方法,它最大化了内存节省率。然而,现有的 KASLR 方法可以通过代码探测[17]来绕过。此外,KASLR 需要在代码探测和 CRAs 发生之间完成。否则,它将无效,只会产生开销。如何选择一个合适的随机化时间点是所有 KASLR 方法面临的挑战。
KASLR。传统的 ASLR 方法,如 fASLR [31]、PointerScope [40]和 Mardu [14],依赖于源代码分析,这使得它们无法应用于闭源对象。Renanz [38]和 RuntimeASLR [21]可以随机化闭源对象,但它们引入了显著的开销。KASLR 可以隐藏内核地址,从而防止 CRAs 从链接小工具[18]。Adelie [28]通过使用 PIC(位置独立代码)模型,使无论小工具的来源如何,都难以通过模块注入 ROP 小工具,从而实现 LKMs 的高效连续 KASLR。KASLR-MT[36]也是一种针对多租户云系统的内核随机化方法,它最大化了内存节省率。然而,现有的 KASLR 方法可以通过代码探测[17]来绕过。此外,KASLR 需要在代码探测和 CRAs 发生之间完成。否则,它将无效,只会产生开销。如何选择一个合适的随机化时间点是所有 KASLR 方法面临的挑战。
Compared with existing methods, our method Dbox is a method specifically designed to detect illegal control flows caused by closedsource drivers. Dbox captures the execution context in real-time by monitoring the data access and code execution of running drivers. The call relationships between different code blocks can be obtained by analyzing the captured context. Based on this design, we can avoid building a high-precision CFG for the closed-source drivers. Meanwhile, Dbox does not randomize physical code like existing KASLR methods, but dynamically changes the virtual space of the running drivers. Therefore, we don’t need to maintain complex calling relationships for the randomized code.
与现有方法相比,我们的方法 Dbox 是专门设计用于检测由闭源驱动程序引起的非法控制流的方法。Dbox 通过监控运行中驱动程序的数据访问和代码执行来实时捕获执行上下文。通过分析捕获的上下文,可以获得不同代码块之间的调用关系。基于此设计,我们可以避免为闭源驱动程序构建高精度的 CFG。同时,Dbox 不像现有的 KASLR 方法那样随机化物理代码,而是动态改变运行中驱动程序的虚拟空间。因此,我们不需要维护随机化代码的复杂调用关系。
与现有方法相比,我们的方法 Dbox 是专门设计用于检测由闭源驱动程序引起的非法控制流的方法。Dbox 通过监控运行中驱动程序的数据访问和代码执行来实时捕获执行上下文。通过分析捕获的上下文,可以获得不同代码块之间的调用关系。基于此设计,我们可以避免为闭源驱动程序构建高精度的 CFG。同时,Dbox 不像现有的 KASLR 方法那样随机化物理代码,而是动态改变运行中驱动程序的虚拟空间。因此,我们不需要维护随机化代码的复杂调用关系。
3 ASSUMPTIONS AND ATTACK VECTORS
First, we assume attackers can inject untrusted drivers into the OS. In practice, untrusted drivers come from the hot-plug hardware or the closed-source objects installed by users. An open OS does not prevent root users from installing untrusted drivers. Second, we assume attackers can exploit memory vulnerabilities to launch control flow attacks. Overflow vulnerabilities are widely distributed in C++/C programs, which can be used to tamper with control data, such as return addresses or function pointers. Third, we assume attackers can use the techniques [8, 29, 33] to probe driver code. For example, attackers can directly read driver code and filter out the code snippets that match specific gadget forms. The threat model used in this paper includes the following 5 attack vectors:
首先,我们假设攻击者可以将不可信的驱动程序注入操作系统。实际上,不可信的驱动程序来自热插拔硬件或用户安装的闭源对象。开放的操作系统不会阻止根用户安装不可信的驱动程序。其次,我们假设攻击者可以利用内存漏洞发起控制流攻击。C++/C 程序中广泛存在溢出漏洞,可以用于篡改控制数据,如返回地址或函数指针。第三,我们假设攻击者可以使用技术 [8, 29, 33] 来探测驱动程序代码。例如,攻击者可以直接读取驱动程序代码并筛选出符合特定小工具形式的代码片段。本文使用的威胁模型包括以下 5 个攻击向量:
首先,我们假设攻击者可以将不可信的驱动程序注入操作系统。实际上,不可信的驱动程序来自热插拔硬件或用户安装的闭源对象。开放的操作系统不会阻止根用户安装不可信的驱动程序。其次,我们假设攻击者可以利用内存漏洞发起控制流攻击。C++/C 程序中广泛存在溢出漏洞,可以用于篡改控制数据,如返回地址或函数指针。第三,我们假设攻击者可以使用技术 [8, 29, 33] 来探测驱动程序代码。例如,攻击者可以直接读取驱动程序代码并筛选出符合特定小工具形式的代码片段。本文使用的威胁模型包括以下 5 个攻击向量:
Vector 1: Kernel rootkits. kernel rootkits are loaded into the kernel as LKMs, which can modify kernel control data, such as system call tables, and hijack kernel control flows.
向量 1:内核 rootkit。内核 rootkit 作为可加载内核模块(LKMs)加载到内核中,可以修改内核控制数据,如系统调用表,并劫持内核控制流。
向量 1:内核 rootkit。内核 rootkit 作为可加载内核模块(LKMs)加载到内核中,可以修改内核控制数据,如系统调用表,并劫持内核控制流。
Vector 2: The CRAs whose gadgets are located in different objects. The gadgets used by such CRAs are partly from untrusted drivers, and partly from the core kernel and other LKMs.
向量 2:其小工具位于不同对象中的 CRAs。此类 CRAs 使用的小工具部分来自不可信的驱动程序,部分来自核心内核和其他 LKMs。
向量 2:其小工具位于不同对象中的 CRAs。此类 CRAs 使用的小工具部分来自不可信的驱动程序,部分来自核心内核和其他 LKMs。
Vector 3: The CRAs whose gadgets are located in a same driver. That is, attackers can build a complete gadget chain using the code snippets in a single driver.
向量 3:位于同一驱动程序中的 CRAs 的小工具。也就是说,攻击者可以使用单个驱动程序中的代码片段构建完整的 gadget 链。
向量 3:位于同一驱动程序中的 CRAs 的小工具。也就是说,攻击者可以使用单个驱动程序中的代码片段构建完整的 gadget 链。
Vector 4: Code reading probes. Attackers can bypass KASLR by directly reading the driver code.
向量 4:代码读取探测。攻击者可以通过直接读取驱动程序代码来绕过 KASLR。
向量 4:代码读取探测。攻击者可以通过直接读取驱动程序代码来绕过 KASLR。
Vector 5: Code pointer probes. Attackers can bypass KASLR by reading the return address stored on the stack and the pointers stored in data structures.
向量 5:代码指针探测。攻击者可以通过读取堆栈上存储的返回地址和数据结构中存储的指针来绕过 KASLR。
向量 5:代码指针探测。攻击者可以通过读取堆栈上存储的返回地址和数据结构中存储的指针来绕过 KASLR。
4 MOTIVATION
The purpose of Dbox is to isolate the untrusted driver and discover the illegal control flows related to it without analyzing or compiling its source code. The defense targets include kernel rootkits, code probes, and CRAs. In summary, the motivations of this paper are as follows:
Dbox 的目的是隔离不可信的驱动程序并发现与其相关的非法控制流,而无需分析或编译其源代码。防御目标包括内核 rootkit、代码探针和 CRAs。总之,本文的动机如下:
Dbox 的目的是隔离不可信的驱动程序并发现与其相关的非法控制流,而无需分析或编译其源代码。防御目标包括内核 rootkit、代码探针和 CRAs。总之,本文的动机如下:
1) Get rid of dependence on source code. The execution logic of a closed-source driver is unknown. The unknown execution logic can hinder KASLR methods from maintaining the original calling relationship after re-randomizing driver code. Moreover, the unknown logic prevents traditional CFI methods from precollecting instruction boundaries to detect illegal control flows. For the untrusted drivers without source code, we can obtain accurate execution logic by capturing and analyzing their execution context.
1) 摆脱对源代码的依赖。闭源驱动程序的执行逻辑是未知的。未知的执行逻辑可能会阻碍 KASLR 方法在重新随机化驱动代码后保持原有的调用关系。此外,未知的逻辑阻止了传统的 CFI 方法预先收集指令边界以检测非法控制流。对于没有源代码的不可信驱动程序,我们可以通过捕获和分析其执行上下文来获得准确的执行逻辑。
1) 摆脱对源代码的依赖。闭源驱动程序的执行逻辑是未知的。未知的执行逻辑可能会阻碍 KASLR 方法在重新随机化驱动代码后保持原有的调用关系。此外,未知的逻辑阻止了传统的 CFI 方法预先收集指令边界以检测非法控制流。对于没有源代码的不可信驱动程序,我们可以通过捕获和分析其执行上下文来获得准确的执行逻辑。
2) Isolate the untrusted driver dynamically. The untrusted driver will be isolated in a private space, which can capture code probes. If there is a code probe, the driver space will slide dynamically. Unlike the existing KASLR methods, we change the untrusted driver’s virtual space instead of its physical space, which can avoid frequent memory operations.
2) 动态隔离不可信驱动。不可信驱动将在一个私有空间中被隔离,该空间可以捕获代码探测。如果存在代码探测,驱动空间将动态滑动。与现有的 KASLR 方法不同,我们改变的是不可信驱动的虚拟空间而不是其物理空间,这样可以避免频繁的内存操作。
2) 动态隔离不可信驱动。不可信驱动将在一个私有空间中被隔离,该空间可以捕获代码探测。如果存在代码探测,驱动空间将动态滑动。与现有的 KASLR 方法不同,我们改变的是不可信驱动的虚拟空间而不是其物理空间,这样可以避免频繁的内存操作。
3) Filter out illegal control flows related to untrusted drivers. We prevent attackers from redirecting illegal control flows to untrusted drivers and hijacking control flows in untrusted drivers. Compared with existing methods, it does not use fixed instruction boundaries to constrain control flows, but detects control flows based on runtime context.
3) 过滤与不可信驱动程序相关的非法控制流。我们防止攻击者将非法控制流重定向到不可信驱动程序并劫持不可信驱动程序中的控制流。与现有方法相比,该方法不使用固定的指令边界来约束控制流,而是基于运行时上下文检测控制流。
3) 过滤与不可信驱动程序相关的非法控制流。我们防止攻击者将非法控制流重定向到不可信驱动程序并劫持不可信驱动程序中的控制流。与现有方法相比,该方法不使用固定的指令边界来约束控制流,而是基于运行时上下文检测控制流。
5 METHODOLOGY
The overall design of Dbox is shown in Figure 1. Dbox isolates the untrusted driver in a private space. This space is not fixed. It will dynamically slide along with the driver execution, which can defend against code probes. Moreover, all incoming/outgoing control flows need to pass through an isolation layer, and only legal control flows can jump to the target locations. Meanwhile, to ensure the control flows inside the untrusted driver are not hijacked, all internal control flows triggered by ICT instructions need to go through a transfer site to jump to the right locations. Under the protection of Dbox, code probe attacks cannot locate the executable code, and the illegal control flows can also be detected.
Dbox 的整体设计如图 1 所示。Dbox 将不可信驱动程序隔离在一个私有空间中。这个空间不是固定的,它会随着驱动程序的执行动态滑动,从而可以防御代码探测。此外,所有进出的控制流都需要通过一个隔离层,只有合法的控制流才能跳转到目标位置。同时,为了确保不可信驱动程序内部的控制流不被劫持,所有由 ICT 指令触发的内部控制流都需要通过一个转移站点才能跳转到正确的位置。在 Dbox 的保护下,代码探测攻击无法定位可执行代码,非法控制流也可以被检测到。
Dbox 的整体设计如图 1 所示。Dbox 将不可信驱动程序隔离在一个私有空间中。这个空间不是固定的,它会随着驱动程序的执行动态滑动,从而可以防御代码探测。此外,所有进出的控制流都需要通过一个隔离层,只有合法的控制流才能跳转到目标位置。同时,为了确保不可信驱动程序内部的控制流不被劫持,所有由 ICT 指令触发的内部控制流都需要通过一个转移站点才能跳转到正确的位置。在 Dbox 的保护下,代码探测攻击无法定位可执行代码,非法控制流也可以被检测到。
Figure 1: The overall design of Dbox
图 1:Dbox 的总体设计
图 1:Dbox 的总体设计
Our detection targets are the untrusted drivers without source code. We cannot obtain their behavior characteristics through static analysis, nor can we preset checkpoints in appropriate locations. Faced with these challenges, we build a lightweight hypervisor [20], as shown in Figure 2. Similar to KVM, it is loaded into the kernel as an LKM. Unlike the KVM, its main function is not to manage virtual machines, but to utilize EPT (Extended Page Tables), VMX (Virtual Machine Extension), and VT-d (Virtualization Technology for Directed I/O) to monitor and control drivers. Moreover, the hypervisor utilizes VMX root and VMX non-root to divide the running modes of the original OS into two types: host and guest. Under normal scenarios, the OS runs in the guest mode. When a specific event occurs, the running mode switches from guest to host, which is called a system trap. Combining EPT and VMX, multiple trap events can be set, including memory permission violation, page directory switching, breakpoint setting, execution of specific instructions (such as int3, vmcall), interrupts, single step debugging, and exception protection, etc. In addition, the CPU context can also be rewritten by modifying the fields in VMCS (Virtual Machine Control Structure) to achieve execution control. For example, by modifying the guest rip in VMCS, control flow redirection can be achieved. Based on the above designs, Dbox can monitor and control the behaviors of untrusted drivers without relying on source code.
我们的检测目标是没有源代码的不可信驱动程序。我们无法通过静态分析获得它们的行为特征,也无法在适当的位置预设检查点。面对这些挑战,我们构建了一个轻量级的 hypervisor [20],如图 2 所示。类似于 KVM,它作为 LKM 加载到内核中。与 KVM 不同,它的主要功能不是管理虚拟机,而是利用 EPT(扩展页表)、VMX(虚拟机扩展)和 VT-d(定向 I/O 的虚拟化技术)来监控和控制驱动程序。此外,hypervisor 利用 VMX 根和 VMX 非根将原始操作系统的运行模式分为两种类型:主机和客户机。在正常情况下,操作系统以客户机模式运行。当特定事件发生时,运行模式从客户机切换到主机,这被称为系统陷阱。结合 EPT 和 VMX,可以设置多种陷阱事件,包括内存权限违规、页目录切换、断点设置、特定指令的执行(如 int3、vmcall)、中断、单步调试和异常保护等。 此外,通过修改 VMCS(虚拟机控制结构)中的字段,也可以重写 CPU 上下文以实现执行控制。例如,通过修改 VMCS 中的 guest rip,可以实现控制流重定向。基于上述设计,Dbox 可以在不依赖源代码的情况下监控和控制不可信驱动的行为。
我们的检测目标是没有源代码的不可信驱动程序。我们无法通过静态分析获得它们的行为特征,也无法在适当的位置预设检查点。面对这些挑战,我们构建了一个轻量级的 hypervisor [20],如图 2 所示。类似于 KVM,它作为 LKM 加载到内核中。与 KVM 不同,它的主要功能不是管理虚拟机,而是利用 EPT(扩展页表)、VMX(虚拟机扩展)和 VT-d(定向 I/O 的虚拟化技术)来监控和控制驱动程序。此外,hypervisor 利用 VMX 根和 VMX 非根将原始操作系统的运行模式分为两种类型:主机和客户机。在正常情况下,操作系统以客户机模式运行。当特定事件发生时,运行模式从客户机切换到主机,这被称为系统陷阱。结合 EPT 和 VMX,可以设置多种陷阱事件,包括内存权限违规、页目录切换、断点设置、特定指令的执行(如 int3、vmcall)、中断、单步调试和异常保护等。 此外,通过修改 VMCS(虚拟机控制结构)中的字段,也可以重写 CPU 上下文以实现执行控制。例如,通过修改 VMCS 中的 guest rip,可以实现控制流重定向。基于上述设计,Dbox 可以在不依赖源代码的情况下监控和控制不可信驱动的行为。
To filter out the illegal control flows, we must capture the activities of control flow transfers. Control flow hijacking often occurs at the moment when the control flow enters or exits an untrusted driver. The differences between illegal control flows and legal control flows are reflected in the execution context at this moment. For example, the VFS (virtual file system) function pointer used by the kernel rootkit adore-ng no longer points to the original kernel code but rather to rootkit code. As a result, we can detect illegal control flows by analyzing the execution context. However, after this moment, the tampered control data (such as return addresses) may be recycled, which results the malicious action cannot be prevented in a timely manner. Therefore, we need to capture the entry_flows and exit_flows and extracts their context in real time.
为了过滤出非法控制流,我们必须捕获控制流转移的活动。控制流劫持通常发生在控制流进入或退出不可信驱动程序的时刻。非法控制流与合法控制流之间的差异体现在这一时刻的执行上下文中。例如,内核 rootkit adore-ng 使用的 VFS(虚拟文件系统)函数指针不再指向原始内核代码,而是指向 rootkit 代码。因此,我们可以通过分析执行上下文来检测非法控制流。然而,在这一时刻之后,被篡改的控制数据(如返回地址)可能会被回收,导致恶意行为无法及时阻止。因此,我们需要捕获 entry_flows 和 exit_flows,并实时提取它们的上下文。
为了过滤出非法控制流,我们必须捕获控制流转移的活动。控制流劫持通常发生在控制流进入或退出不可信驱动程序的时刻。非法控制流与合法控制流之间的差异体现在这一时刻的执行上下文中。例如,内核 rootkit adore-ng 使用的 VFS(虚拟文件系统)函数指针不再指向原始内核代码,而是指向 rootkit 代码。因此,我们可以通过分析执行上下文来检测非法控制流。然而,在这一时刻之后,被篡改的控制数据(如返回地址)可能会被回收,导致恶意行为无法及时阻止。因此,我们需要捕获 entry_flows 和 exit_flows,并实时提取它们的上下文。
Figure 2: The overall design of the customized hypervisor
图 2:定制 hypervisor 的总体设计
图 2:定制 hypervisor 的总体设计
To capture entry_flows and exit_flows, we propose a sliding space mechanism, which dynamically adjusts the space and permissions of the driver. Dbox allocates redundant virtual spaces for untrusted drivers. The real address space of the untrusted driver can randomly slide among redundant spaces. At any time, there is only one space (called current space) that will be mapped to real code pages through EPT. Other redundant spaces and the original space of the untrusted driver are mapped to a trap page that is nonreadable, non-writable, and non-executable. Therefore, all control flows that jump to the original space can be captured due to triggering system traps. In the same way, by remapping the core kernel and other LKMs and adjusting their code permissions, all control flows that jump out of untrusted drivers can also be captured.
为了捕获入口流和出口流,我们提出了一种滑动空间机制,该机制动态调整驱动程序的空间和权限。Dbox 为不可信驱动程序分配冗余的虚拟空间。不可信驱动程序的真实地址空间可以在冗余空间中随机滑动。任何时候,只有一个空间(称为当前空间)会被通过 EPT 映射到真实的代码页。其他冗余空间和不可信驱动程序的原始空间被映射到一个不可读、不可写且不可执行的陷阱页。因此,所有跳转到原始空间的控制流都会因触发系统陷阱而被捕获。同样地,通过重新映射核心内核和其他 LKM 并调整其代码权限,所有从不可信驱动程序跳出的控制流也可以被捕获。
为了捕获入口流和出口流,我们提出了一种滑动空间机制,该机制动态调整驱动程序的空间和权限。Dbox 为不可信驱动程序分配冗余的虚拟空间。不可信驱动程序的真实地址空间可以在冗余空间中随机滑动。任何时候,只有一个空间(称为当前空间)会被通过 EPT 映射到真实的代码页。其他冗余空间和不可信驱动程序的原始空间被映射到一个不可读、不可写且不可执行的陷阱页。因此,所有跳转到原始空间的控制流都会因触发系统陷阱而被捕获。同样地,通过重新映射核心内核和其他 LKM 并调整其代码权限,所有从不可信驱动程序跳出的控制流也可以被捕获。
In practice, the control flow enters untrusted drivers through specific points (such as syscall). Dbox creates an isolation layer. Based on the layer, all entry_flows must pass through specific points to enter untrusted drivers. Otherwise a system trap will be triggered and the control flow will be checked according to security strategies.
在实际中,控制流通过特定点(如系统调用)进入不可信驱动。Dbox 创建了一个隔离层。基于该层,所有入口流必须通过特定点才能进入不可信驱动。否则将触发系统陷阱,并根据安全策略检查控制流。
在实际中,控制流通过特定点(如系统调用)进入不可信驱动。Dbox 创建了一个隔离层。基于该层,所有入口流必须通过特定点才能进入不可信驱动。否则将触发系统陷阱,并根据安全策略检查控制流。
In theory, legal control flows can exit the driver through direct transfer instructions, indirect transfer instructions, and return instructions. In the real world, drivers only use call address, call *xx, and ret to transfer control flows to other LKMs or core kernel, as shown in Table 1. In real attack scenarios (such as ROP and JOP), CRAs transfer illegal control flows to the next gadget through ICT instructions or return instructions[32]. The code snippets containing call *pointer or ret in untrusted drivers are key components to build gadget chains. To defend against CRAs, all exit_flows driven by ICT instructions and ret should be detected and analyzed. For legal control flows, Dbox uses a transfer site to transfer them to right locations.
理论上,合法控制流可以通过直接转移指令、间接转移指令和返回指令从驱动程序中流出。在现实世界中,驱动程序仅使用调用地址、调用 *xx 和 ret 将控制流转移到其他 LKM 或核心内核,如表 1 所示。在实际攻击场景(如 ROP 和 JOP)中,CRAs 通过 ICT 指令或返回指令[32]将非法控制流转移到下一个小工具。包含调用 *指针或 ret 的不受信任驱动程序中的代码片段是构建小工具链的关键组件。为了防御 CRAs,应检测和分析由 ICT 指令和 ret 驱动的所有退出流。对于合法控制流,Dbox 使用一个转移站点将它们转移到正确的位置。
理论上,合法控制流可以通过直接转移指令、间接转移指令和返回指令从驱动程序中流出。在现实世界中,驱动程序仅使用调用地址、调用 *xx 和 ret 将控制流转移到其他 LKM 或核心内核,如表 1 所示。在实际攻击场景(如 ROP 和 JOP)中,CRAs 通过 ICT 指令或返回指令[32]将非法控制流转移到下一个小工具。包含调用 *指针或 ret 的不受信任驱动程序中的代码片段是构建小工具链的关键组件。为了防御 CRAs,应检测和分析由 ICT 指令和 ret 驱动的所有退出流。对于合法控制流,Dbox 使用一个转移站点将它们转移到正确的位置。
Both kernel rootkits and CRAs can tamper with the control data to make it point to an absolute location. Kernel rootkits hijack the control flow that should be transferred to the kernel to an untrusted driver, while CRAs redirect the control flow to the gadgets that have no calling relationship with the current code snippets. Illegal control flows inevitably violate code logic or data logic. For example, a tampered function pointer no longer points to the function header, but rather points to the interior of another function, which violates data logic; the instruction ret, whose machine code is c3, is not an opcode in binary code but an operand, which violates the code logic. Based on the above designs, Dbox can detect illegal control flows and discover tampered control data by analyzing the code logic and data logic. Meanwhile, the private space can detect code probes, which will trigger the sliding space mechanism.
内核 rootkit 和 CRAs 都可以篡改控制数据,使其指向一个绝对位置。内核 rootkit 劫持了应转移到内核的控制流,将其转移到不可信的驱动程序,而 CRAs 将控制流重定向到与当前代码片段没有调用关系的小工具。非法控制流不可避免地违反代码逻辑或数据逻辑。例如,被篡改的函数指针不再指向函数头,而是指向另一个函数的内部,这违反了数据逻辑;指令 ret,其机器码为 c3,在二进制代码中不是操作码而是操作数,这违反了代码逻辑。基于上述设计,Dbox 可以通过分析代码逻辑和数据逻辑来检测非法控制流并发现被篡改的控制数据。同时,私有空间可以检测代码探针,这将触发滑动空间机制。
内核 rootkit 和 CRAs 都可以篡改控制数据,使其指向一个绝对位置。内核 rootkit 劫持了应转移到内核的控制流,将其转移到不可信的驱动程序,而 CRAs 将控制流重定向到与当前代码片段没有调用关系的小工具。非法控制流不可避免地违反代码逻辑或数据逻辑。例如,被篡改的函数指针不再指向函数头,而是指向另一个函数的内部,这违反了数据逻辑;指令 ret,其机器码为 c3,在二进制代码中不是操作码而是操作数,这违反了代码逻辑。基于上述设计,Dbox 可以通过分析代码逻辑和数据逻辑来检测非法控制流并发现被篡改的控制数据。同时,私有空间可以检测代码探针,这将触发滑动空间机制。
Table 1: Number of control flow transfer instructions in common drivers.
表 1:常见驱动程序中的控制流转移指令数量。
表 1:常见驱动程序中的控制流转移指令数量。
6 IMPLEMENTATION
6.1 Build dynamic spaces 6.1 构建动态空间
The dynamic spaces can be used to defend against code probes. The space sliding mechanism proposed in this paper is essentially a runtime randomization method, as shown in Figure 3. The real address space of the untrusted driver can slide within multiple areas in the 64-bit kernel space. In addition, the spaces of core kernel and LKMs depended by the untrusted driver can also dynamically slide.
动态空间可以用于防御代码探测。本文提出的空间滑动机制本质上是一种运行时随机化方法,如图 3 所示。不可信驱动程序的真实地址空间可以在 64 位内核空间的多个区域内滑动。此外,不可信驱动程序依赖的核心内核和加载模块(LKMs)的空间也可以动态滑动。
动态空间可以用于防御代码探测。本文提出的空间滑动机制本质上是一种运行时随机化方法,如图 3 所示。不可信驱动程序的真实地址空间可以在 64 位内核空间的多个区域内滑动。此外,不可信驱动程序依赖的核心内核和加载模块(LKMs)的空间也可以动态滑动。
For the untrusted driver being loaded, Dbox maps it to a new address space after it is loaded into kernel (module->state is MODULE_STATE_LIVE) and before it is called. The kernel function do_mmap is used to create redundant spaces for the untrusted driver. One of the redundant spaces will be selected as an executable area. The handling steps are as follows.
对于加载的不可信驱动程序,Dbox 在其被加载到内核(module->state 为 MODULE_STATE_LIVE)之后但在调用之前,将其映射到新的地址空间。内核函数 do_mmap 用于为不可信驱动程序创建冗余空间。其中一个冗余空间将被选作可执行区域。处理步骤如下。
对于加载的不可信驱动程序,Dbox 在其被加载到内核(module->state 为 MODULE_STATE_LIVE)之后但在调用之前,将其映射到新的地址空间。内核函数 do_mmap 用于为不可信驱动程序创建冗余空间。其中一个冗余空间将被选作可执行区域。处理步骤如下。
First, we read all addressing items related to the untrusted driver and filter out the last item that can address the entire driver space (called last item). In the 64-bit space, each item in four level page tables can address 512GB, 1GB, 2MB, and 4KB memory, respectively. For example, if the driver size is 1MB, the last item is located in PMD (Page Middle Directory). The page table pointed to by last item is called last table. Second, we allocate private page tables for untrusted drivers. In private page tables, the last items corresponding to the original space, current space, and redundant space point to different last tables. The last table of the current space can address all code and data of untrusted drivers. In contrast, all items in the last table of redundant spaces point to the trap page. Finally, taking the virtual address’s bits corresponding to the last item as the lower boundary, the high-order address of the redundant space can slide freely in the kernel, thereby changing the driver space.
首先,我们读取所有与不可信驱动器相关的寻址项,并过滤出可以寻址整个驱动器空间的最后一项(称为最后一项)。在 64 位空间中,四级页表中的每一项分别可以寻址 512GB、1GB、2MB 和 4KB 的内存。例如,如果驱动器大小为 1MB,最后一项位于 PMD(页中目录)。由最后一项指向的页表称为最后一表。其次,我们为不可信驱动器分配私有页表。在私有页表中,对应于原始空间、当前空间和冗余空间的最后一项指向不同的最后一表。当前空间的最后一表可以寻址不可信驱动器的所有代码和数据。相比之下,冗余空间的最后一表中的所有项都指向陷阱页。最后,以虚拟地址中对应于最后一项的位作为下界,冗余空间的高阶地址可以在内核中自由滑动,从而改变驱动器空间。
首先,我们读取所有与不可信驱动器相关的寻址项,并过滤出可以寻址整个驱动器空间的最后一项(称为最后一项)。在 64 位空间中,四级页表中的每一项分别可以寻址 512GB、1GB、2MB 和 4KB 的内存。例如,如果驱动器大小为 1MB,最后一项位于 PMD(页中目录)。由最后一项指向的页表称为最后一表。其次,我们为不可信驱动器分配私有页表。在私有页表中,对应于原始空间、当前空间和冗余空间的最后一项指向不同的最后一表。当前空间的最后一表可以寻址不可信驱动器的所有代码和数据。相比之下,冗余空间的最后一表中的所有项都指向陷阱页。最后,以虚拟地址中对应于最后一项的位作为下界,冗余空间的高阶地址可以在内核中自由滑动,从而改变驱动器空间。
However, the sliding spaces face two issues. One is the community Linux OSes restrict their KASLR range to 32 bits due to architectural constraints, which makes them vulnerable to even unsophisticated brute force ROP attachments due to low entropy [28]. Another issue is that the sliding spaces cannot hide return addresses on the stack, and the attacker (Vector 5) can bypass the sliding space by reading them. To address these issues, we propose a control flow redirection method, as shown in Figure 4.
然而,滑动空间面临两个问题。一是由于架构限制,社区 Linux 操作系统将其 KASLR 范围限制在 32 位,这使得它们容易受到即使是不复杂的暴力 ROP 攻击,因为熵较低[28]。另一个问题是滑动空间无法隐藏堆栈上的返回地址,攻击者(Vector 5)可以通过读取这些地址来绕过滑动空间。为了解决这些问题,我们提出了一种控制流重定向方法,如图 4 所示。
然而,滑动空间面临两个问题。一是由于架构限制,社区 Linux 操作系统将其 KASLR 范围限制在 32 位,这使得它们容易受到即使是不复杂的暴力 ROP 攻击,因为熵较低[28]。另一个问题是滑动空间无法隐藏堆栈上的返回地址,攻击者(Vector 5)可以通过读取这些地址来绕过滑动空间。为了解决这些问题,我们提出了一种控制流重定向方法,如图 4 所示。
To overcome the limitation of 32-bit address width in the driver code and hide return addresses, we rewrite all relative addresses related to the instruction call in binary code. Before the untrusted driver is loaded, the executable file will be analyzed to extract all call xx. After driver loading, the operand address of the call address in memory will be rewritten with a value pointing to the sliding window. The code in the sliding window rewrites the return address on the stack to make it point to an ICT instruction (jmp *target_2). Afterwards, another ICT instruction (jmp *target_1) transfers the control flow to the target function (func1) of the original instruction (call addr), which ensures the original control flow can be transferred to the right location. When the target function returns, the instruction ret uses the modified return address (addr_6) on the stack to transfer the control flow to the pre-designed ICT instruction (jmp *target_2). Finally, the ICT instruction transfers the control flow to the original return address (addr2). Unlike call address, the pointer in the call *pointer is not fixed. The function address (func_2) pointed to by the pointer will be modified to point to the address of the sliding window. The subsequent operation is the same as the processing method of handling call address. It should be noted that all the operands used by ICT instructions mentioned in the above point to a function pointer table (target), which stores the absolute addresses of all target functions of the instruction call.
为了克服驱动代码中 32 位地址宽度的限制并隐藏返回地址,我们重写了二进制代码中与指令调用相关的所有相对地址。在未受信任的驱动程序加载之前,将分析可执行文件以提取所有调用指令(call xx)。驱动程序加载后,内存中调用地址的操作数地址将被重写为指向滑动窗口的值。滑动窗口中的代码会重写堆栈上的返回地址,使其指向 ICT 指令(jmp *target_2)。之后,另一个 ICT 指令(jmp *target_1)将控制流转移到原始指令(call addr)的目标函数(func1),这确保了原始控制流可以转移到正确的位置。当目标函数返回时,返回指令 ret 使用堆栈上修改后的返回地址(addr_6)将控制流转移到预先设计的 ICT 指令(jmp *target_2)。最后,ICT 指令将控制流转移到原始返回地址(addr2)。与调用地址不同,调用指令中的指针(call *pointer)不是固定的。 指针指向的函数地址(func_2)将被修改为指向滑动窗口的地址。后续操作与处理调用地址的方法相同。需要注意的是,上述提到的所有 ICT 指令使用的操作数都指向一个函数指针表(目标),该表存储了指令调用的所有目标函数的绝对地址。
为了克服驱动代码中 32 位地址宽度的限制并隐藏返回地址,我们重写了二进制代码中与指令调用相关的所有相对地址。在未受信任的驱动程序加载之前,将分析可执行文件以提取所有调用指令(call xx)。驱动程序加载后,内存中调用地址的操作数地址将被重写为指向滑动窗口的值。滑动窗口中的代码会重写堆栈上的返回地址,使其指向 ICT 指令(jmp *target_2)。之后,另一个 ICT 指令(jmp *target_1)将控制流转移到原始指令(call addr)的目标函数(func1),这确保了原始控制流可以转移到正确的位置。当目标函数返回时,返回指令 ret 使用堆栈上修改后的返回地址(addr_6)将控制流转移到预先设计的 ICT 指令(jmp *target_2)。最后,ICT 指令将控制流转移到原始返回地址(addr2)。与调用地址不同,调用指令中的指针(call *pointer)不是固定的。 指针指向的函数地址(func_2)将被修改为指向滑动窗口的地址。后续操作与处理调用地址的方法相同。需要注意的是,上述提到的所有 ICT 指令使用的操作数都指向一个函数指针表(目标),该表存储了指令调用的所有目标函数的绝对地址。
Through the above operations, all real return addresses have been rewritten. Therefore, attackers cannot calculate the sliding space based on the return address. At the same time, the sliding window is unreadable, which can hide the target that stores real function pointers. In addition, the target is non-writable, which ensures function pointers cannot be tampered with. The sliding window and target will slide along with the driver space. Therefore, after the driver space is changed, we only need to update the function pointers in the table target, which ensures all control flows can be transferred to right locations.
通过上述操作,所有实际返回地址都已被重写。因此,攻击者无法根据返回地址计算滑动空间。同时,滑动窗口不可读,这可以隐藏存储实际函数指针的目标。此外,目标不可写,这确保了函数指针不会被篡改。滑动窗口和目标将随着驱动程序空间的滑动而滑动。因此,在驱动程序空间改变后,我们只需要更新目标表中的函数指针,这确保了所有控制流都可以转移到正确的位置。
通过上述操作,所有实际返回地址都已被重写。因此,攻击者无法根据返回地址计算滑动空间。同时,滑动窗口不可读,这可以隐藏存储实际函数指针的目标。此外,目标不可写,这确保了函数指针不会被篡改。滑动窗口和目标将随着驱动程序空间的滑动而滑动。因此,在驱动程序空间改变后,我们只需要更新目标表中的函数指针,这确保了所有控制流都可以转移到正确的位置。
Figure 3: Dynamic space sliding mechanism
Figure 4: The redirection method of control flows
图 4:控制流的重定向方法
图 4:控制流的重定向方法
The probe technology based on reading code (Vector 4) is a basic way to obtain code snippets that conform to gadget forms. To address this issue, Dbox uses EPT to set the code pages of untrusted drivers to be unreadable. For mixed pages, we migrate the data to a new readable page and redirect data accesses to the page. Moreover, we use VT-d to hide driver code pages, thereby preventing attackers from obtaining driver code through DMA (direct memory access). However, unreadable code can only prevent attackers from obtaining code forms, while cannot prevent attackers from probing code addresses. Because the exception thrown by reading code indicates that the pages being read are code pages. In response to this issue, we slide the driver space when capturing code reading, which can make the code address obtained by the attacker unusable.
基于读取代码的探测技术(Vector 4)是获取符合小工具形式的代码片段的基本方法。为了解决这个问题,Dbox 使用 EPT 将不可信驱动程序的代码页设置为不可读。对于混合页面,我们将数据迁移到新的可读页面,并将数据访问重定向到该页面。此外,我们使用 VT-d 隐藏驱动程序代码页,从而防止攻击者通过 DMA(直接内存访问)获取驱动程序代码。然而,不可读代码只能防止攻击者获取代码形式,而不能防止攻击者探测代码地址。因为读取代码时抛出的异常表明正在读取的页面是代码页。针对这一问题,我们在捕获代码读取时滑动驱动程序空间,这可以使攻击者获取的代码地址无法使用。
基于读取代码的探测技术(Vector 4)是获取符合小工具形式的代码片段的基本方法。为了解决这个问题,Dbox 使用 EPT 将不可信驱动程序的代码页设置为不可读。对于混合页面,我们将数据迁移到新的可读页面,并将数据访问重定向到该页面。此外,我们使用 VT-d 隐藏驱动程序代码页,从而防止攻击者通过 DMA(直接内存访问)获取驱动程序代码。然而,不可读代码只能防止攻击者获取代码形式,而不能防止攻击者探测代码地址。因为读取代码时抛出的异常表明正在读取的页面是代码页。针对这一问题,我们在捕获代码读取时滑动驱动程序空间,这可以使攻击者获取的代码地址无法使用。
6.2 Detect control flows 6.2 检测控制流
To filter out illegal entry_flows and exit_flows, we isolate the untrusted driver in a private space, as shown in Figure 5. We allocate private EPTs (D-EPT) and private page tables (D-CR3) for untrusted drivers. In the original EPT (O-EPT), all redundant spaces including the real space of the untrusted driver are mapped to a non-executable trap page, while other spaces keep their original mapping relationships. It should be noted that although the original space of the untrusted driver is mapped to real code, it is non-executable with O-EPT. So, when the entry_flow is transferred to an untrusted driver, a system trap will be triggered. In D-EPT, the original space is mapped to a non-executable trap page, while the kernel sliding space and current driver space are mapped to executable code pages. In addition, when an untrusted driver uses call *register/pointer to transfer control flows to other functions of the driver, it will also trigger system traps. The reason is the pointer in register/pointer still points to the original driver space, which is mapped to a trap page.
为了过滤非法的入口流和出口流,我们将不可信的驱动程序隔离在一个私有空间中,如图 5 所示。我们为不可信驱动程序分配私有的 EPT(D-EPT)和私有的页表(D-CR3)。在原始 EPT(O-EPT)中,所有冗余空间,包括不可信驱动程序的实际空间,都被映射到一个不可执行的陷阱页,而其他空间保持其原始的映射关系。需要注意的是,尽管不可信驱动程序的原始空间被映射到实际代码,但在 O-EPT 中它是不可执行的。因此,当入口流转移到不可信驱动程序时,将触发系统陷阱。在 D-EPT 中,原始空间被映射到一个不可执行的陷阱页,而内核滑动空间和当前驱动程序空间被映射到可执行代码页。此外,当不可信驱动程序使用调用*寄存器/指针将控制流转移到驱动程序的其他函数时,也会触发系统陷阱。原因是寄存器/指针中的指针仍然指向原始驱动程序空间,该空间被映射到一个陷阱页。
为了过滤非法的入口流和出口流,我们将不可信的驱动程序隔离在一个私有空间中,如图 5 所示。我们为不可信驱动程序分配私有的 EPT(D-EPT)和私有的页表(D-CR3)。在原始 EPT(O-EPT)中,所有冗余空间,包括不可信驱动程序的实际空间,都被映射到一个不可执行的陷阱页,而其他空间保持其原始的映射关系。需要注意的是,尽管不可信驱动程序的原始空间被映射到实际代码,但在 O-EPT 中它是不可执行的。因此,当入口流转移到不可信驱动程序时,将触发系统陷阱。在 D-EPT 中,原始空间被映射到一个不可执行的陷阱页,而内核滑动空间和当前驱动程序空间被映射到可执行代码页。此外,当不可信驱动程序使用调用*寄存器/指针将控制流转移到驱动程序的其他函数时,也会触发系统陷阱。原因是寄存器/指针中的指针仍然指向原始驱动程序空间,该空间被映射到一个陷阱页。
Based on the above designs, when ICT instructions and ret in the driver attempt to transfer exit_flows to the kernel, LKMs, or original driver space, the control flows can be captured due to system traps. In contrast, the relative instruction call/jmp address that cannot be used as gadgets can transfer control flows to right locations through sliding spaces without triggering any system traps. After the call address is executed and before the control flow returns, if the kernel/LKM uses call *xx to transfer control flows to the original space, a system trap will also be triggered. In this scenario, we redirect the control flow of call address to a specific module to switch the current page table to O-EPT and O-CR3 (see section 6.3 for the method) by fixing address. Meanwhile, the return address of call address will be replaced with a pointer pointing to a module to switch back to D-EPT and O-CR3. This design can reduce the frequency of system traps.
根据上述设计,当驱动程序中的 ICT 指令和 ret 尝试将 exit_flows 转移到内核、LKMs 或原始驱动空间时,控制流可以因系统陷阱而被捕获。相比之下,不能用作小工具的相对指令调用/跳转地址可以通过滑动空间将控制流转移到正确的位置,而不会触发任何系统陷阱。在调用地址执行后且控制流返回之前,如果内核/LKM 使用 call *xx 将控制流转移到原始空间,也会触发系统陷阱。在这种情况下,我们将调用地址的控制流重定向到一个特定模块,通过固定地址将当前页表切换到 O-EPT 和 O-CR3(具体方法见第 6.3 节)。同时,调用地址的返回地址将被替换为指向一个模块的指针,该模块用于切换回 D-EPT 和 O-CR3。这种设计可以减少系统陷阱的频率。
根据上述设计,当驱动程序中的 ICT 指令和 ret 尝试将 exit_flows 转移到内核、LKMs 或原始驱动空间时,控制流可以因系统陷阱而被捕获。相比之下,不能用作小工具的相对指令调用/跳转地址可以通过滑动空间将控制流转移到正确的位置,而不会触发任何系统陷阱。在调用地址执行后且控制流返回之前,如果内核/LKM 使用 call *xx 将控制流转移到原始空间,也会触发系统陷阱。在这种情况下,我们将调用地址的控制流重定向到一个特定模块,通过固定地址将当前页表切换到 O-EPT 和 O-CR3(具体方法见第 6.3 节)。同时,调用地址的返回地址将被替换为指向一个模块的指针,该模块用于切换回 D-EPT 和 O-CR3。这种设计可以减少系统陷阱的频率。
Figure 5: The Isolation Mechanism
A. Detect entry_flows A. 检测入口流
Kernel rootkits (Vector 1) can hijack control flows by tampering with control data, such as system call tables and VFS pointers, and redirect them to untrusted drivers. CRAs can exploit memory vulnerabilities to redirect illegal control flows to gadgets (Vector 2) in untrusted drivers. In response to these threats, we propose a detection mechanism for entry_flows, as shown in Figure 6. Entry_flows can only enter the real driver code through source checker, EPT switch, and redirector. For entry_flows, the legal paths entering driver code include system calls, interrupts, exported functions, device files, and return instructions, as shown in Table 2. Entry_flows can only enter the untrusted driver through these paths, otherwise they are illegal.
内核 rootkit(向量 1)可以通过篡改控制数据(如系统调用表和 VFS 指针)来劫持控制流,并将它们重定向到不可信的驱动程序。CRAs 可以利用内存漏洞将非法控制流重定向到不可信驱动程序中的小工具(向量 2)。为了应对这些威胁,我们提出了一种 entry_flows 的检测机制,如图 6 所示。entry_flows 只能通过源检查器、EPT 开关和重定向器进入实际的驱动程序代码。对于 entry_flows,进入驱动程序代码的合法路径包括系统调用、中断、导出函数、设备文件和返回指令,如表 2 所示。entry_flows 只能通过这些路径进入不可信驱动程序,否则它们就是非法的。
内核 rootkit(向量 1)可以通过篡改控制数据(如系统调用表和 VFS 指针)来劫持控制流,并将它们重定向到不可信的驱动程序。CRAs 可以利用内存漏洞将非法控制流重定向到不可信驱动程序中的小工具(向量 2)。为了应对这些威胁,我们提出了一种 entry_flows 的检测机制,如图 6 所示。entry_flows 只能通过源检查器、EPT 开关和重定向器进入实际的驱动程序代码。对于 entry_flows,进入驱动程序代码的合法路径包括系统调用、中断、导出函数、设备文件和返回指令,如表 2 所示。entry_flows 只能通过这些路径进入不可信驱动程序,否则它们就是非法的。
When the entry_flow enters an untrusted driver for the first time, it will be transferred to the original space that has been mapped to a trap page. Therefore, a system trap will be triggered and the entry_flow can be captured. Then, the callee instruction, callee address, caller instruction, caller address, and control data will be recorded one by one, which are the basis for checking the legitimacy of control flows. To detect entry_flows, we backup the system call table (sys_call_table), interrupt vector table (IDT), interrupt descriptor (struct irq_desc), and registered driver functions (irq_desc>action->handler) during OS startup. After that, we update and backup these contents again before the untrusted driver loading. The detection method for entry_flows is shown in Algorithm 1, which adopts the following security strategies.
当 entry_flow 首次进入不受信任的驱动程序时,它将被转移到已映射到陷阱页的原始空间。因此,将触发系统陷阱并捕获 entry_flow。然后,依次记录被调用指令、被调用地址、调用指令、调用地址和控制数据,这些是检查控制流合法性基础。为了检测 entry_flows,我们在操作系统启动期间备份系统调用表(sys_call_table)、中断向量表(IDT)、中断描述符(struct irq_desc)和注册的驱动程序函数(irq_desc>action->handler)。之后,在加载不受信任的驱动程序之前,我们再次更新并备份这些内容。entry_flows 的检测方法如算法 1 所示,该方法采用了以下安全策略。
当 entry_flow 首次进入不受信任的驱动程序时,它将被转移到已映射到陷阱页的原始空间。因此,将触发系统陷阱并捕获 entry_flow。然后,依次记录被调用指令、被调用地址、调用指令、调用地址和控制数据,这些是检查控制流合法性基础。为了检测 entry_flows,我们在操作系统启动期间备份系统调用表(sys_call_table)、中断向量表(IDT)、中断描述符(struct irq_desc)和注册的驱动程序函数(irq_desc>action->handler)。之后,在加载不受信任的驱动程序之前,我们再次更新并备份这些内容。entry_flows 的检测方法如算法 1 所示,该方法采用了以下安全策略。
Figure 6: The detection mechanism for entry_flows
图 6:entry_flows 的检测机制
图 6:entry_flows 的检测机制
(1) If the caller instruction is in the kernel function __handle_irq_event_percpu, it indicates that the control flow is caused by an interrupt. If the original IDT has been tampered with, the control flow is illegal. If there is a function registered by a driver that no longer points to the original driver, but points to an untrusted driver, it indicates that the original function pointer has been tampered with by the untrusted driver. At this point, the control flow is also
(1) 如果调用指令位于内核函数 __handle_irq_event_percpu 中,则表明控制流是由中断引起的。如果原始的 IDT 被篡改,控制流就是非法的。如果有一个由驱动程序注册的函数不再指向原始驱动程序,而是指向一个不可信的驱动程序,这表明原始函数指针已被不可信驱动程序篡改。此时,控制流也是
(1) 如果调用指令位于内核函数 __handle_irq_event_percpu 中,则表明控制流是由中断引起的。如果原始的 IDT 被篡改,控制流就是非法的。如果有一个由驱动程序注册的函数不再指向原始驱动程序,而是指向一个不可信的驱动程序,这表明原始函数指针已被不可信驱动程序篡改。此时,控制流也是
Algorithm1: The processing method of enter_flow.
算法 1:enter_flow 的处理方法。
算法 1:enter_flow 的处理方法。
Table 2: The information of entry_flow
表 2:entry_flow 的信息
表 2:entry_flow 的信息
illegal. In contrast, legal control flows will be transferred to the untrusted driver through the control data in a new irq_desc.
非法。相比之下,合法的控制流将通过新 irq_desc 中的控制数据传输给不可信的驱动程序。
非法。相比之下,合法的控制流将通过新 irq_desc 中的控制数据传输给不可信的驱动程序。
(2) If the caller instruction is in the kernel function do_syscall_64, it indicates that the control flow is caused by a system call. In this scenario, the control flow can only enter the driver through a newly added system call. Otherwise, the control flow is illegal.
(2) 如果调用指令位于内核函数 do_syscall_64 中,这表明控制流是由系统调用引起的。在这种情况下,控制流只能通过新添加的系统调用来进入驱动程序。否则,控制流是非法的。
(2) 如果调用指令位于内核函数 do_syscall_64 中,这表明控制流是由系统调用引起的。在这种情况下,控制流只能通过新添加的系统调用来进入驱动程序。否则,控制流是非法的。
(3) If the instruction ret that triggers a system trap is in the __switch_to, it indicates that the kernel thread of the untrusted driver is being scheduled, and the driver space has been slid. Afterwards, we redirect the control flow to the current driver space. It should be noted that, if the driver space remains unchanged, ret will not trigger any system traps. In addition, the ret in the kernel/LKM
(3) 如果触发系统陷阱的指令 ret 位于 __switch_to 中,这表明不受信任的驱动程序的内核线程正在被调度,并且驱动程序空间已经滑动。之后,我们将控制流重定向到当前的驱动程序空间。需要注意的是,如果驱动程序空间保持不变,ret 将不会触发任何系统陷阱。此外,内核/LKM 中的 ret
(3) 如果触发系统陷阱的指令 ret 位于 __switch_to 中,这表明不受信任的驱动程序的内核线程正在被调度,并且驱动程序空间已经滑动。之后,我们将控制流重定向到当前的驱动程序空间。需要注意的是,如果驱动程序空间保持不变,ret 将不会触发任何系统陷阱。此外,内核/LKM 中的 ret
function called by the untrusted driver does not trigger a system trap. The reason is the return address on the stack has been replaced with a pointer pointing to the sliding window. As a result, if the ret is not in the __switch_to, the control flow is illegal.
被非可信驱动调用的函数不会触发系统陷阱。原因是堆栈上的返回地址已被替换为指向滑动窗口的指针。因此,如果 ret 不在 __switch_to 中,则控制流是非法的。
被非可信驱动调用的函数不会触发系统陷阱。原因是堆栈上的返回地址已被替换为指向滑动窗口的指针。因此,如果 ret 不在 __switch_to 中,则控制流是非法的。
(4) If the caller instruction that triggers a system trap is in another LKM, it indicates that the control flow is driven by an exported function (recorded by module->kernel_symbol). Only the LKMs with dependencies on the untrusted driver are allowed to transfer control flows to the the driver space through exported functions.Otherwise, the control flow is illegal.
(4) 如果触发系统陷阱的调用指令位于另一个加载模块(LKM)中,则表明控制流是由导出函数(由 module->kernel_symbol 记录)驱动的。只有依赖于不可信驱动程序的加载模块(LKM)才允许通过导出函数将控制流转移到驱动程序空间。否则,控制流是非法的。
(4) 如果触发系统陷阱的调用指令位于另一个加载模块(LKM)中,则表明控制流是由导出函数(由 module->kernel_symbol 记录)驱动的。只有依赖于不可信驱动程序的加载模块(LKM)才允许通过导出函数将控制流转移到驱动程序空间。否则,控制流是非法的。
(5) If the caller instruction is in other kernel functions, it indicates that the control flow is caused by a device file (i.e. VFS) access. Kernel functions can utilize VFS function pointers in the data structures file_operations, block_device_operations and net_device_ops to enter character device drivers, block device drivers, and network device drivers, respectively. Attackers can hijack control flows by tampering with their function pointers. To prevent such attacks, we should locate the registered VFS functions and check the legitimacy of function pointers after a system trap occurs. In the driver initialization phase, we set breakpoints at the functions cdev_add, usb_register_dev, __device_add_disk and _register_netdev. Then, we can locate the registered VFS functions based on the their parameters (the data structures cdev, usb_class_driver, gendisk, and net_device). After a system trap occurs, if the caller function is not the VFS function registered by the untrusted driver, it indicates that the control flow is illegal.
(5) 如果调用者指令位于其他内核函数中,则表明控制流是由设备文件(即 VFS)访问引起的。内核函数可以利用数据结构 file_operations、block_device_operations 和 net_device_ops 中的 VFS 函数指针分别进入字符设备驱动程序、块设备驱动程序和网络设备驱动程序。攻击者可以通过篡改这些函数指针来劫持控制流。为了防止此类攻击,我们应该在系统陷阱发生后定位已注册的 VFS 函数并检查函数指针的合法性。在驱动程序初始化阶段,我们在函数 cdev_add、usb_register_dev、__device_add_disk 和 _register_netdev 处设置断点。然后,我们可以根据它们的参数(数据结构 cdev、usb_class_driver、gendisk 和 net_device)定位已注册的 VFS 函数。系统陷阱发生后,如果调用者函数不是由不可信驱动程序注册的 VFS 函数,则表明控制流是非法的。
(5) 如果调用者指令位于其他内核函数中,则表明控制流是由设备文件(即 VFS)访问引起的。内核函数可以利用数据结构 file_operations、block_device_operations 和 net_device_ops 中的 VFS 函数指针分别进入字符设备驱动程序、块设备驱动程序和网络设备驱动程序。攻击者可以通过篡改这些函数指针来劫持控制流。为了防止此类攻击,我们应该在系统陷阱发生后定位已注册的 VFS 函数并检查函数指针的合法性。在驱动程序初始化阶段,我们在函数 cdev_add、usb_register_dev、__device_add_disk 和 _register_netdev 处设置断点。然后,我们可以根据它们的参数(数据结构 cdev、usb_class_driver、gendisk 和 net_device)定位已注册的 VFS 函数。系统陷阱发生后,如果调用者函数不是由不可信驱动程序注册的 VFS 函数,则表明控制流是非法的。
For illegal control flows, we inject a regular protection exception into the current execution object to prevent its control flows from continuing to flow. For legal control flows, we transfer them to the current space of the untrusted driver. Meanwhile, the callee address, caller address, control data, and data address will be recorded, which can be used to avoid the legal control flows being detected again when they enter the untrusted driver. We propose an algorithm to transfer legal control flows, as shown in Algorithm 2.
对于非法控制流,我们向当前执行对象注入一个常规保护异常,以防止其控制流继续流动。对于合法控制流,我们将其转移到不可信驱动程序的当前空间。同时,记录调用者地址、被调用者地址、控制数据和数据地址,这些信息可用于避免合法控制流在进入不可信驱动程序时再次被检测。我们提出了一种转移合法控制流的算法,如算法 2 所示。
对于非法控制流,我们向当前执行对象注入一个常规保护异常,以防止其控制流继续流动。对于合法控制流,我们将其转移到不可信驱动程序的当前空间。同时,记录调用者地址、被调用者地址、控制数据和数据地址,这些信息可用于避免合法控制流在进入不可信驱动程序时再次被检测。我们提出了一种转移合法控制流的算法,如算法 2 所示。
If the legal control flow enters the untrusted driver through the instruction call *pointer, we rewrite the transfer target in the pointer with the address of a detection module. If the caller instruction is call address, we modify the address with a pointer pointing to a detection module. The detection module can check the caller instruction based on the recorded addresses and data. If the address has been recorded and the control data has not been changed, it indicates that the control flow is legal. Afterwards, the return address on the stack will be rewritten to make it point a function that can switch EPTs and page tables. Next, the original EPT will be switched to the private EPT through vmfunc, and the page tables will also be switched to the private page tables through mov to cr3. Finally, the detection module searches for the right jump target based on
如果法律控制流通过指令调用 *pointer 进入不可信驱动程序,我们将指针中的传输目标重写为检测模块的地址。如果调用指令是 call address,我们将地址修改为指向检测模块的指针。检测模块可以根据记录的地址和数据检查调用指令。如果地址已被记录且控制数据未被更改,则表明控制流是合法的。之后,堆栈上的返回地址将被重写,使其指向一个可以切换 EPT 和页表的函数。接下来,原始 EPT 将通过 vmfunc 切换到私有 EPT,页表也将通过 mov 到 cr3 切换到私有页表。最后,检测模块根据记录的地址和数据查找正确的跳转目标。
如果法律控制流通过指令调用 *pointer 进入不可信驱动程序,我们将指针中的传输目标重写为检测模块的地址。如果调用指令是 call address,我们将地址修改为指向检测模块的指针。检测模块可以根据记录的地址和数据检查调用指令。如果地址已被记录且控制数据未被更改,则表明控制流是合法的。之后,堆栈上的返回地址将被重写,使其指向一个可以切换 EPT 和页表的函数。接下来,原始 EPT 将通过 vmfunc 切换到私有 EPT,页表也将通过 mov 到 cr3 切换到私有页表。最后,检测模块根据记录的地址和数据查找正确的跳转目标。
Algorithm2: The transfer method of legal control flow.
算法 2:法律控制流的转移方法。
算法 2:法律控制流的转移方法。
Input: the transfer instruction—Ins; the address of Ins--α; the target of Ins in memory--τ; the return address--γ; he offset between the original space and the current space--σ;
Output: null
1. if Ins ⊆ {__handle_irq_event_percpu, do_syscall_64, VFS functions, LKM functions } then
1. 如果 Ins ⊆ {__handle_irq_event_percpu, do_syscall_64, VFS 函数, LKM 函数} 那么
1. 如果 Ins ⊆ {__handle_irq_event_percpu, do_syscall_64, VFS 函数, LKM 函数} 那么
2. rewrite_tar(τ, check_addr)
3. rewrite_rip(Ins, τ+σ)
4. end if
5. if Ins ∈ __switch_to || Ins==ret then
5. 如果 Ins ∈ __switch_to || Ins == ret 则
5. 如果 Ins ∈ __switch_to || Ins == ret 则
6. rewrite_rip(Ins, τ+σ)
7. end if
8. check_addr:
9. if (check_source(α, τ)==1)
10. rewrite_ret(γ, switch_addr)
11. switch(o_ept, d_ept, o_cr3, d_cr3)
12. jmp_table(α)
13. else goto Error
14. switch_addr:
15. switch (d_ept, o_ept, d_cr3, o_cr3)
16. Error:
17. Insert_GP();
the instruction address (stored in the non-writable jmp_table), and uses the instruction jmp *xx to transfer the control flow to the real space of the driver. After the driver space is changed, we only need to update the jmp_table that can transfer control flows to right locations. When the control flow attempts to jump to an untrusted driver using the recorded instruction call *pointer/address again, it will be redirected to the detection module without triggering a system trap. When the control flow returns, the EPT will switch back to the original EPT through vmfunc, and the page tables will also switch back to the original page tables. The above designs can avoid legal control flows repeatedly triggering system traps, thereby reducing overhead.
指令地址(存储在不可写的 jmp_table 中),并使用指令 jmp *xx 将控制流转移到驱动程序的真实空间。在驱动程序空间更改后,我们只需要更新可以将控制流转移到正确位置的 jmp_table。当控制流尝试使用记录的指令 call *pointer/address 再次跳转到不可信的驱动程序时,它将被重定向到检测模块,而不会触发系统陷阱。当控制流返回时,EPT 将通过 vmfunc 切换回原始 EPT,页表也将切换回原始页表。上述设计可以避免合法的控制流反复触发系统陷阱,从而减少开销。
指令地址(存储在不可写的 jmp_table 中),并使用指令 jmp *xx 将控制流转移到驱动程序的真实空间。在驱动程序空间更改后,我们只需要更新可以将控制流转移到正确位置的 jmp_table。当控制流尝试使用记录的指令 call *pointer/address 再次跳转到不可信的驱动程序时,它将被重定向到检测模块,而不会触发系统陷阱。当控制流返回时,EPT 将通过 vmfunc 切换回原始 EPT,页表也将切换回原始页表。上述设计可以避免合法的控制流反复触发系统陷阱,从而减少开销。
B. Detect exit_flows
The above designs can prevent attackers from transferring illegal entry_flows to untrusted drivers, but it cannot prevent attackers from transferring illegal exit_flows to the core kernel, other LKMs, and the untrusted driver’s own code. In real attack scenarios, attackers can use dispatcher-gadgets [4] in the untrusted driver to transfer illegal exit_flows, which can be Vector-1/2/3. The instructions that can be used as dispatcher gadgets include ret, call *pointer, call *register, jmp *pointer, and jmp *register. These instructions in the driver may be legal instructions or non-aligned operands (such as the data c3 used as an instruction ret) caused by vulnerabilities. Therefore, each exit_flow caused by the ICT instruction or ret needs to be captured for detection.
上述设计可以防止攻击者将非法入口流传递给不可信驱动程序,但无法防止攻击者将非法出口流传递给核心内核、其他 LKM 和不可信驱动程序自身的代码。在实际攻击场景中,攻击者可以使用不可信驱动程序中的调度小工具(dispatcher-gadgets)[4] 来传递非法出口流,这些出口流可以是 Vector-1/2/3。可以作为调度小工具的指令包括 ret、call *pointer、call *register、jmp *pointer 和 jmp *register。驱动程序中的这些指令可能是合法指令或由于漏洞引起的非对齐操作数(例如,数据 c3 用作指令 ret)。因此,需要捕获由 ICT 指令或 ret 引起的每个出口流以进行检测。
上述设计可以防止攻击者将非法入口流传递给不可信驱动程序,但无法防止攻击者将非法出口流传递给核心内核、其他 LKM 和不可信驱动程序自身的代码。在实际攻击场景中,攻击者可以使用不可信驱动程序中的调度小工具(dispatcher-gadgets)[4] 来传递非法出口流,这些出口流可以是 Vector-1/2/3。可以作为调度小工具的指令包括 ret、call *pointer、call *register、jmp *pointer 和 jmp *register。驱动程序中的这些指令可能是合法指令或由于漏洞引起的非对齐操作数(例如,数据 c3 用作指令 ret)。因此,需要捕获由 ICT 指令或 ret 引起的每个出口流以进行检测。
The scenario where the control flow jumps out of the driver can be divided into three categories. The first is the control flow returns its caller function through the instruction ret in an untrusted driver when the task is completed. The second is the untrusted driver transfers control flows to kernel or other LKMs through the instruction call (almost without involving jmp). The third is the CPU of the current kernel thread is preempted by another thread.
控制流跳出驱动程序的情景可以分为三类。第一类是在任务完成后,通过不受信任的驱动程序中的 ret 指令将控制流返回给调用者函数。第二类是不受信任的驱动程序通过 call 指令将控制流转移到内核或其他 LKM(几乎不涉及 jmp)。第三类是当前内核线程的 CPU 被另一个线程抢占。
控制流跳出驱动程序的情景可以分为三类。第一类是在任务完成后,通过不受信任的驱动程序中的 ret 指令将控制流返回给调用者函数。第二类是不受信任的驱动程序通过 call 指令将控制流转移到内核或其他 LKM(几乎不涉及 jmp)。第三类是当前内核线程的 CPU 被另一个线程抢占。
Due to the sliding space, when ICT instructions and ret use the original control data or probed code addresses to transfer exit_flows, a system trap will be triggered. We use the following security strategies to check the legitimacy of exit_flows.
由于滑动空间的存在,当 ICT 指令和 ret 使用原始控制数据或探测代码地址来转移 exit_flows 时,将触发系统陷阱。我们采用以下安全策略来检查 exit_flows 的合法性。
由于滑动空间的存在,当 ICT 指令和 ret 使用原始控制数据或探测代码地址来转移 exit_flows 时,将触发系统陷阱。我们采用以下安全策略来检查 exit_flows 的合法性。
1) If the return address used by ret no longer points to the sliding window (shown as Figure 4), the exit_flow is illegal. Under the sliding mechanism, the modified instruction call will transfer legal control flows to the sliding window. And the original return address has been rewritten to make it point to the sliding window. In contrast, CRAs tamper with the return address with a pointer pointing to the original space or probed space, rather than the sliding window. As a result, the illegal control will trigger a system trap and be detected.
1) 如果 ret 使用的返回地址不再指向滑动窗口(如图 4 所示),则退出流是非法的。在滑动机制下,修改后的指令调用将合法的控制流转移到滑动窗口。而原始返回地址已被重写,使其指向滑动窗口。相比之下,CRAs 会篡改返回地址,使其指向原始空间或探测空间,而不是滑动窗口。因此,非法控制将触发系统陷阱并被检测到。
1) 如果 ret 使用的返回地址不再指向滑动窗口(如图 4 所示),则退出流是非法的。在滑动机制下,修改后的指令调用将合法的控制流转移到滑动窗口。而原始返回地址已被重写,使其指向滑动窗口。相比之下,CRAs 会篡改返回地址,使其指向原始空间或探测空间,而不是滑动窗口。因此,非法控制将触发系统陷阱并被检测到。
2) If the caller instruction is jmp *xx, the exit_flow is illegal. Under normal circumstances, the driver uses call xx to call kernel functions or exported functions. In contrast, jmp *xx can transfer exit_flows to any locations.
2) 如果调用指令是 jmp *xx,则退出流是非法的。在正常情况下,驱动程序使用 call xx 来调用内核函数或导出函数。相比之下,jmp *xx 可以将退出流转移到任何位置。
2) 如果调用指令是 jmp *xx,则退出流是非法的。在正常情况下,驱动程序使用 call xx 来调用内核函数或导出函数。相比之下,jmp *xx 可以将退出流转移到任何位置。
3) If the caller instruction is call *xx, the exit_flow must jump to the header of an LKM exported function or a kernel function. Otherwise, it is illegal. Moreover, call *xx must match the opcode and operand in the executable file (.ko), otherwise it is illegal.
3) 如果调用指令是 call *xx,exit_flow 必须跳转到 LKM 导出函数或内核函数的头部。否则,它是非法的。此外,call *xx 必须与可执行文件 (.ko) 中的操作码和操作数匹配,否则它是非法的。
3) 如果调用指令是 call *xx,exit_flow 必须跳转到 LKM 导出函数或内核函数的头部。否则,它是非法的。此外,call *xx 必须与可执行文件 (.ko) 中的操作码和操作数匹配,否则它是非法的。
4) From the OS perspective, it needs to locate the kernel function based on the symbols in the driver during the module loading. The function addresses will be filled to the operands of all instructions call address or the data area of the driver. In benign drivers, all transfer targets of call *pointer are in driver’s data area. In contrast, the transfer target of call *register may be in the data area or may be from the return value of the function kallsyms_lookup_name. From the attacker’s perspective, it can only tamper with the pointers stored in writable pages rather than registers. Nevertheless, the call *register can still be used as a dispatcher gadget. The reason is function pointers are not always stored in registers and may be temporarily stored in writable memory (such as the stack) during parameter transfer. When the untrusted driver calls kallsyms_lookup_name, we record the return value in rax, which can be achieved by replacing the caller’s return address with a recording function address. In a word, all pointers will be rewritten to make them point to the sliding window. All rewritten pointers are stored in a table, which records the pointer addresses, original pointers, and current pointers. After the system trap triggered by call *xx is captured, we check whether the pointer address is recorded in the table. The unchanged pointer will point to the sliding window, which can avoid triggering any system traps. If the pointer address has been recorded but the exit_flow still triggers a system trap, it indicates that the pointer stored in the original memory has been changed. Then, we can determine the current control flow is illegal.
4) 从操作系统(OS)的角度来看,它需要在模块加载期间根据驱动程序中的符号定位内核函数。函数地址将被填充到所有调用指令地址或驱动程序数据区域的操縱数中。在良性驱动程序中,所有调用*指针的转移目标都在驱动程序的数据区域中。相比之下,调用*寄存器的转移目标可能在数据区域中,也可能来自函数 kallsyms_lookup_name 的返回值。从攻击者的角度来看,它只能篡改存储在可写页面中的指针,而不是寄存器。然而,调用*寄存器仍然可以作为调度小工具使用。原因是函数指针并不总是存储在寄存器中,在参数传递过程中可能会暂时存储在可写内存(如堆栈)中。当不可信驱动程序调用 kallsyms_lookup_name 时,我们记录 rax 中的返回值,这可以通过将调用者的返回地址替换为记录函数地址来实现。总之,所有指针都将被重写,使它们指向滑动窗口。 所有重写后的指针都存储在一个表中,该表记录了指针地址、原始指针和当前指针。在由调用 *xx 触发的系统陷阱被捕获后,我们检查该指针地址是否记录在表中。未更改的指针将指向滑动窗口,这可以避免触发任何系统陷阱。如果指针地址已被记录但 exit_flow 仍然触发系统陷阱,这表明存储在原始内存中的指针已被更改。然后,我们可以确定当前控制流是非法的。
4) 从操作系统(OS)的角度来看,它需要在模块加载期间根据驱动程序中的符号定位内核函数。函数地址将被填充到所有调用指令地址或驱动程序数据区域的操縱数中。在良性驱动程序中,所有调用*指针的转移目标都在驱动程序的数据区域中。相比之下,调用*寄存器的转移目标可能在数据区域中,也可能来自函数 kallsyms_lookup_name 的返回值。从攻击者的角度来看,它只能篡改存储在可写页面中的指针,而不是寄存器。然而,调用*寄存器仍然可以作为调度小工具使用。原因是函数指针并不总是存储在寄存器中,在参数传递过程中可能会暂时存储在可写内存(如堆栈)中。当不可信驱动程序调用 kallsyms_lookup_name 时,我们记录 rax 中的返回值,这可以通过将调用者的返回地址替换为记录函数地址来实现。总之,所有指针都将被重写,使它们指向滑动窗口。 所有重写后的指针都存储在一个表中,该表记录了指针地址、原始指针和当前指针。在由调用 *xx 触发的系统陷阱被捕获后,我们检查该指针地址是否记录在表中。未更改的指针将指向滑动窗口,这可以避免触发任何系统陷阱。如果指针地址已被记录但 exit_flow 仍然触发系统陷阱,这表明存储在原始内存中的指针已被更改。然后,我们可以确定当前控制流是非法的。
7 EVALUATION
We conduct all experiments on a dell server equipped with 2 Xeon silver CPUs@2.4Ghz, Intel E1000E 1GbE, and 64GB Samsung 970 NVMe. The performance test is performed on an Ubuntu-21.04with kernel 5.2, and all results are averaged after 10 runs.
我们在配备有 2 个 Xeon 银牌 CPU@2.4Ghz、Intel E1000E 1GbE 和 64GB 三星 970 NVMe 的戴尔服务器上进行所有实验。性能测试在 Ubuntu-21.04(内核 5.2)上进行,所有结果均取自 10 次运行的平均值。
我们在配备有 2 个 Xeon 银牌 CPU@2.4Ghz、Intel E1000E 1GbE 和 64GB 三星 970 NVMe 的戴尔服务器上进行所有实验。性能测试在 Ubuntu-21.04(内核 5.2)上进行,所有结果均取自 10 次运行的平均值。
7.1 Security evaluation
Vector 1. To verify Dbox’s defense effect on kernel rootkits, we deploy 7 kernel rootkits (Vector 1) in different Linux OSes (such as Ubuntu 12.04 and Centos-6.10), as shown in Table 3. The results show that kernel rootkits will violate the security strategies when they hijack control flows. Compared with VTW [27], Dbox can capture and detect all entry_flows without frequent system traps, resulting in less overhead on target drivers.
向量 1. 为了验证 Dbox 对内核 rootkit 的防御效果,我们在不同的 Linux 操作系统(如 Ubuntu 12.04 和 Centos-6.10)中部署了 7 个内核 rootkit(向量 1),如表 3 所示。结果表明,当内核 rootkit 劫持控制流时,会违反安全策略。与 VTW [27]相比,Dbox 可以在不频繁触发系统陷阱的情况下捕获并检测所有 entry_flows,从而减少对目标驱动程序的开销。
向量 1. 为了验证 Dbox 对内核 rootkit 的防御效果,我们在不同的 Linux 操作系统(如 Ubuntu 12.04 和 Centos-6.10)中部署了 7 个内核 rootkit(向量 1),如表 3 所示。结果表明,当内核 rootkit 劫持控制流时,会违反安全策略。与 VTW [27]相比,Dbox 可以在不频繁触发系统陷阱的情况下捕获并检测所有 entry_flows,从而减少对目标驱动程序的开销。
Vector 2. CRAs can transfer illegal entry_flows from kernel/LKMs to untrusted drivers. And they can also transfer illegal exit_flows from untrusted drivers to kernel/LKMs (Vector 2). Due to the isolation layer, the entry_flow/exit_flow must pass through specific entries to enter/exit an untrusted driver. Otherwise, a system trap will be triggered and the illegal entry_flow/exit_flow can be captured. For example, a ROP attack initiated in kernel space will be blocked due to violating security strategy (3) when redirecting the return control flow to an untrusted driver.
向量 2. CRAs 可以将非法入口流从内核/LKMs 转移到不可信驱动程序。它们还可以将非法出口流从不可信驱动程序转移到内核/LKMs(向量 2)。由于隔离层的存在,入口流/出口流必须通过特定的入口进入/退出不可信驱动程序。否则,将触发系统陷阱,非法入口流/出口流可以被捕获。例如,从内核空间发起的 ROP 攻击在重定向返回控制流到不可信驱动程序时,由于违反安全策略(3)而被阻止。
向量 2. CRAs 可以将非法入口流从内核/LKMs 转移到不可信驱动程序。它们还可以将非法出口流从不可信驱动程序转移到内核/LKMs(向量 2)。由于隔离层的存在,入口流/出口流必须通过特定的入口进入/退出不可信驱动程序。否则,将触发系统陷阱,非法入口流/出口流可以被捕获。例如,从内核空间发起的 ROP 攻击在重定向返回控制流到不可信驱动程序时,由于违反安全策略(3)而被阻止。
Vector 3. To verify Dbox’s defense effect on Vector 3, we simulate a specific ROP in the driver e1000. During e1000 running, we periodically capture the running function in the driver and manually modify the return address to make it point to the original driver space. In this experiment, all illegal exit_flows can be captured by Dbox due to violating security strategy 1). For JOP, we select the instruction call *pointer in the function e1000_down as a dispatcher gadget. Then, we modify the pointer with a random address in the original driver space. The test result shows that the exit_flow violates security strategy 4).
向量 3。为了验证 Dbox 对向量 3 的防御效果,我们在驱动程序 e1000 中模拟了一个特定的 ROP。在 e1000 运行期间,我们定期捕获驱动程序中的运行函数,并手动修改返回地址,使其指向原始驱动程序空间。在此次实验中,所有非法的 exit_flows 都因违反安全策略 1 而被 Dbox 捕获。对于 JOP,我们选择函数 e1000_down 中的指令 call *pointer 作为调度小工具。然后,我们将指针修改为原始驱动程序空间中的一个随机地址。测试结果显示,exit_flow 违反了安全策略 4。
向量 3。为了验证 Dbox 对向量 3 的防御效果,我们在驱动程序 e1000 中模拟了一个特定的 ROP。在 e1000 运行期间,我们定期捕获驱动程序中的运行函数,并手动修改返回地址,使其指向原始驱动程序空间。在此次实验中,所有非法的 exit_flows 都因违反安全策略 1 而被 Dbox 捕获。对于 JOP,我们选择函数 e1000_down 中的指令 call *pointer 作为调度小工具。然后,我们将指针修改为原始驱动程序空间中的一个随机地址。测试结果显示,exit_flow 违反了安全策略 4。
Vector 4. To verify Dbox’s defense effect on code probes, we trigger the sliding space mechanism by reading driver code. After code reading, the spaces mapped to real code pages are shown in Table 4. The results show that the executable code of the driver will be mapped to a different space after a code probe occurs. At the same time, the original space and the probed space remain non-executable. Therefore, neither the code in the original space nor the probed code can be used as gadgets.
向量 4. 为了验证 Dbox 对代码探测的防御效果,我们通过读取驱动程序代码触发滑动空间机制。代码读取后,映射到实际代码页的空间如表 4 所示。结果显示,驱动程序的可执行代码在发生代码探测后将被映射到不同的空间。同时,原始空间和被探测的空间保持不可执行状态。因此,原始空间中的代码和被探测的代码都不能作为小工具使用。
向量 4. 为了验证 Dbox 对代码探测的防御效果,我们通过读取驱动程序代码触发滑动空间机制。代码读取后,映射到实际代码页的空间如表 4 所示。结果显示,驱动程序的可执行代码在发生代码探测后将被映射到不同的空间。同时,原始空间和被探测的空间保持不可执行状态。因此,原始空间中的代码和被探测的代码都不能作为小工具使用。
Vector 5. Since the return addresses have been modified to make them point to the sliding window, attackers cannot calculate the addresses of available gadgets based on them. For function pointers in data area, they generally appear as member variables in specific data structures, such as struct file_operations. These data structures are initialized at driver loading stage and remain unchanged throughout the driver’s lifecycle. Once such function pointers are changed, system traps will be triggered when they are dereferenced, which can be captured and analyzed by Dbox. Therefore, they are difficult to be used for control flow hijacking. In addition, after
向量 5. 由于返回地址已被修改以指向滑动窗口,攻击者无法根据它们计算可用小工具的地址。对于数据区域中的函数指针,它们通常作为特定数据结构(如 struct file_operations)的成员变量出现。这些数据结构在驱动程序加载阶段初始化,并在整个驱动程序生命周期中保持不变。一旦这些函数指针被更改,在它们被解引用时将触发系统陷阱,这些陷阱可以被 Dbox 捕获和分析。因此,它们很难被用于控制流劫持。此外,在
向量 5. 由于返回地址已被修改以指向滑动窗口,攻击者无法根据它们计算可用小工具的地址。对于数据区域中的函数指针,它们通常作为特定数据结构(如 struct file_operations)的成员变量出现。这些数据结构在驱动程序加载阶段初始化,并在整个驱动程序生命周期中保持不变。一旦这些函数指针被更改,在它们被解引用时将触发系统陷阱,这些陷阱可以被 Dbox 捕获和分析。因此,它们很难被用于控制流劫持。此外,在
Table 3: The detection effect on kernel rootkits. P: process, F: file, M: module, N: network port.
表 3:内核 rootkit 的检测效果。P:进程,F:文件,M:模块,N:网络端口。
表 3:内核 rootkit 的检测效果。P:进程,F:文件,M:模块,N:网络端口。
the sliding window is enabled, such pointers do not point to the original functions, but to a detection module. So, even if attackers combine the known binary code with the pointers, they cannot calculate any available gadgets.
滑动窗口启用时,此类指针不会指向原始函数,而是指向检测模块。因此,即使攻击者将已知的二进制代码与指针结合,也无法计算出任何可用的 gadgets。
滑动窗口启用时,此类指针不会指向原始函数,而是指向检测模块。因此,即使攻击者将已知的二进制代码与指针结合,也无法计算出任何可用的 gadgets。
In summary, Dbox has good defense against code probes, kernel rootkits, and CRAs. However, Dbox still has the potential for false positive and false negative. First, when a root user performs online debugging on a running driver, he will read the driver code. Such a legal activity will be misjudged as a code probe. Second, if an attacker can build a complete gadget chain using relative jump instructions (such as call/jmp address), Dbox will misjudge such an attack as a benign activity. In practice, such attacks do exist. For example, in the code snippet "if (val==0) call func-1; else call func-2", if the non-control data val is stored in the memory that can be tampered with (such as the stack), an attacker can set the calling order of fun-1 and fun-2 arbitrarily by tampering with val. Fortunately, such attacks have significant limitations, making them difficult to achieve practical attack effects. Third, Dbox cannot prevent kernel rootkits from tampering with kernel data in realtime. It can only prevent them from hijacking control flows when the tampered data is dereferenced.
总之,Dbox 对代码探测、内核 rootkit 和 CRA 有良好的防御能力。然而,Dbox 仍然存在误报和漏报的可能。首先,当 root 用户对正在运行的驱动程序进行在线调试时,他会读取驱动程序代码。这种合法活动会被误判为代码探测。其次,如果攻击者能够使用相对跳转指令(如 call/jmp 地址)构建完整的 gadget 链,Dbox 会将此类攻击误判为良性活动。实际上,这样的攻击确实存在。例如,在代码片段“if (val==0) call func-1; else call func-2”中,如果非控制数据 val 存储在可被篡改的内存中(如堆栈),攻击者可以通过篡改 val 任意设置 fun-1 和 fun-2 的调用顺序。幸运的是,此类攻击存在显著的局限性,使其难以实现实际攻击效果。第三,Dbox 无法防止内核 rootkit 实时篡改内核数据。它只能在篡改的数据被解引用时防止它们劫持控制流。
总之,Dbox 对代码探测、内核 rootkit 和 CRA 有良好的防御能力。然而,Dbox 仍然存在误报和漏报的可能。首先,当 root 用户对正在运行的驱动程序进行在线调试时,他会读取驱动程序代码。这种合法活动会被误判为代码探测。其次,如果攻击者能够使用相对跳转指令(如 call/jmp 地址)构建完整的 gadget 链,Dbox 会将此类攻击误判为良性活动。实际上,这样的攻击确实存在。例如,在代码片段“if (val==0) call func-1; else call func-2”中,如果非控制数据 val 存储在可被篡改的内存中(如堆栈),攻击者可以通过篡改 val 任意设置 fun-1 和 fun-2 的调用顺序。幸运的是,此类攻击存在显著的局限性,使其难以实现实际攻击效果。第三,Dbox 无法防止内核 rootkit 实时篡改内核数据。它只能在篡改的数据被解引用时防止它们劫持控制流。
7.2 Performance evaluation
Impact on the OS When Dbox is working, it will preempt the CPU, which causes overhead to the OS. To observe the impact of Dbox on OS under different CPU loads, both stress-ng and SpecCPU2006 run simultaneously. In this period, we gradually increase CPU usage using stress-ng and observe the overhead caused by Dbox. The experiment results are shown in Figure 7. All results in the figure have been normalized using the original OS as the standard. The results show that the overhead introduced by Dbox gradually increases as the CPU load increases. When the CPU usage is less than 40%, the average overhead introduced by Dbox is less than 3.6%. When the CPU is close to full load (the limit of stress-ng is 99%, not 100%), the average overhead is about 10%.
当 Dbox 运行时,它会抢占 CPU,从而对操作系统(OS)造成开销。为了观察 Dbox 在不同 CPU 负载下对 OS 的影响,同时运行了 stress-ng 和 SpecCPU2006。在此期间,我们逐渐增加使用 stress-ng 的 CPU 使用率,并观察由 Dbox 引起的开销。实验结果如图 7 所示。图中的所有结果都以原始 OS 为标准进行了归一化处理。结果显示,随着 CPU 负载的增加,Dbox 引入的开销逐渐增加。当 CPU 使用率低于 40%时,Dbox 引入的平均开销小于 3.6%。当 CPU 接近满载(stress-ng 的上限为 99%,而非 100%)时,平均开销约为 10%。
当 Dbox 运行时,它会抢占 CPU,从而对操作系统(OS)造成开销。为了观察 Dbox 在不同 CPU 负载下对 OS 的影响,同时运行了 stress-ng 和 SpecCPU2006。在此期间,我们逐渐增加使用 stress-ng 的 CPU 使用率,并观察由 Dbox 引起的开销。实验结果如图 7 所示。图中的所有结果都以原始 OS 为标准进行了归一化处理。结果显示,随着 CPU 负载的增加,Dbox 引入的开销逐渐增加。当 CPU 使用率低于 40%时,Dbox 引入的平均开销小于 3.6%。当 CPU 接近满载(stress-ng 的上限为 99%,而非 100%)时,平均开销约为 10%。
In addition to CPU overhead, Dbox has an impact on system latency and bandwidth. We use Lmbench to measure the system latency and bandwidth attenuation caused by Dbox, as shown in Figure 8. During the test, we inject different numbers of untrusted drivers with a code size of 1MB into the OS. The results show that when the number of drivers monitored by Dbox is 0, both the average system latency and average bandwidth attenuation are less than 2%. As the number of monitored drivers increases, both system latency and bandwidth attenuation will increase. When the number of monitored drivers reaches 40, the both increase to 8.6% and 7.6%, respectively.
除了 CPU 开销外,Dbox 还对系统延迟和带宽产生了影响。我们使用 Lmbench 测量了 Dbox 引起的系统延迟和带宽衰减,如图 8 所示。在测试过程中,我们将不同数量的不可信驱动程序(代码大小为 1MB)注入操作系统。结果显示,当 Dbox 监控的驱动程序数量为 0 时,平均系统延迟和平均带宽衰减均小于 2%。随着监控的驱动程序数量的增加,系统延迟和带宽衰减也会增加。当监控的驱动程序数量达到 40 时,两者分别增加到 8.6%和 7.6%。
除了 CPU 开销外,Dbox 还对系统延迟和带宽产生了影响。我们使用 Lmbench 测量了 Dbox 引起的系统延迟和带宽衰减,如图 8 所示。在测试过程中,我们将不同数量的不可信驱动程序(代码大小为 1MB)注入操作系统。结果显示,当 Dbox 监控的驱动程序数量为 0 时,平均系统延迟和平均带宽衰减均小于 2%。随着监控的驱动程序数量的增加,系统延迟和带宽衰减也会增加。当监控的驱动程序数量达到 40 时,两者分别增加到 8.6%和 7.6%。
During running, Dbox can trigger system traps, which affects the OS. System traps can be divided into unconditional traps and conditional traps. The former are triggered by specific instructions and they are inevitable. In guest mode, the instructions CPUID, gettsec, invd, xsetbv, and all VMX instructions except vmfunc will cause unconditional traps. Therefore, even if Dbox does not monitor any drivers, it incurs some overhead. The conditional traps are triggered by specific events set by Dbox. When a control flow enters/exits the untrusted driver for the first time, a system trap will be triggered. Additionally, both the control flow transfer violating security strategies and the memory access violating EPT configuration can also trigger system traps. During handling system traps, Dbox suspends the running function to check the legitimacy of the current control flow, which affects the running speed of the target. Moreover, during the detection period, Dbox needs to occupy CPU time slices, which in turn affects the execution of other processes.
在运行过程中,Dbox 可以触发系统陷阱,这会影响操作系统。系统陷阱可以分为无条件陷阱和有条件陷阱。前者由特定指令触发,是不可避免的。在客户模式下,CPUID、gettsec、invd、xsetbv 以及除 vmfunc 之外的所有 VMX 指令都会导致无条件陷阱。因此,即使 Dbox 不监控任何驱动程序,也会产生一些开销。有条件陷阱由 Dbox 设置的特定事件触发。当控制流首次进入/退出不受信任的驱动程序时,将触发系统陷阱。此外,违反安全策略的控制流转移和违反 EPT 配置的内存访问也可以触发系统陷阱。在处理系统陷阱时,Dbox 会暂停运行中的功能以检查当前控制流的合法性,这会影响目标的运行速度。此外,在检测期间,Dbox 需要占用 CPU 时间片,这反过来会影响其他进程的执行。
在运行过程中,Dbox 可以触发系统陷阱,这会影响操作系统。系统陷阱可以分为无条件陷阱和有条件陷阱。前者由特定指令触发,是不可避免的。在客户模式下,CPUID、gettsec、invd、xsetbv 以及除 vmfunc 之外的所有 VMX 指令都会导致无条件陷阱。因此,即使 Dbox 不监控任何驱动程序,也会产生一些开销。有条件陷阱由 Dbox 设置的特定事件触发。当控制流首次进入/退出不受信任的驱动程序时,将触发系统陷阱。此外,违反安全策略的控制流转移和违反 EPT 配置的内存访问也可以触发系统陷阱。在处理系统陷阱时,Dbox 会暂停运行中的功能以检查当前控制流的合法性,这会影响目标的运行速度。此外,在检测期间,Dbox 需要占用 CPU 时间片,这反过来会影响其他进程的执行。
Table 4: The executable space of the driver. Pn: The executable space of the driver after the 𝑛𝑡ℎ probes.
表 4:驱动程序的可执行空间。Pn:第𝑛次探测后驱动程序的可执行空间。
表 4:驱动程序的可执行空间。Pn:第𝑛次探测后驱动程序的可执行空间。
Figure 7: The test results of SpecCPU2006
图 7:SpecCPU2006 的测试结果
图 7:SpecCPU2006 的测试结果
Figure 8: The test results of Lmbench
图 8:Lmbench 的测试结果
图 8:Lmbench 的测试结果
Dbox needs to allocate redundant spaces for untrusted drivers. At the same time, it also needs to add some code for control flow detection. Therefore, compared to the original driver, the untrusted driver needs to occupy more memory. To observe the memory overhead introduced by Dbox, we manually compiled and loaded some common drivers. Dbox takes them as untrusted drivers and allocates different numbers of redundant spaces for them. The memory overhead is shown in Figure 9.
Dbox 需要为不可信驱动分配冗余空间。同时,它还需要添加一些代码以进行控制流检测。因此,与原始驱动相比,不可信驱动需要占用更多内存。为了观察 Dbox 引入的内存开销,我们手动编译并加载了一些常见驱动。Dbox 将它们视为不可信驱动,并为它们分配不同数量的冗余空间。内存开销如图 9 所示。
Dbox 需要为不可信驱动分配冗余空间。同时,它还需要添加一些代码以进行控制流检测。因此,与原始驱动相比,不可信驱动需要占用更多内存。为了观察 Dbox 引入的内存开销,我们手动编译并加载了一些常见驱动。Dbox 将它们视为不可信驱动,并为它们分配不同数量的冗余空间。内存开销如图 9 所示。
The results show that the number of redundant spaces does not incur significant overhead on memory. When the number of redundant spaces is 10 and 100, Dbox increases the untrusted driver by an average of 5.6% and 5.8%, respectively. Due to the fact that redundant page tables are shared and they are mapped to a same trap page, they do not occupy too much physical pages. In contrast, EPT occupies more pages. To reduce unnecessary memory overhead, we only map the pages related to the current driver when creating private EPTs. The objects mapped by a private EPT include the untrusted driver, the core kernel, and the LKMs needed by the driver. In general, a private EPT occupies no more than 5MB memory.
结果显示,冗余空间的数量不会对内存产生显著的开销。当冗余空间的数量为 10 和 100 时,Dbox 分别使不可信驱动程序增加了平均 5.6%和 5.8%。由于冗余页表是共享的,并且它们映射到同一个陷阱页,因此它们不会占用太多的物理页。相比之下,EPT 占用的页更多。为了减少不必要的内存开销,我们在创建私有 EPT 时仅映射与当前驱动程序相关的页。私有 EPT 映射的对象包括不可信驱动程序、核心内核以及驱动程序所需的 LKMs。通常,私有 EPT 占用的内存不超过 5MB。
结果显示,冗余空间的数量不会对内存产生显著的开销。当冗余空间的数量为 10 和 100 时,Dbox 分别使不可信驱动程序增加了平均 5.6%和 5.8%。由于冗余页表是共享的,并且它们映射到同一个陷阱页,因此它们不会占用太多的物理页。相比之下,EPT 占用的页更多。为了减少不必要的内存开销,我们在创建私有 EPT 时仅映射与当前驱动程序相关的页。私有 EPT 映射的对象包括不可信驱动程序、核心内核以及驱动程序所需的 LKMs。通常,私有 EPT 占用的内存不超过 5MB。
Impact on the driver During the running of an untrusted driver, Dbox needs to track and detect all entry_flows and exit_flows,
在运行不受信任的驱动程序期间,Dbox 需要跟踪和检测所有入口流和出口流
在运行不受信任的驱动程序期间,Dbox 需要跟踪和检测所有入口流和出口流
Figure 9: The memory overhead
which can slow down the running speed of the driver. The sliding mechanism adopted by Dbox essentially is to a re-randomization method[28, 43]. We manually install NVMe and E1000e and see them as untrusted drivers. Afterwards, an LKM reads the driver code at regular intervals (1ms, 10ms, and 50ms) to trigger the sliding space mechanism.
这会减慢驱动程序的运行速度。Dbox 采用的滑动机制本质上是一种重新随机化方法[28, 43]。我们手动安装 NVMe 和 E1000e,并将它们视为不可信的驱动程序。之后,一个 LKM 以固定的时间间隔(1ms、10ms 和 50ms)读取驱动程序代码,以触发滑动空间机制。
这会减慢驱动程序的运行速度。Dbox 采用的滑动机制本质上是一种重新随机化方法[28, 43]。我们手动安装 NVMe 和 E1000e,并将它们视为不可信的驱动程序。之后,一个 LKM 以固定的时间间隔(1ms、10ms 和 50ms)读取驱动程序代码,以触发滑动空间机制。
When testing NVMe, we use the same method as Adelie [28] to open and read a file stored on the NVMe storage. The file is opened with O_DIRECT and O_SYNC flags, and a block size of 32 bytes is repeatedly read from the beginning of the file in a tight loop [28]. Afterwards, we set the block size to 64 bytes, 128 bytes, and 256 bytes, respectively. At the same time, we test the file reading throughput and CPU usage every 1 minute and compared them with Adelie’s test results. The results are shown in Figure 10.
在测试 NVMe 时,我们使用与 Adelie [28]相同的方法打开并读取存储在 NVMe 存储上的文件。文件使用 O_DIRECT 和 O_SYNC 标志打开,并从文件开头以 32 字节的块大小重复读取。之后,我们将块大小分别设置为 64 字节、128 字节和 256 字节。同时,我们每 1 分钟测试文件读取吞吐量和 CPU 使用率,并将其与 Adelie 的测试结果进行比较。结果如图 10 所示。
在测试 NVMe 时,我们使用与 Adelie [28]相同的方法打开并读取存储在 NVMe 存储上的文件。文件使用 O_DIRECT 和 O_SYNC 标志打开,并从文件开头以 32 字节的块大小重复读取。之后,我们将块大小分别设置为 64 字节、128 字节和 256 字节。同时,我们每 1 分钟测试文件读取吞吐量和 CPU 使用率,并将其与 Adelie 的测试结果进行比较。结果如图 10 所示。
When testing E1000e, we run ApacheBench to test network performance. During the experiment, we test the network throughput and CPU usage at 0, 1, 5, and 10 minutes after the driver is loaded. The test results are shown in Figure 11.
在测试 E1000e 时,我们运行 ApacheBench 来测试网络性能。在实验过程中,我们在驱动程序加载后的 0、1、5 和 10 分钟测试网络吞吐量和 CPU 使用率。测试结果如图 11 所示。
在测试 E1000e 时,我们运行 ApacheBench 来测试网络性能。在实验过程中,我们在驱动程序加载后的 0、1、5 和 10 分钟测试网络吞吐量和 CPU 使用率。测试结果如图 11 所示。
In the above experiments, the I/O waiting time exceeded the CPU running time. Therefore, they cannot verify the running status of the driver under CPU constraints. Similar to Adelie [28], we also create a dummy device driver that implements a null ioctl operation. We repeatedly execute the syscall ioctl to call the driver code and measure the number of ioctl operations performed per second. The results are shown in Figure 12.
在上述实验中,I/O 等待时间超过了 CPU 运行时间。因此,它们无法验证在 CPU 限制下驱动程序的运行状态。类似于 Adelie [28],我们也创建了一个实现空 ioctl 操作的虚拟设备驱动程序。我们反复执行 syscall ioctl 来调用驱动程序代码,并测量每秒执行的 ioctl 操作次数。结果如图 12 所示。
在上述实验中,I/O 等待时间超过了 CPU 运行时间。因此,它们无法验证在 CPU 限制下驱动程序的运行状态。类似于 Adelie [28],我们也创建了一个实现空 ioctl 操作的虚拟设备驱动程序。我们反复执行 syscall ioctl 来调用驱动程序代码,并测量每秒执行的 ioctl 操作次数。结果如图 12 所示。
The above results show that Dbox incurs significant overhead in the early stage of driver running. As the driver runs, the overhead gradually decreases. The reason is that all ICT instructions will trigger system traps when they enter/exit the untrusted driver for the first time. Then, the instructions triggering system traps will be analyzed by Dbox. For the legal instructions, they will be recorded and rewritten, which can prevent them from triggering system traps when they are called again. As the driver runs, more and more legal ICT instructions are captured and recorded. Therefore, the number of system traps will decrease, which can reduce overhead gradually. However, the overhead incurred by Dbox is still more than Adelie, especially when the re-randomization interval is very small. The reason is our targets are untrusted drivers without source code instead of the open-source drivers protected by Adelie. As a result, Dbox cannot construct a control flow transfer table offline by analyzing the source code like Adelie. In addition, compared to Adelie, Dbox can detect entry_flows and exit_flows, which also increases more overhead.
上述结果表明,Dbox 在驱动程序运行的早期阶段会产生显著的开销。随着驱动程序的运行,开销逐渐减少。原因是所有 ICT 指令在首次进入/退出不可信驱动程序时都会触发系统陷阱。然后,触发系统陷阱的指令将由 Dbox 分析。对于合法指令,它们将被记录和重写,这可以防止它们在再次被调用时触发系统陷阱。随着驱动程序的运行,越来越多的合法 ICT 指令被捕获和记录。因此,系统陷阱的数量将减少,从而逐渐减少开销。然而,Dbox 产生的开销仍然比 Adelie 多,尤其是在重新随机化间隔非常小的情况下。原因是我们的目标是没有源代码的不可信驱动程序,而不是由 Adelie 保护的开源驱动程序。因此,Dbox 无法通过分析源代码像 Adelie 那样离线构建控制流转移表。此外,与 Adelie 相比,Dbox 可以检测 entry_flows 和 exit_flows,这也增加了更多的开销。
上述结果表明,Dbox 在驱动程序运行的早期阶段会产生显著的开销。随着驱动程序的运行,开销逐渐减少。原因是所有 ICT 指令在首次进入/退出不可信驱动程序时都会触发系统陷阱。然后,触发系统陷阱的指令将由 Dbox 分析。对于合法指令,它们将被记录和重写,这可以防止它们在再次被调用时触发系统陷阱。随着驱动程序的运行,越来越多的合法 ICT 指令被捕获和记录。因此,系统陷阱的数量将减少,从而逐渐减少开销。然而,Dbox 产生的开销仍然比 Adelie 多,尤其是在重新随机化间隔非常小的情况下。原因是我们的目标是没有源代码的不可信驱动程序,而不是由 Adelie 保护的开源驱动程序。因此,Dbox 无法通过分析源代码像 Adelie 那样离线构建控制流转移表。此外,与 Adelie 相比,Dbox 可以检测 entry_flows 和 exit_flows,这也增加了更多的开销。
Fortunately, Dbox is a event-triggered method. It does not randomize the untrusted driver periodically like Adelie. Although we periodically trigger the space sliding mechanism, the randomization frequency is not as high as that in the experiment in real execution scenarios. The current space of the untrusted driver will slide to an random location only when a code probe occurs. Moreover, it can also provide strong protection for the control flows of the running drivers, which is a capability not available in other KASLR methods. More importantly, Dbox can detect untrusted drivers without source code, which is impossible for the methods [13, 25, 26] relying on source code.
幸运的是,Dbox 是一种事件触发的方法。它不像 Adelie 那样定期随机化不可信驱动程序。虽然我们定期触发空间滑动机制,但在实际执行场景中的随机化频率并不像实验中那么高。只有当代码探测发生时,不可信驱动程序的当前空间才会滑动到一个随机位置。此外,它还可以为运行中的驱动程序的控制流提供强大的保护,这是其他 KASLR 方法所不具备的能力。更重要的是,Dbox 可以在没有源代码的情况下检测不可信驱动程序,而方法 [13, 25, 26] 依赖于源代码,这是不可能实现的。
幸运的是,Dbox 是一种事件触发的方法。它不像 Adelie 那样定期随机化不可信驱动程序。虽然我们定期触发空间滑动机制,但在实际执行场景中的随机化频率并不像实验中那么高。只有当代码探测发生时,不可信驱动程序的当前空间才会滑动到一个随机位置。此外,它还可以为运行中的驱动程序的控制流提供强大的保护,这是其他 KASLR 方法所不具备的能力。更重要的是,Dbox 可以在没有源代码的情况下检测不可信驱动程序,而方法 [13, 25, 26] 依赖于源代码,这是不可能实现的。
8 CONCLUSION
To isolate and detect the untrusted drivers without source code, this paper proposes a method Dbox. It isolates the untrusted drivers in a private space. Unlike existing KASLR methods, Dbox does not fix the untrusted driver in a driver space. It builds a sliding space mechanism for untrusted drivers and dynamically adjusts their code permissions. Based on our designs, all entry_flows and exit_flows can be captured and analyzed. The experiment results and analysis show that Dbox has good defense against code probes, kernel rootkits, and CRAs. In addition, as the untrusted driver runs, the impact of Dbox on the untrusted driver will gradually decrease. As a result, Dbox does not introduce too much overhead to the protected drivers.
为了在没有源代码的情况下隔离和检测不可信驱动程序,本文提出了一种方法 Dbox。它在私有空间中隔离不可信驱动程序。与现有的 KASLR 方法不同,Dbox 不会将不可信驱动程序固定在驱动程序空间中。它为不可信驱动程序构建了一个滑动空间机制,并动态调整其代码权限。根据我们的设计,所有 entry_flows 和 exit_flows 都可以被捕获和分析。实验结果和分析表明,Dbox 对代码探测、内核 rootkit 和 CRAs 有良好的防御效果。此外,随着不可信驱动程序的运行,Dbox 对不可信驱动程序的影响将逐渐减少。因此,Dbox 不会给受保护的驱动程序引入过多的开销。
为了在没有源代码的情况下隔离和检测不可信驱动程序,本文提出了一种方法 Dbox。它在私有空间中隔离不可信驱动程序。与现有的 KASLR 方法不同,Dbox 不会将不可信驱动程序固定在驱动程序空间中。它为不可信驱动程序构建了一个滑动空间机制,并动态调整其代码权限。根据我们的设计,所有 entry_flows 和 exit_flows 都可以被捕获和分析。实验结果和分析表明,Dbox 对代码探测、内核 rootkit 和 CRAs 有良好的防御效果。此外,随着不可信驱动程序的运行,Dbox 对不可信驱动程序的影响将逐渐减少。因此,Dbox 不会给受保护的驱动程序引入过多的开销。
However, Dbox still has some limitations. First, it is only effective for drivers that are loaded after the OS starts. For the drivers that start with the OS, Dbox cannot perform binary rewriting before they run. Fortunately, developers do not set untrusted drivers as the LKMs that start with the OS, let alone compile them into the kernel. Second, Dbox is only effective for the open-source Linux that is running on x86 processors equipped with VMX and EPT. Third, due to the limitation of the number of entries in the EPT list, Dbox can only detect up to 511 untrusted drivers at the same time. According to our observation, the total number of drivers does not exceed 511 in most scenarios, let alone untrusted drivers.
然而,Dbox 仍有一些局限性。首先,它仅对操作系统启动后加载的驱动程序有效。对于随操作系统启动的驱动程序,Dbox 无法在它们运行前进行二进制重写。幸运的是,开发人员不会将不可信的驱动程序设置为随操作系统启动的 LKMs,更不用说将它们编译到内核中。其次,Dbox 仅对运行在配备 VMX 和 EPT 的 x86 处理器上的开源 Linux 有效。第三,由于 EPT 列表中的条目数量有限,Dbox 同时只能检测最多 511 个不可信驱动程序。根据我们的观察,在大多数情况下,驱动程序的总数不会超过 511 个,更不用说不可信的驱动程序了。
然而,Dbox 仍有一些局限性。首先,它仅对操作系统启动后加载的驱动程序有效。对于随操作系统启动的驱动程序,Dbox 无法在它们运行前进行二进制重写。幸运的是,开发人员不会将不可信的驱动程序设置为随操作系统启动的 LKMs,更不用说将它们编译到内核中。其次,Dbox 仅对运行在配备 VMX 和 EPT 的 x86 处理器上的开源 Linux 有效。第三,由于 EPT 列表中的条目数量有限,Dbox 同时只能检测最多 511 个不可信驱动程序。根据我们的观察,在大多数情况下,驱动程序的总数不会超过 511 个,更不用说不可信的驱动程序了。
9 ACKNOWLEDGMENTS
This research was supported by the Fundamental Research Funds for the Central Universities (2023QN1078)
本研究得到了中央高校基本科研业务费(2023QN1078)的资助
本研究得到了中央高校基本科研业务费(2023QN1078)的资助
REFERENCES
[1] Ahmed M Azab, Peng Ning, Jitesh Shah, Quan Chen, Rohan Bhutkar, Guruprasad Ganesh, Jia Ma, and Wenbo Shen. 2014. Hypervision across worlds: Real-time kernel protection from the arm trustzone secure world. In Proceedings of the 2014 ACM SIGSAC Conference on Computer and Communications Security. 90–102.
Ahmed M Azab, Peng Ning, Jitesh Shah, Quan Chen, Rohan Bhutkar, Guruprasad Ganesh, Jia Ma, 和 Wenbo Shen. 2014. 跨世界的 Hypervision:从 ARM TrustZone 安全世界实现实时内核保护. 在 2014 ACM SIGSAC 计算机和通信安全会议论文集. 90–102.
Ahmed M Azab, Peng Ning, Jitesh Shah, Quan Chen, Rohan Bhutkar, Guruprasad Ganesh, Jia Ma, 和 Wenbo Shen. 2014. 跨世界的 Hypervision:从 ARM TrustZone 安全世界实现实时内核保护. 在 2014 ACM SIGSAC 计算机和通信安全会议论文集. 90–102.
[2] Davi L et al Biondo A, Conti M. 2018. The Guard’s Dilemma: Efficient {CodeReuse} Attacks Against Intel {SGX}. In Proceedings of the 27th USENIX Security Symposium. 1213–1227.
[2] Davi L 等. 2018. 《守卫者的困境:针对 Intel SGX 的高效代码重用攻击》. In 第 27 届 USENIX 安全研讨会论文集. 1213–1227.
[2] Davi L 等. 2018. 《守卫者的困境:针对 Intel SGX 的高效代码重用攻击》. In 第 27 届 USENIX 安全研讨会论文集. 1213–1227.
[3] Andrea Bittau, Adam Belay, Ali Mashtizadeh, David Mazières, and Dan Boneh. 2014. Hacking blind. In 2014 IEEE Symposium on Security and Privacy. IEEE, 227–242.
[4] Bramwell Brizendine and Austin Babcock. 2021. Pre-built JOP Chains with the JOP ROCKET: Bypassing DEP without ROP. Black Hat Asia (2021).
[4] Bramwell Brizendine 和 Austin Babcock. 2021. 使用 JOP ROCKET 预构建 JOP 链:无需 ROP 绕过 DEP。Black Hat Asia (2021).
[4] Bramwell Brizendine 和 Austin Babcock. 2021. 使用 JOP ROCKET 预构建 JOP 链:无需 ROP 绕过 DEP。Black Hat Asia (2021).
[5] Bigelow R et al Brown M D, Pruett M. 2021. Not so fast: understanding and mitigating negative impacts of compiler optimizations on code reuse gadget sets. In Proceedings of the ACM on Programming Languages. 1–30.
[6] Haubenwallner M et al. Canella C, Schwarz M. 2020. KASLR: Break it, fix it, repeat. In Proceedings of the 15th ACM Asia Conference on Computer and Communications Security. 481–493.
[7] Xinyang Ge, Nirupama Talele, Mathias Payer, and Trent Jaeger. 2016. Fine-grained control-flow integrity for kernel software. In 2016 IEEE European Symposium on Security and Privacy (EuroS&P). IEEE, 179–194.
[8] Ben Gras, Kaveh Razavi, Erik Bosman, Herbert Bos, and Cristiano Giuffrida. 2017. ASLR on the Line: Practical Cache Attacks on the MMU. . In NDSS, Vol. 17. 26.
[9] Jinyu Gu, Xinyue Wu, Wentai Li, Nian Liu, Zeyu Mi, Yubin Xia, and Haibo Chen. 2020. Harmonizing performance and isolation in microkernels with efficient intra-kernel isolation and communication. In 2020 USENIX Annual Technical Conference (USENIX ATC 20). 401–417.
[10] Polychronakis M et al. Göktaş E, Athanasopoulos E. 2014. Size Does Matter: Why Using {Gadget-Chain} Length to Prevent {Code-Reuse} Attacks is Hard. In Proceedings of the 23rd USENIX Security Symposium. 417–432.
[11] C. Harini and C. Fancy. 2020. A study on the prevention mechanisms for kernel attacks. In Artificial Intelligence Techniques for Advanced Computing Applications: Proceedings of ICACT 2020. Springer, 11–17.
[12] Williams D Holmes B, Waterman J. 2022. KASLR in the age of MicroVMs. In Proceedings of the Seventeenth European Conference on Computer Systems. 149– 165.
[13] Detweiler D et al Huang Y, Narayanan V. 2022. KSplit: Automating Device Driver Isolation. In Proceedings of the 16th USENIX Symposium on Operating Systems Design and Implementation (OSDI 22). 613–631.
[14] Christopher Jelesnianski, Jinwoo Yom, Changwoo Min, and Yeongjin Jang. 2020. Mardu: Efficient and scalable code re-randomization. In Proceedings of the 13th ACM International Systems and Storage Conference. 49–60.
Figure 10: The throughput and CPU usage of NVMe
Figure 11: The throughput and CPU usage of E1000e
Figure 12: The throughput and CPU usage of ioctl
[15] Groß S et al. Lekies S, Kotowicz K. 2017. Code-reuse attacks for the web: Breaking cross-site scripting mitigations via script gadgets. In Proceedings of the 2017 ACM SIGSAC Conference on Computer and Communications Security. 1709–1723.
[16] Jinku Li, Xiaomeng Tong, Fengwei Zhang, and Jianfeng Ma. 2018. Fine-cfi: finegrained control-flow integrity for operating system kernels. IEEE Transactions on Information Forensics and Security 13, 6 (2018), 1535–1550.
[17] YongGang Li, Yeh-Ching Chung, Jinbiao Xing, Yu Bao, and Guoyuan Lin. 2022. MProbe: Make the code probing meaningless. In Proceedings of the 38th Annual Computer Security Applications Conference. 214–226.
[18] YongGang Li, GuoYuan Lin, Yeh-Ching Chung, YaoWen Ma, Yi Lu, and Yu Bao. 2023. MagBox: Keep the risk functions running safely in a magic box. Future Generation Computer Systems 140 (2023), 282–298.
[19] Yong-Gang Li, Yeh-Ching Chung, Kai Hwang, and Yue-Jin Li. 2020. Virtual wall: Filtering rootkit attacks to protect linux kernel functions. IEEE Trans. Comput. 70, 10 (2020), 1640–1653.
[20] Yutao Liu, Tianyu Zhou, Kexin Chen, Haibo Chen, and Yubin Xia.2015. Thwarting memory disclosure with efficient hypervisor-enforced intra-domain isolation. In Proceedings of the 22nd ACM SIGSAC Conference on Computer and Communications Security. 1607–1619.
[21] Kangjie Lu, Wenke Lee, Stefan Nürnberger, and Michael Backes. 2016. How to Make ASLR Win the Clone Wars: Runtime Re-Randomization. . In NDSS.
[22] Yandong Mao, Haogang Chen, Dong Zhou, Xi Wang, Nickolai Zeldovich, and M Frans Kaashoek. 2011. Software fault isolation with API integrity and multiprincipal modules. In Proceedings of the Twenty-Third ACM Symposium on Operating Systems Principles. 115–128.
[23] Alyssa Milburn, Erik Van Der Kouwe, and Cristiano Giuffrida. 2022. Mitigating information leakage vulnerabilities with type-based data isolation. In 2022 IEEE Symposium on Security and Privacy (SP). IEEE, 1049–1065.
[24] Wei-Loon Mow, Shih-Kun Huang, and Hsu-Chun Hsiao. 2022. LAEG: Leak-based AEG using Dynamic Binary Analysis to Defeat ASLR. In 2022 IEEE Conference on Dependable and Secure Computing (DSC). IEEE, 1–8.
[25] Jacobsen C et al Narayanan V, Balasubramanian A. 2019. LXDs: Towards isolation of kernel subsystems. In Proceedings of the 2019 USENIX Annual Technical Conference (USENIX ATC 19). 269–284.
[26] Tan G et al Narayanan V, Huang Y. 2020. Lightweight kernel isolation with virtualization and VM functions.In Proceedings of the 16th ACM SIGPLAN/SIGOPS international conference on virtual execution environments. 157–171.
[27] Matthias Neugschwandtner, Alessandro Sorniotti, and Anil Kurmus. 2019. Memory categorization: Separating attacker-controlled data. In Detection of Intrusions and Malware, and Vulnerability Assessment: 16th International Conference, DIMVA 2019, Gothenburg, Sweden, June 19–20, 2019, Proceedings 16. Springer, 263–287.
[28] Ruslan Nikolaev, Hassan Nadeem, Cathlyn Stone, and Binoy Ravindran. 2022. Adelie: continuous address space layout re-randomization for Linux drivers. In Proceedings of the 27th ACM International Conference on Architectural Support for Programming Languages and Operating Systems. 483–498.
[29] Angelos Oikonomopoulos, Elias Athanasopoulos, Herbert Bos, and Cristiano Giuffrida. 2016. Poking holes in information hiding. In 25th USENIX Security Symposium (USENIX Security 16). 121–138.
[30] et al Pomonis, Marios. 2017. kRX:ˆ Comprehensive kernel protection against just-in-time code reuse. In Proceedings of the Twelfth European Conference on Computer Systems. 420–436.
[31] Xinhui Shao, Lan Luo, Zhen Ling, Huaiyu Yan, Yumeng Wei, and Xinwen Fu. 2022. fASLR: Function-based ASLR for resource-constrained IoT systems. In European Symposium on Research in Computer Security. Springer, 531–548.
[32] et al Shrivastava, Rajesh Kumar. 2022. Securing Internet of Things devices against code tampering attacks using Return Oriented Programming. Computer Communications 193 (2022), 38–46.
[33] Kevin Z Snow, Fabian Monrose, Lucas Davi, Alexandra Dmitrienko, Christopher Liebchen, and Ahmad-Reza Sadeghi. 2013. Just-in-time code reuse: On the effectiveness of fine-grained address space layout randomization. In 2013 IEEE symposium on security and privacy. IEEE, 574–588.
[34] Abhinav Srivastava and Jonathon T Giffin.2011. Efficient Monitoring of Untrusted Kernel-Mode Execution. . In NDSS. Citeseer.
[35] Göktas E. et al Van Der Veen, V. 2016. A tough call: Mitigating advanced codereuse attacks at the binary level. In Proceedings of the IEEE Symposium on Security and Privacy (SP). 934–953.
[36] Fernando Vano-Garcia and Hector Marco-Gisbert. 2020. KASLR-MT: Kernel address space layout randomization for multi-tenant cloud systems. J. Parallel and Distrib. Comput. 137 (2020), 77–90.
[37] Wenhao Wang, Guangyu Hu, Xiaolin Xu, and Jiliang Zhang. 2021. CRAlert: Hardware-assisted code reuse attack detection. IEEE Transactions on Circuits and Systems II: Express Briefs 69, 3 (2021), 1607–1611.
[38] Zhe Wang, Chenggang Wu, Jianjun Li, Yuanming Lai, Xiangyu Zhang, Wei-Chung Hsu, and Yueqiang Cheng. 2017. Reranz: A light-weight virtual machine to mitigate memory disclosure attacks. In Proceedings of the 13th ACM SIGPLAN/SIGOPS International Conference on Virtual Execution Environments. 143–156.
[39] Cui W et al Wang Z, Jiang X. 2009. Countering kernel rootkits with lightweight hook protection. In Proceedings of the 16th ACM conference on Computer and communications security. 545–554.
[40] Mengfei Xie, Yan Lin, Chenke Luo, Guojun Peng, and Jianming Fu. 2022. PointerScope: Understanding Pointer Patching for Code Randomization. IEEE Transactions on Dependable and Secure Computing (2022).
[41] Yutian Yang, Songbo Zhu, Wenbo Shen, Yajin Zhou, Jiadong Sun, and Kui Ren. 2019. ARM pointer authentication based forward-edge and backward-edge control flow integrity for kernels. arXiv preprint arXiv:1912.10666 (2019).
[42] Sungbae Yoo, Jinbum Park, Seolheui Kim, Yeji Kim, and Taesoo Kim. 2022. {InKernel}{Control-Flow} Integrity on Commodity {OSes} using {ARM} Pointer Authentication.In 31st USENIX Security Symposium (USENIX Security 22). 89–106.
[43] Changwei Zou, Xudong Wang, Yaoqing Gao, and Jingling Xue. 2022. Buddy stacks: Protecting return addresses with efficient thread-local storage and runtime re-randomization. ACM Transactions on Software Engineering and Methodology (TOSEM) 31, 2 (2022), 1–37.