# HYPERSEC: An Extensible Hypervisor-Assisted Framework for Kernel Rootkit Detection

Lionel Hemmerlé, Guillaume Hiet, Frédéric Tronel, Pierre Wilke, Jean-Christophe Prévotet

► To cite this version:

Lionel Hemmerlé, Guillaume Hiet, Frédéric Tronel, Pierre Wilke, Jean-Christophe Prévotet. HYPERSEC: An Extensible Hypervisor-Assisted Framework for Kernel Rootkit Detection. ISC 2025 - 28th Information Security Conference, Oct 2025, Seoul, South Korea. pp.431-451, 10.1007/978-3-032-08124-7_25 . hal-05469459

HAL Id: hal-05469459

https://inria.hal.science/hal-05469459v1

Submitted on 21 Jan 2026

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

<figure><img src="image_1.png" /></figure>

Distributed under a Creative Commons CC BY 4.0 - Attribution - International License
# HYPERSEC: an Extensible Hypervisor-Assisted Framework for Kernel Rootkit Detection

Lionel Hemmerlé<fnref n="1" />[0009-0002-3883-9664], Guillaume Hiet<fnref n="1" />[0000-0002-7176-9760], Frédéric Tronel<fnref n="1" />[0000-0002-2420-6105], Pierre Wilke<fnref n="1" />[0000-0001-9681-644X], and Jean-Christophe Prévotet<fnref n="2" />[0000-0001-6951-4702]

**Abstract.** Modern Endpoint Detection and Reaction (EDR) systems must remain reliable even when attackers acquire high privileges within the monitored operating system. Ideally, such systems should be protected from compromise, while maintaining deep visibility into the internal behavior of the target system. To meet this challenge, we aim to isolate the monitored system in a virtual machine (VM) and move the EDR logic into the hypervisor. We present HYPERSEC, a domain-specific language that allows the VM to safely delegate detection logic to the hypervisor by transmitting specialized monitoring programs. This design bridges the semantic gap by enabling the hypervisor to understand OS-level structures, while constraining accepted programs to protect the hypervisor against potential vulnerabilities. We evaluated our system against kernel rootkits that hide processes or elevate privileges and showed that HYPERSEC enables reliable detection of such threats. Our results indicate that the performance overhead remains acceptable, paving the way for broader adoption of hypervisor-based EDR with custom in-VM insight.

**Keywords:** Security · Hypervisor · Intrusion Detection Systems · Rootkit

## 1 Introduction

Modern Host-based Intrusion Detection Systems (HIDS), or more broadly Endpoint Detection and Response (EDR hereafter) systems, are essential components for detecting attacks directly within operating systems. These systems may operate as privileged user-space processes or kernel-space modules, relying on access to system internals to detect threats such as local privilege escalation.

If running inside the monitored operating systems, it has been shown that EDR can be disabled by attacker [41]. To strengthen security guarantees, recent research has explored out-of-guest monitoring approaches that isolate detection

<footnote n="1">¹ CentraleSupélec, Inria, CNRS, University of Rennes
`firstname.lastname@centralesupelec.fr`</footnote>

<footnote n="2">² UnivRennes, INSA Rennes, CNRS, IETR-UMR 6164
`jean-christophe.prevotet@insa-rennes.fr`</footnote>
mechanisms from the system under surveillance. A prominent line of work relies on Virtual Machine Introspection (VMI), which allows a hypervisor to observe guest VM behavior without relying on its cooperation. Garfinkel and Rosenblum introduced this principle in their seminal paper [17]. Additionally, practical implementations like Samsung's Real-time Kernel Protection (RKP) [37] have shown how ARM-based systems can support such external monitoring.

However, deploying detection mechanisms outside the monitored system introduces a semantic gap, as the hypervisor does not natively understand OS-level abstractions (e.g., process lists or syscall tables). This semantic gap has been extensively studied in [20,13,16,42], and bridging it remains a fundamental challenge. Moreover, research has revealed that static monitoring or periodic checking introduces windows of opportunity for attackers. In particular, [21] demonstrated that attackers can manipulate address translation mechanisms to hide their activity from VMI-based tools. Similarly, [24] showed that event-triggered monitoring is necessary to handle mutable kernel objects that may be modified between periodic checks. These works stress the importance of dynamic and semantic-aware monitoring that reacts to actual system events.

To improve detection coverage while retaining strong isolation guarantees, we aim to design a system in which the monitored OS actively assists the hypervisor by conveying semantic information without exposing the hypervisor to compromise. Our goal is to react to VM events rather than rely on time-triggered polling, while allowing high-level detection logic to remain adaptable and verifiable.

We introduce HYPERSEC, a hypervisor-assisted monitoring architecture for ARM-based systems, which are increasingly used in embedded, server, and IoT deployments. HYPERSEC allows the monitored guest VM to provision event-triggered inspection programs to the hypervisor using a domain-specific introspection language. These programs are evaluated and safely executed by the hypervisor on relevant events (e.g., process creation), enabling precise kernel-state monitoring. Importantly, the hypervisor verifies the safety of HYPERSEC programs before loading them, and does not require persistent in-guest agents (only a transient boot-time module).

Our solution stands out from state-of-the-art work by proposing an approach that is sufficiently generic to detect a wide range of malicious behaviors at the kernel level while ensuring strong isolation properties for the monitor and performance comparable to existing approaches. Generality and extensibility are achieved through a dedicated introspection language, which is expressive enough to capture various forms of invariants on kernel data, including those legitimately modified. The use of this dedicated language ensures a balance between expressiveness and the verification of the safety of inspection programs. Finally, HYPERSEC benefits from architectural support such as ARM's virtualization extensions, and its deployment model aligns with emerging trusted computing bases, including Intel ME and AMD PSP architectures [35]. The contributions of this research can be summarized as follows:
- We design HYPERSEC, a language dedicated to the specification of security monitors inside a hypervisor.

- We implement a verifier for HYPERSEC programs running inside the XVIsor [46] hypervisor.

- We implement three known and one new attacks that aim at hiding processes or elevating their privilege, as Linux kernel modules.

- We implement detection techniques against these attacks using HYPERSEC programs.

- We evaluate the performance and security of our approach using typical workloads.

The remainder of this article is organized as follows. First, Section 2 gives a high-level overview of our approach and Section 3 introduces several malicious techniques that we aim to detect with our solution. Section 4 presents the features of HYPERSEC, the proposed domain-specific language, and the associated security concerns. Section 5 reports on implementing HYPERSEC features on a Cortex-A53 ARM processor. Section 6 explains how we use HYPERSEC programs to detect all the rootkits described in Section 3. Section 7 describes the related work. Finally, Section 8 discusses the limitations of our approach and provides perspectives for future works. Section 9 concludes.

## 2 Threat Model and Approach

*Threat Model.* The OS kernel of VMs is assumed to be sane when it is first booted, so it can send valid information to the hypervisor. During that time, the integrity of the kernel can be protected using industry-grade security mechanisms like UEFI secure boot [40]. Once all the setup is done, the VM will send a specific signal to the hypervisor indicating not to accept any more information from the VM. After this, we suppose that the OS kernel of VMs can be compromised, and the hypervisor will not trust it anymore. Hence, we focus on detecting malware that can change the internal data structures of the kernel. We leave the task of detecting user-land malware to regular EDRs, which can themselves be protected by our system. Our hypervisor itself is assumed to be safe, and our goal is that our approach does not introduce vulnerabilities in the hypervisor.

*Approach.* To detect kernel rootkits, we introduce the HYPERSEC language. The VM to be protected will describe how intrusions should be detected by writing HYPERSEC programs, compile these programs into binary executables, and send them to the hypervisor. These programs contain all the information needed for the hypervisor to cross the semantic gap. More precisely, they allow to attach actions (update of the hypervisor view of the OS, raising alerts, monitoring new events, ...) to events (memory writes in specific kernel data structures, privileged register modifications, kernel function calls). The hypervisor will verify HYPERSEC programs, and run them upon detecting specific events.

Safely executing programs from untrusted sources, in a high-privilege context, is a problem that has already been addressed by EBPF [10], with proof-carrying
code [30] or with Singularity [19], an OS that runs every process in ring 0 and relies on properties of its language to ensure the system's safety.

Our approach is analogous to the one used by EBPF inside the Linux kernel: EBPF is a language designed to trace events occurring in the kernel, from user-space. However, our approach differs from EBPF: the objective is not to provide the VM (the untrusted environment) with insight into the state of the hypervisor (the trusted environment) but rather the opposite: the hypervisor needs to get information about the state of the VM. Consequently, HYPERSEC programs can only read VM information, and send alerts to the hypervisor. We chose to design a new language instead of using directly EBPF because we wanted a simpler language, and only write a verifier in the hypervisor but no bytecode interpreter or JIT compiler. Furthermore, the implementation of EBPF in Linux would differ a lot from its implementation in an hypervisor. Our solution also has additional security constraints since an attacker may be able to manipulate our program's inputs (for example, the VM memory). Programs sent by the VM and executed by the hypervisor should not introduce vulnerabilities. In particular, they should not be able to access the whole hypervisor's memory. We develop these security aspects of HYPERSEC in Section 5.4.

## 3 Rootkits

In this section, we present rootkits targeting the Linux kernel (version 5.15.36). These rootkits are loaded either in the form of Linux kernel modules, or by exploiting vulnerabilities in the kernel, in the same vein as KOPYCAT [27].

We wrote five rootkits (`RK<sub>ro</sub>`, `RK<sub>vfs</sub>`, `RK<sub>pid</sub>` and two variants of `RK<sub>cred</sub>`) and used two existing ones (Diamorphine [28] and Brokepkg [8]). `RK<sub>cred</sub>` elevates a process' privileges, while the others aim to hide a process. To hide a process, a rootkit may delete or hide the directory corresponding to that process from the `/proc` filesystem. This can be achieved in a number of ways, as we will describe below. The techniques used in the rootkits `RK<sub>ro</sub>`, `RK<sub>vfs</sub>`, and `RK<sub>cred</sub>` have already been described in [25,38,15]. However, to the best of our knowledge, `RK<sub>pid</sub>`, described below, has not been mentioned elsewhere.

In Rutkowska's classification [36], Type 1 malware modifies areas of code or data that are supposed to remain constant during the system's lifetime, while Type 2 malware alters data that evolves dynamically over time. Type 1 is easier to detect and mitigate since ensuring the read-only property is sufficient. Type 2 is more difficult to detect, since we cannot simply make data read-only. Instead, we must rely on invariants that usually have to be inferred from the knowledge of OS internals. Furthermore, although such invariants exist, they may be transiently violated by the OS code. For example, consider the invariant "all processes appear in a list": even in the absence of attacker, this invariant is broken when the kernel inserts a newly created process. So we cannot rely on simply checking regularly the consistency of this list. Rather, we must synchronize with the kernel code, to check the consistency at some critical points (e.g. after the creation or destruction of a process).
**RK<sub>ro</sub>**: changes only purely static data, and **RK<sub>vfs</sub>** changes static data on a dynamic structure (e.g., a constant field in the **inode** structure). Those are Type 1 rootkits. **RK<sub>pid</sub>** change structures that are dynamically allocated and deallocated by the kernel (**pid** structures), and **RK<sub>cred</sub>** changes structures that can also be subject to legitimate changes (**task_struct** and **cred** structures), corresponding to Type 2 rootkits.

**RK<sub>ro</sub>: Modifying Read-Only Data.** A rootkit can hide a process by modifying the syscall table to intercept calls to `getdents64` (`get directory entries`) and filter out some entries, e.g., the directories corresponding to the processes to hide. To implement such rootkits, we used Diamorphine [28], which directly tampers with the syscall table to make the kernel call its functions instead of the original ones, and Brokepkg [8], which uses the ftrace mechanism, that directly changes the kernel code to intercept function calls. Those two rootkits can then be used to hide a process. To detect this attack from the hypervisor, it is necessary to detect and prevent any write access to the syscall table.

**RK<sub>vfs</sub>: Modifying the Virtual File System Operations.** The Virtual File System (VFS) provides a uniform interface for supported filesystem types. It uses **inode** structures to represent files and directories and **file_operations** structures with function pointers for inode operations. **RK<sub>vfs</sub>** modifies the pointer to the **file_operations** so that it points to an attacker-controlled structure, which filters entries of the `/proc` directory, as **RK<sub>ro</sub>**. This attack has been implemented in `adore-ng` [1] for AMD64; we re-implemented it for ARM64.

To detect this attack from the hypervisor, we need to detect writes to the structure **file_operations** used by the **inode** representing the directory `/proc` and to the **inode**'s field pointing to that **file_operations** structure. Note that both the syscall table and the **file_operations** relative to the `/proc` filesystem are placed in read-only pages in the VM and are not expected to be modified during legitimate VM execution. However, some fields of the **inode** structure can be legitimately modified. This raises new challenges for detecting malicious writes on that structure, which will be solved in Section 5.1.

**RK<sub>pid</sub>: Tampering with pid Structures.** In the kernel, processes and threads are represented by **task_struct** structures. To keep track of the running processes, the kernel uses a radix-tree containing **pid** structures, which contain a field **tasks** that references to the corresponding **task_struct**. The `/proc` VFS iterates on this radix-tree to construct the list of running processes. Therefore, our third rootkit, **RK<sub>pid</sub>**, hides processes by tampering with the **tasks** field, pointing to the corresponding **task_struct** from the **pid** structure.

To detect this attack, it is necessary to build a shadow copy of the list of running processes, by tracking the execution of the `kernel_clone` and `do_exit` kernel functions.
<figure><img src="image_2.png" /><figcaption>Fig. 1. Syntax of HYPERSEC</figcaption></figure>

**RK<sub>cred</sub>: Altering Credential Structures.** In each *task_struct* is a *cred* structure, which stores the various user identifiers (uid, euid, suid, fsuid) associated with the process. This structure is accessed for each operation that requires access control (e.g., *open*). The structure itself is supposed to be read-only, but the *cred* pointer in the *task_struct* can be modified, e.g. by the *setuid* syscall.

**RK<sub>cred</sub>** changes the *cred* structure of an unprivileged process to elevate its privileges to the root user. We implemented **RK<sub>cred</sub>** using two different methods: first with a kernel module that directly overwrites the credentials of the targeted process; second by exploiting a vulnerability in the BPF verifier [12], that allows arbitrary writes in the kernel memory.

To detect this attack, we need to distinguish legitimate changes by system calls of the *setuid* family, from illegitimate ones. We also need to verify that changes made by system calls are correct to ensure that an attacker does not use a system call and simultaneously rewrite the credentials to hide their attack from our detection system. This implies that we need a shadow copy in the hypervisor of the credentials of each process.

## 4 Language Specification

The HYPERSEC language allows to bridge the semantic gap between a VM and the hypervisor, and to specify security properties to be enforced. The hypervisor executes HYPERSEC programs to react to events occurring inside the VM. We also introduce verifications when loading programs to ensure that these programs cannot cause vulnerabilities in the hypervisor. Examples of HYPERSEC programs are given in Fig. 2 and Fig. 3.

The syntax of HYPERSEC is presented in Fig. 1. A program consists of a list of commands that are the messages sent by the VM to the hypervisor. These commands are: (1) `map m [ t₁ ] → t₂`, for the definition of a new map named *m* whose keys are of type *t₁* and values of type *t₂*. Maps are key-value stores which are persistent between several invocations of the hypervisor; (2) `fun f ( x : t ) → tₘet { i * }`, for the definition of a new function named
```go
func f(1: listener) {
    alert();
}

func protect_ro_data() {
    register_write($_start_rodata, $_[end_rodata] - $_[start_rodata, fAlert];
    register_write($_stext, $_[etext] - $_[stext, fAlert);
}

exec protect_ro_data
```

<caption>Fig. 2. A HYPERSEC program that detect writes on read-only section in the kernel.</caption>

```go
map syscalls_per_pid[u64] -> u64
fun onInvoke_syscall(1: listener) {
    task_struct_ptr: u64 = vm_reg(SP_ELO);
    pid: u64 = vm_mem_4(task_struct_ptr + offset_of(task_struct, pid));
    counter: u64 = 1;
    if contains(syscalls_per_pid, pid) {
        counter = get(syscalls_per_pid, pid) + 1;
    }
    set(syscalls_per_pid, pid, counter);
}

fun init() {
    register_exec($invoke_syscall, onInvoke_syscall);
}

exec init
```

<caption>Fig. 3. A HYPERSEC program to count the number of syscalls per process.</caption>

$f$ with arguments $x: \mathbb{N} \to \mathbb{N}$, returning a value of type $t_{ret}$, and whose body is $i^*$;
(3) `exec f`, for calling function `f`, previously defined. This command is typically used to initialize the monitor environment, and requires no argument; and
(4) `last_command`, which indicates that the VM will stop sending programs.

The functions consist of instructions: array update, variable declaration and initialization, fixed count loops, loops over map entries, conditional statements, function calls, emission of alerts, and function returns.

Expressions may be unsigned 64-bit integers $u$, variables $x$, maps $m$, access to a register of the VM `vm_reg(r)`, a kernel symbol $s$, binary or unary operations over expressions, a function call $f(e)$, array access $e_1[e_2]$, construction of an array by repeating $u$ times an expression $e$: $[e; u]$ or an array literal $[e]$. Observable VM registers are the program counter `pc`, the stack pointer `sp`, the system register `SP_ELO` and regular registers $x_i$ (with $i$ from 0 to 31).

The `SP_ELO` register is made available to recover the identity of the process currently executing. Indeed, the Linux kernel updates this register with the address of the `task_struct` of the current process when the VM executes in kernel mode. This assumption is only valid if traps never occur directly from user mode to hypervisor mode. This should always be the case since we specifically target events from kernel space. The hypervisor checks any deviation from this behaviour and raises an alert if necessary.

We provide several built-in functions: (1) `vm_mem_1`, `vm_mem_2`, `vm_mem_4`, `vm_mem_8` reads 1, 2, 4 or 8 bytes from the VM memory; (2) `register_write` and `register_exec` attach a handler to a write in a memory area and to the
execution of a given function, respectively, and returns an opaque object identify-
ing that handler; (3) `unregister` disables a previously defined handler; (4) `get`, `set` and reads and stores values in a map; (5) `contains` test if a key has an attributed value in a map; (6) `debug` prints a value; and (7) `alert` raises an alert when a rootkit is detected.

In `HYPERSEC`, kernel symbols are prefixed by a `$` character and are replaced (at preprocessing time, using the `/proc/kallsyms` file) by the corresponding addresses before sending the program to the hypervisor. Field offsets within data structures are written `offset_of(struct, field)`, and are compiled into actual offsets by reading the VM's debug information (BPF Type Format or BTF [7]) provided by recent Linux kernels. These primitives greatly help in bridging the semantic gap since address or structure offset can evolve between releases of the Linux kernel or depending on compilation options.

# 5 Language Implementation

Our implementation is based on XVisor [46], a small hypervisor that mainly targets ARM processors [31]. Our test setup is a Xilinx Zynq UltraScale+ ZCU104 development board, with a Cortex-A53 ARM processor, on which we run a VM with Linux (version 5.15.36) on top of XVisor. We extended XVisor with an execution environment and a verifier for compiled `HYPERSEC` programs. Our implementation largely differs from eBPF because our hypervisor does not include neither a JIT compiler nor a bytecode interpreter. It only verifies annotated binaries, using two simple passes over the instruction stream, making the process more tractable.

## 5.1 Intercepting Writes into Memory Locations

To intercept writes in memory, depending on the size of the memory area to be watched and its alignment, we rely either on page permissions of the second stage of the address translation mechanism (which is controlled by the hypervisor), or on hardware breakpoints.

**Page Permissions.** On ARM processors, virtual memory is configured by a set of page tables organized in a radix-tree structure. In a traditional setting, without any hypervisor, the translation of a virtual address is performed by splitting this address into several indexes and looking up the first index in the table whose address can be found in the system register `TTBR1_EL1`. This gives the address of the next-level page table to look up into, using the next index from the virtual address, and so on until the end of this lookup chain.

When a hypervisor is present, the address translation mechanism is split into two stages. The first stage translates VM virtual addresses into intermediate addresses. The second stage translates intermediate addresses into physical addresses, using a process similar to the first stage, starting from the page table whose address is given in the `VTTBR_EL2` register.
<figure><img src="image_3.png" /><figcaption>**Fig. 4.** Example of infinite loop caused by a memory trap in the hypervisor. The code comes from the compilation of the `rwsem_acquire_read` macro from the Linux kernel.</figcaption></figure>

The page tables corresponding to the first stage are controlled by the VM and, therefore, considered compromised, while the page tables corresponding to the second stage are controlled by the hypervisor and are considered safe.

To intercept writes in memory at a given virtual address, the hypervisor first translates this address into an intermediate address, using e.g the `at` (for *Address Translate*) ARM instruction. For architectures such as x86 that do not have similar instruction [47], one can also translate the virtual address directly using the page tables. Then, we modify the page table entry in the second stage that corresponds to that memory area to make it read-only. Thus, any write access to that memory area will trigger a page fault exception in the hypervisor. The page fault handler will then determine, using the `FAR_EL2` and `ESR_EL2` registers that contain the address of the memory access and its size, respectively, whether the access is authorized. We give some details about the treatment of this exception in section 5.3.

**Mega Pages.** To increase its performance, XVisor uses mega pages when mapping the VM memory to the physical memory. It thus exploits the possibility for page table level 2 to point directly to physical memory instead of a new page table. This reduces the number of steps needed to translate a virtual address. Consequently, since the data we want to protect are usually smaller than a mega page, we split those mega pages into 512 regular pages by creating the necessary level 3 page table to reduce the amount of data that may unnecessarily trigger exceptions in the hypervisor. Without this split, we would modify the permissions of the mega page and would trap every write access inside that mega page, incurring a larger overhead.

**Atomic Instructions and Exclusive Monitor.** In the course of implementing HYPERSEC, we encounter some difficulties related to atomic instructions and the way they are dealt with by ARM processors. This paragraph explains more thoroughly this difficulties and the way we solved them.

ARM v8.0 processors feature exclusive load and store instructions. ARM uses the load-linked/store-conditional semantics (also known as *ll/sc*) when the Large System Extensions extension [3] is not present. When an exclusive load is exe-
cuted, the processor stores the address of that load in a component named exclusive monitor. Then, when an exclusive store is executed, the processor compares the address in the exclusive monitor with the one written at by the instruction. The memory write only occurs if the two addresses match. The store instruction will then write the status result of the store in an additional register. Furthermore, the exclusive monitor is emptied every time another write at the address stored in the exclusive monitor is made or when a CLREX (*CLeaR* EXclusive) or ERET (*Exception Return*) instruction is executed. Both instructions have this behavior to ensure that the exclusive monitor is emptied in case of a context switch to detect the loss of atomicity of execution for the instructions encompassed between the load and the store. Fig. 4 shows an example of those instructions' usage from the `rwsem_acquire_read` macro [26] in the Linux kernel that can indeed turn into an infinite loop if it accesses an address monitored by the hypervisor. Without the proposed protection, the `ldaxr` instruction populates the exclusive monitor and reads at the address stored into `x0` (1), then the `stxr` instruction compares its address to the one stored in the exclusive monitor (2) and performs the write since they are equal. If the exclusive monitor is emptied between the load and the store, the conditional jump executes again that block of code (3).

In the literature, multiple approaches have been proposed to address this problem: either by using other atomic instructions [14], a Transactional Hardware Memory extension [11], or by inferring the result of the exclusive store based on the previous instructions [23]. The first two approaches are not available to us because our processor does not feature these extensions, and the third is unacceptable since any instruction could be present between the `ldxr` and the `stxr`. We decided to fully emulate the exclusive monitor inside the hypervisor. When an exclusive write or an exclusive load targets an area protected by the hypervisor, the hypervisor will start to intercept both reads and writes on that area (by removing read and write permissions from the page). If the faulting instruction is an exclusive load or store, the hypervisor will emulate the instruction and store the targeted address in a memory structure we call a *virtual exclusive monitor*. Furthermore, when this virtual exclusive monitor is not empty, the hypervisor must intercept when the `ERET` instruction is called in the VM. Using the Fine-Grained Trap extension [2] of ARM, we could have generated an interrupt when `ERET` instructions are emitted [2]. Unfortunately, this extension is only available from version 8.6 of ARM processors, while we are targeting an ARM processor v8.0 (as it is frequently the case in embedded systems). Therefore, we decided to empty the virtual exclusive monitor when any interrupt occurs in the VM context. For that, we used the technique to intercept the execution of specific instructions described in Section 5.2 to intercept when the first instruction of every segment of the Interrupt Vector Table is executed. To reduce the overhead, we enable that interception only when the exclusive monitor is not empty.
<figure><img src="image_4.png" /><figcaption>Fig. 5. Attacks against memory accesses interception.</figcaption></figure>

**Leveraging Hardware Watchpoints.** Intercepting writes by modifying the address translation mechanism requires to protect a whole page (4096 bytes on this architecture): this incurs a large performance penalty onto the VM, especially when areas surrounding the protected one are frequently written to, and even worse when those writes use the exclusive operations described previously.

To improve the performance of our implementation, we leverage hardware watchpoints to detect writes whenever its size is 8 bytes or less, and one hardware watchpoint is available (only 4 are available for each core on our processor). The VM kernel can also use watchpoints. Therefore, we configure the hypervisor to trap VM accesses to these registers by setting the corresponding flag in *MDCR_EL2* (*Monitor Debug Configuration Register*).

**Mitigating Evasion of the Memory Accesses Interception.** If an attacker knows that we are intercepting memory writes, they could try to use the first step of the address translation mechanism, which is controlled by the VM, to avoid being detected. That technique is called Address Translation Redirection Attack (ATRA) [21]. To that end, they can use two different methods depending on the hook type used by the EDR.

If we use the second stage of the address translation mechanism to protect a given intermediate address (IA in Fig. 5a), a rootkit could copy this memory region somewhere else (at another intermediate address IA') and remap the original virtual address VA onto the new IA', thus evading the monitoring. This is one of the attacks presented by Jang et al. [21]. To detect and mitigate this attack, we enforce read-only permissions on page table entries used to translate each memory area we want to protect. Furthermore, we need to intercept writes to *TTBR1_EL1*, which contains the address of the first table used in stage one of the address translation process, to ensure that the intruder cannot change it. For that, we modified the TRM bit in the register *HCR_EL2*.

If watchpoints are used to monitor access to a given virtual address, rootkits can evade them using a variant of ATRA: they can map the same intermediate address to an unprotected virtual address as shown in Fig. 5b. To detect this attack, we use the hardware watchpoint to intercept both reads and writes to an address, and before every access, we compare the current value stored at the given address to a shadow copy stored in the hypervisor (updated after every access). If those two values differ, it means that an attacker used an ATRA-like technique to bypass our detection system.
## 5.2 Detecting the Execution of an Instruction

To trap the execution at a given address, we replace the targeted instruction with a hypercall, and remove the write permission on this memory region in the second stage of address translation to prevent an attacker from removing the hypercall. When the hypercall instruction is executed, the hypervisor restores the initial instruction, executes it in the VM, and replaces that instruction with the hypercall again.

## 5.3 Single Stepping Mode

Upon detection of a write or execution event, the hypervisor needs to temporarily remove protection so that exactly one instruction is executed in the VM. To do so, we use the debug extension of ARM processors to trigger software-step exceptions each time an instruction is executed in the VM. The configuration of this mechanism is done through the `MDSCR_EL1` (*Monitor Debug System Control Register*), which can, by default, be accessed from kernel mode (EL1). We modify the `MDCR_EL2` (*Monitor Debug Configuration Register*) register to prevent the VM from modifying the `MDSCR_EL1` register, and so the generated step exceptions are handled in the hypervisor rather than in the OS kernel. Furthermore, we modify the `PSTATE` (*Processor State*) register to enable the software-step exceptions and disable other kernel mode interrupts. Without this last point, an attacker could trigger an kernel-level exception while the protection is lifted, and bypass our protection.

## 5.4 Hypervisor Verifications

The HYPERSEC programs that are supposed to protect the VM are sent by the VM itself. Therefore, we take some measures to ensure that these programs do not compromise the hypervisor and effectively protect the VM.

First, we restrict the ability of a VM to send commands to the hypervisor. We suppose the kernel is protected during its first boot, e.g. using Secure Boot. It can thus send its commands during this phase. When there are no more commands to be sent, the VM should send `last_command` to the hypervisor. Subsequent commands will be ignored by the hypervisor. This aims to prevent attackers who may compromise the VM from sending malicious programs.

The hypervisor verifies a number of properties when loading HYPERSEC programs, to ensure that they do not introduce vulnerabilities. Programs are sent together with annotations: each basic block of each function is annotated with preconditions, giving the type of registers and memory slots (in the stack or any memory area pointed by a register). When the type of a register is integer, additional bound and alignment information may be present. When it is a pointer type, information about the range of the pointed object is also provided.

Then, the hypervisor performs static analysis inside each basic block, to track the type information associated with registers and memory slots. With this analysis, the hypervisor can enforce that memory accesses are always aligned on
8-byte boundaries and within the bounds of the accessed objects. It also ensures that the type of the value read/written matches the type of the address accessed. This ensures that memory accesses are only allowed in the memory of the VM. Pointers themselves cannot be written to/read from memory.

Control flow is restricted to function calls and jumps to beginning of basic blocks inside the current function. Forward jumps are forbidden if they allow to enter a loop from anywhere else than its beginning. Backward jumps must target the beginning of a loop the current basic block is already in and only occurs if the iterator of that loop is decremented and different from zero. Loop iterators can only be modified this way. This ensures the termination of HYPERSEC programs.

Finally, when returning from a function, we ensure that callee-saved registers have been restored (including the stack pointer, the stack base and the link register), and if the function is expected to return a value, that the register x0 contains a value of the correct type.

If any instruction does not comply with the previous rules, the hypervisor rejects the program.

# 6 Rootkit Detection and Performance Evaluation

This section presents the various methods, using HYPERSEC programs, that we used to detect each rootkit and the overhead incurred by our EDR.

## 6.1 Detecting Rootkits using HYPERSEC

**RK<sub>ro</sub> and RK<sub>vfs</sub>.** To detect those rootkits, we must detect writes to addresses that are constant in kernel virtual address space. This therefore does not incur overhead for legitimate usage. To detect attacks on the VFS, we also detect writes on the *i_fop* field of the *inode* corresponding to the */proc* directory, which contains the address of the corresponding *file_operations* structure. Unlike the address of the *file_operations* used by */proc*, the address of the corresponding *inode* is not exported in */proc/kallsyms* and can vary between reboots, even when KASLR [9] is disabled. To find it, we use the address of the head of the *super_blocks* linked list.

**RK<sub>pid</sub>.** To detect malicious modifications of pid structures, the hypervisor must maintain a shadow list of running processes. To do so, we first extract the initial running process list by iterating over the radix tree containing all the pid structures. Then, we detect newly created *task_struct* by hooking on the function *on_wake_up_new_task* and we detect process destruction by hooking on the *do_exit* function. The running process list and the addresses of the corresponding *pid* and *task_struct* structures are stored in a dedicated map.

The kernel calls *proc_root_readdir* to list the contents of the */proc* directory. Consequently, we only need to verify that *pid* and *task_struct* are untampered with when that function is called. For that, at the beginning of the function call, the hypervisor iterates over all those structures to check that they
correspond to the data saved earlier. Following this approach, we do not detect
the initial tampering when the rootkit hides the process, but we detect it as soon
as the kernel calls proc_root_readdir, which gives us better performance than
detecting all writes to the task_struct and pid structures.

**RK<sub>cred</sub>**. To detect changes in cred structures, we need to store a copy of the user and group identifiers of every process. Thus, we adapted the code that detects **RK<sub>pid</sub>** to store a copy of those identifiers when processes are created. Since some syscalls can legitimately change the credentials of a process (e.g. setuid or execve), we detect when these are called and update the hypervisor's copy of the credentials accordingly.

To detect syscalls, we track the execution of the `invoke_syscall` function, whose second argument is the syscall code, to compute the new credentials using the arguments of the syscall. To correctly compute the credentials after the `execveat` syscall, we also need to know the owner and group of the executed file. For that, we add a hook in a specific instruction of the `bprm_execve` function to get the inode structure representing the binary, which contains the user and group IDs of the file and the setuid and setgid flags.

We could detect the exiting of `invoke_syscall` by inserting a breakpoint at the address stored in the return address register (x30), but this greatly reduces the performance of our detection system. Instead, we check at the beginning of the next system call if the credentials have changed according to the rules of the last system call that has been made.

Even then, an attacker could modify the process credentials within the window between the hypervisor's initial and subsequent checks. This results in a Time Of Check vs. Time Of Use vulnerability. Indeed, the `inode permission` function, which is used to check the process permission for various system calls (e.g., `open` or `faccessat`), will see the updated credentials that the hypervisor has not captured.

To protect against those attacks, we ensure that the corresponding fields of the cred and task_struct structure are not changed by the attacker while the kernel is using them. Since making those structures read-only adds a heavy overhead to the VM, we cannot protect them during the entire syscall. Instead, they are made read-only for the shortest time possible, only when the kernel uses them. Furthermore, since we only protect some fields of those struts, we use hardware watchpoints when available to detect writes on those fields. We implemented that protection for the `inode permission` function. Before calling this function, we check that the credentials have not been changed, and we make them read-only during the function call.

## 6.2 Performance Evaluation

One initial metric of interest is the number of lines of code needed to detect each of the previously discussed rootkits. We need only 8 lines of code to detect **RK<sub>ro</sub>**. We need 46 lines of code to detect **RK<sub>vfs</sub>**. The most complex rootkit
(RK<sub>cred</sub>) requires 524 lines of code for its detection, which we believe remains modest.

Performance results are shown in Tables 1, 4, 3 and 4. The columns contain the execution time of our different benchmarks. The baseline corresponds to launching the VM in the hypervisor without any detection. Each line RK<sub>i</sub> corresponds to activating protection against RK<sub>i</sub>.

<table><caption>**Table 1.** Average overhead on the compilation time of binutils, the Mibench tests, and the time needed for 10 MQTT clients to send 10000 messages when detecting the different attacks.</caption><thead><tr><th></th><th>binutils</th><th>mibench overheads</th><th>MQTT</th></tr></thead><tbody><tr><td>Baseline</td><td>147m42s</td><td>-</td><td>223.2s</td></tr><tr><td>RK<sub>ro</sub></td><td>148m53s (1%)</td><td>0.4%</td><td>266.0s (19.1%)</td></tr><tr><td>RK<sub>vfs</sub></td><td>148m16s (0%)</td><td>0.0%</td><td>253.2s (13.4%)</td></tr><tr><td>RK<sub>pid</sub></td><td>149m11s (1%)</td><td>0.0%</td><td>268.8s (20.4%)</td></tr><tr><td>RK<sub>cred</sub></td><td>218m52s (48%)</td><td>20.4%</td><td>535.0s (39.7%)</td></tr></tbody></table>

<table><caption>**Table 2.** Phoronix results for NGINX binary (overhead are in parentheses)</caption><thead><tr><th></th><th colspan="5">NGINX (requests/s)</th></tr><tr><th>Connexions</th><th>1</th><th>20</th><th>100</th><th>200</th><th>500</th></tr></thead><tbody><tr><td>Baseline</td><td>300.0</td><td>248.5</td><td>284.7</td><td>205.5</td><td>100.6</td></tr><tr><td>RK<sub>ro</sub></td><td>279.3 (7.4%)</td><td>217.5 (14.2%)</td><td>263.3 (8.1%)</td><td>181.6 (13.2%)</td><td>94.7 (6.3%)</td></tr><tr><td>RK<sub>vfs</sub></td><td>286.6 (4.7%)</td><td>225.0 (10.4%)</td><td>262.4 (8.5%)</td><td>186.5 (10.2%)</td><td>96.7 (4.0%)</td></tr><tr><td>RK<sub>pid</sub></td><td>272.4 (10.1%)</td><td>216.6 (14.7%)</td><td>258.1 (10.3%)</td><td>188.8 (8.9%)</td><td>100.7 (-0.1%)</td></tr><tr><td>RK<sub>cred</sub></td><td>173.6 (72.8%)</td><td>144.7 (71.8%)</td><td>164.6 (72.9%)</td><td>120.5 (70.6%)</td><td>52.0 (93.6%)</td></tr></tbody></table>

We evaluated the overhead of our rootkit detection programs by compiling binutils [5] (once in each configuration) and using the MiBench [18] automotive, network, telecoms, and security benchmarks (except for the PGP test, which could not be compiled for our architecture) 250 times for each configuration. To evaluate the overheads in an embedded context, we also ran mosquitto MQTT server on the VM and launched 10 clients that sent 10000 messages each.

To emulate realistic uses, we also used some benchmarks from the Phoronix test suite [34]: 7Zip (compression and decompression), NGINX (1, 50, 100, 200 and 500 simultaneous connections), sqlite (with one thread), OPENSSL (RSA4096 sign and verify), and REDIS (get, lpop, sadd, and set with 50 connections).

For all tests except NGINX and MQTT, the overheads induced by the detection of RK<sub>ro</sub>, RK<sub>vfs</sub> and RK<sub>pid</sub> are lower than 2%. For nginx and同期, the overheads can be up to 20.4% for those rootkits. We hypothesize that these
<table><caption>**Table 3.** Phoronix results for OPENSSL, sqlite and 7Zip binaries (overhead are in parentheses).</caption><thead><tr><th rowspan="2"></th><th colspan="2">OPENSSL RSA4096</th><th>sqlite</th><th colspan="2">7Zip (MIPS)</th></tr><tr><th>sign/s</th><th>verify/s</th><th>copy (s)</th><th>Compression</th><th>Decompression</th></tr></thead><tbody><tr><td>Baseline</td><td>16.3</td><td>1088.8</td><td>128.4</td><td>979.0</td><td>1259.0</td></tr><tr><td>RK<sub>ro</sub></td><td>16.2 (0.6%)</td><td>1087.6 (0.1%)</td><td>130.9 (2.0%)</td><td>974.0 (0.5%)</td><td>1256.0 (0.2%)</td></tr><tr><td>RK<sub>vfs</sub></td><td>16.2 (0.6%)</td><td>1088.0 (0.1%)</td><td>128.8 (0.4%)</td><td>975.0 (0.4%)</td><td>1258.0 (0.1%)</td></tr><tr><td>RK<sub>pid</sub></td><td>16.2 (0.6%)</td><td>1087.0 (0.2%)</td><td>129.6 (1.0%)</td><td>974.0 (0.5%)</td><td>1256.0 (0.2%)</td></tr><tr><td>RK<sub>cred</sub></td><td>16.2 (0.6%)</td><td>1084.0 (0.4%)</td><td>183.8 (43.2%)</td><td>963.0 (1.7%)</td><td>1249.0 (0.8%)</td></tr></tbody></table>

<table><caption>**Table 4.** Phoronix results for REDIS database (overhead are in parentheses).</caption><thead><tr><th colspan="5">REDIS, 50 simultaneous connexions (requests/s)</th></tr><tr><th></th><th>GET</th><th>LPOP</th><th>SADD</th><th>SET</th></tr></thead><tbody><tr><td>Baseline</td><td>202873.5</td><td>205450.5</td><td>166348.9</td><td>144450.2</td></tr><tr><td>RK<sub>ro</sub></td><td>202334.6 (0.3%)</td><td>205716.0 (-0.1%)</td><td>166786.5 (-0.3%)</td><td>144003.5 (0.3%)</td></tr><tr><td>RK<sub>vfs</sub></td><td>204196.1 (-0.6%)</td><td>204408.5 (0.5%)</td><td>165393.2 (0.6%)</td><td>143335.7 (0.8%)</td></tr><tr><td>RK<sub>pid</sub></td><td>204848.8 (-1.0%)</td><td>207348.5 (-0.9%)</td><td>165712.3 (0.4%)</td><td>144130.8 (0.2%)</td></tr><tr><td>RK<sub>cred</sub></td><td>190246.5 (6.6%)</td><td>194639.0 (5.6%)</td><td>154435.9 (7.7%)</td><td>135891.3 (6.3%)</td></tr></tbody></table>

services frequently interact with the kernel, and since the kernel code is now mapped in small pages instead of mega-pages, they induce more TLB cache misses.

The overhead incurred by the detection of RK<sub>cred</sub> is 20.4% on average for the MiBench benchmark and 48% for the compilation of binutils. As expected, the benchmarks that frequently interact with the kernel exhibit a significant overhead (the most extreme cases are MQTT with an overhead of 39.7% and NGINX with an overhead of 93.6%). At the same time, those that mainly perform calculations are less affected (OPENSSL and 7zip have an overhead between 0.4% and 1.7%). Indeed, every system call, and especially those that interact with inodes and process credentials, involves permission checks. This overhead keeps the system usable for typical workloads and is acceptable for most security-critical applications.

# 7 Related Work

Various methods have been suggested for bridging the semantic gap. Garfinkel et al. [17] proposed developing a library for a specific version of the operating system that retrieves data from the VM memory. This approach is highly accurate but not very versatile, as the library needs to be rewritten for each new version of the operating system. Xiao et al. [45] use heuristics to increase versatility and work on multiple operating systems. However, they can only retrieve partial data, such as the names and process IDs of the running processes. Zhao et al. [16] propose
a solution to reuse user-space programs as VM introspection tools, but their
solution is vulnerable to direct kernel object manipulation from an attacker.
Dolan-Gavit et al. [13] use a training phase to teach the intrusion detection
system how to retrieve data. If the training process is incomplete, the VMI tool
may fail to extract data from the VM memory. Westphal et al. [43] developed a
domain-specific language that allows users to specify probes that are triggered
on specific events inside a guest OS. However, their language can only describe
a subset of data structures inside the Linux kernel. Adding new data structures
would be possible, although this requires modifying the framework itself. On
the contrary, our language is sufficiently expressive to handle any data structure
used by a kernel (we are currently porting our approach to FreeBSD).

For our EDR to be compatible with multiple operating systems, we could use
a paravirtualization technique [44] by modifying the VM to communicate with
the hypervisor and provide information about its state. This would allow the VM
to delegate certain security checks to the hypervisor [29] and send information
to the hypervisor whenever specific events occur within the VM [32]. While this
approach can help to protect the VM and provide detailed information to the
hypervisor, it requires modifying the guest kernel code to enable communication
between the VM and the hypervisor. Additionally, it could reduce the resilience
of the EDR since an attacker could prevent the VM from communicating with
the hypervisor.

An approach closely related to ours was proposed by Wei and Amit [42],
but it has not been designed primarily for security purposes. Their equivalent of
our programs can be sent at any time by the VM. This could allow a privileged
attacker to turn off the security monitoring by sending malicious programs to the
hypervisor. Additionally, they rely on hyperupcalls to react to events occurring
in the hypervisor (such as memory reclaim or virtual processor preemption),
whereas our solution detects events happening within a VM. Bitdefender [6]
proposes a similar approach for x86. However, the semantic gap problem is not
solved by the protected VM but is hardcoded in the introspection library, which
is not flexible.

The detection of rootkits infecting OS kernels has been extensively stud-
ied in the literature. However, as highlighted by the recent study by Stühn et
al. [39], the effective detection of these malware remains an open problem that
requires specialized tools. In particular, few tools are available to detect rootkits
on Linux, and their detection capabilities are limited. According to the authors,
the most effective approach relies on memory snapshot analysis. However, while
this method is relevant for post-mortem analysis, it is not robust against eva-
sion when used for intrusion detection. Indeed, an attacker can erase their traces
before the memory snapshot is taken. The approach we propose is event-based
and does not suffer from this limitation.

Traditional signature-based detection methods are not resilient to evasion
techniques and cannot detect new rootkits. Anomaly detection approaches ap-
pear more promising. Pham et al. propose analyzing side channels [33], but
this method requires external hardware to measure electromagnetic emissions.
Landauer et al. suggest analyzing the execution time of kernel functions [22]. However, observing these execution times relies on probes located within the OS kernel, which can be compromised by an attacker. In the evaluation of our approach, we developed monitoring programs to monitor the integrity of invariants on mutable kernel data. Baliga et al. emphasised the importance of monitoring such invariants for kernel rootkit detection, but their approach requires an external hardware device for the monitoring [4].

# 8 Limitations and Future Work

**Reboots.** First of all, detecting VM reboot is trivial within the hypervisor. We could easily add a new event to the language, so that programs can take into account VM reboot. They could, for example, reset their internal state (maps) to reflect the fact that the VM has been restarted. A last problem is to retrieve the loading address of the kernel that can be randomized if KASLR [9] is enabled. We could hook into the `map_kernel` function that remap the kernel at random address to discover it dynamically and rebase all addresses accordingly.

**Kernel Updates.** Since kernel updates force the virtual machine to reboot, we propose to take advantage of this reboot to leverage on the possibility offered by (most) firmware to programmatically change the boot options of the next reboot so that the VM is not restarted immediately, but rather an isolated (and hence safe) *update* VM is started instead. This *update* VM will be minimalist to reduce its attack surface, and its unique goal will be to load the latest version of the HYPERSEC programs that correspond to the upgraded kernel. Finally, the *update* VM ends the whole process by rebooting and transferring control back to the upgraded VM.

**Attack Mitigation.** We are currently only detecting rootkits, but we could also mitigate some of them: attacks that only modify constant data like $RK_{ro}$ and $RK_{vfs}$ can be easily mitigated by discarding the incriminated memory writes. Rootkits that tamper with dynamic data ($RK_{pid}$ and $RK_{cred}$) are harder to mitigate since they can change existing data structures but they could also move those resources and update the references pointing to them. This makes it harder for the hypervisor to restore them without writing in incorrect places of the VM memory.

# 9 Conclusion

This paper presents HYPERSEC, a dedicated language for adaptive rootkit detection in virtual machines. HYPERSEC enables virtual machines to autonomously generate detection programs tailored to their specific configurations, enhancing adaptability and security. Our prototype was implemented on a ZCU104 platform featuring an ARM Cortex-A53 processor, selected to meet constraints from
an ongoing research project<fnref n="3" /> requirements. Despite this platform's modest virtualization performance compared to high-end Intel Xeon-based systems, our evaluation demonstrates the feasibility and potential of HYPERSEC for dynamic rootkit detection. All artifacts, including our XVisor extension, the rootkits we developed, and the detection scripts, are publicly available <fnref n="4" />.

**Acknowledgments.** The work presented in this paper was realized in the frame of the TrustGW project number "ANR-21-CE39-0005", supported by a grant of the French National Research Agency (ANR) and by the "France 2030" government investment plan managed by the French National Research Agency, under the reference "ANR-22-PECY-0005".

**Disclosure of Interests.** The authors have no competing interests to declare that are relevant to the content of this article.

## References

1. Linux rootkit adapted for 2.6 and 3.x. https://github.com/yaoyumeng/adore-ng (2024)

2. Armv8-a architecture registers. https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/HFGITR-EL2--Hypervisor-Fine-Grained-Instruction-Trap-Register (2024)

3. Introduction to Large System Extensions. https://learn.arm.com/learning-paths/servers-and-cloud-computing/lse/intro/ (2024)

4. Baliga, A., Ganapathy, V., Iftode, L.: Detecting kernel-level rootkits using data structure invariants. IEEE Trans. Dependable Secur. Comput. 8(5), 670–684 (Sep 2011). https://doi.org/10.1109/TDSC.2010.38

5. Source code. https://www.gnu.org/software/binutils/ (2024)

6. Hypervisor memory introspection - specification. https://hvmi.readthedocs.io/en/latest/index.html (2023)

7. Bpf type format (btf) — the linux kernel documentation. https://docs.kernel.org/bpf/btf.html (2024)

8. Buzeti, J.: R3tr074/brokepkg: The lkm rootkit working in linux kernels 2.6.x/3.x/4.x/5.x. https://github.com/R3tr074/brokepkg (2024)

9. Canella, C., Schwarz, M., Haubenwallner, M., Schwarzl, M., Gruss, D.: Kaslr: Break it, fix it, repeat. In: Proceedings of the 15th ACM Asia Conference on Computer and Communications Security. p. 481–493. ACM, New York, NY, USA (2020). https://doi.org/10.1145/3320269.3384747

10. Cilium: BPF and XDP Reference Guide (2022)

11. Cota, E.G., Bonzini, P., Bennée, A., Carloni, L.P.: Cross-isa machine emulation for multicores. In: 2017 IEEE/ACM International Symposium on Code Generation and Optimization (CGO). pp. 210–220. IEEE, Washington, D.C., USA (2017). https://doi.org/10.1109/CGO.2017.7863741

12. Vulnerability details. https://www.cvedetails.com/cve/CVE-2023-2163/ (2024)

<footnote n="3">³ https://trustgw projects.labsticc.fr/</footnote>

<footnote n="4">⁴ https://gitlab.inria.fr/SUSHI-public/hypersec</footnote>
13. Dolan-Gavitt, B., Leek, T., Zhivich, M., Giffin, J., Lee, W.: Virtuoso: Narrowing the semantic gap in virtual machine introspection. In: IEEE Symposium on Security and Privacy. pp. 297–312. IEEE, Washington, D.C., USA (2011). https://doi.org/10.1109/SP.2011.11

14. Exclusive monitors. https://dynamorio.org/page_ldstex.html (2024)

15. Eitani, A.: Detecting drovorub's file operations hooking with tracee. https://www.aquasec.com/blog/detect-drovorub-kernel-rootkit-attack-tracee/ (2022)

16. Fu, Y., Lin, Z.: Space traveling across vm: Automatically bridging the semantic gap in virtual machine introspection via online kernel data redirection. In: IEEE Symposium on Security and Privacy. pp. 586–600. IEEE, Washington, D.C., USA (2012). https://doi.org/10.1109/SP.2012.40

17. Garfinkel, T., Rosenblum, M.: A virtual machine introspection based architecture for intrusion detection. In: Proceedings of the Network and Distributed System Security Symposium, San Diego, California, USA. pp. 191–206. The Internet Society, 11710 Plaza America Drive, Suite 400, Reston, VA 20190, USA (2003)

18. Guthaus, M., Ringenberg, J., Ernst, D., Austin, T., Mudge, T., Brown, R.: Mibench: A free, commercially representative embedded benchmark suite. In: Proceedings of the Fourth Annual IEEE International Workshop on Workload Characterization. pp. 3–14. IEEE, Washington, D.C., USA (2001). https://doi.org/10.1109/WWC.2001.990739

19. Hunt, G.C., Larus, J.R.: Singularity: rethinking the software stack. ACM SIGOPS Operating Systems Review 41(2), 37–49 (2007). https://doi.org/10.1145/1243418.1243424

20. Jain, B., Baig, M.B., Zhang, D., Porter, D.E., Sion, R.: Sok: Introspections on trust and the semantic gap. In: 2014 IEEE Symposium on Security and Privacy, SP 2014, Berkeley, CA, USA, May 18-21, 2014. pp. 605–620. IEEE Computer Society, Washington, D.C., USA (2014). https://doi.org/10.1109/SP.2014.45

21. Jang, D., Lee, H., Kim, M., Kim, D., Kim, D., Kang, B.B.: Atra: Address translation redirection attack against hardware-based external monitors. In: Proceedings of the 2014 ACM SIGSAC Conference on Computer and Communications Security. pp. 167–178. ACM, New York, NY, USA (2014). https://doi.org/10.1145/2660267.2660303

22. Landauer, M., Alton, L., Lindorfer, M., Skopik, F., Wurzenberger, M., Hotwagner, W.: Trace of the times: Rootkit detection through temporal anomalies in kernel activity (2025), https://arxiv.org/abs/2503.02402

23. Lauterbach GmbH: Trace32: ARMv8-A/-R Debugger (2024), support for exclusive monitor, page 157

24. Lee, H., Moon, H., Heo, I., Jang, D., Jang, J., Kim, K., Paek, Y., Kang, B.B.: Kimon arm: A hardware-assisted event-triggered monitoring platform for mutable kernel object. IEEE Transactions on Dependable and Secure Computing 16(2), 287–300 (2017). https://doi.org/10.1109/TDSC.2017.2679710

25. Linux on-the-fly kernel patching without lkm. http://phrack.org/issues/58/7.html (2001)

26. lockdep.h. https://elixir bootlin.com/linux/v5.15.36/source/include/linux/lockdep.h#L535 (2022)

27. Matveychikov, I.V.: Kopycat - linux kernel module-less implant (backdoor). https://github.com/milabs/kopycat (2021)

28. Mello, V.R.: Diamorphine: Lkm rootkit for linux kernels 2.6.x/3.x/4.x/5.x/6.x (x86/x86_64 and arm64). https://github.com/m0nad/Diamorphine (2024)

29. Morris, J., Salaün, M., Gopinath, T.: Linux virtualization based security (lvbs). https://lpc.events/event/17/contributions/1515/ (2023)
30. Necula, G.C., Lee, P.: Safe kernel extensions without run-time checking. SIGOPS Oper. Syst. Rev. 30(SI), 229-243 (oct 1996). https://doi.org/10.1145/238721.238781

31. Patel, A., Daftedar, M., Shalan, M., El-Kharashi, M.W.: Embedded hypervisor xvisor: A comparative analysis. In: 2015 23rd Euromicro International Conference on Parallel, Distributed, and Network-Based Processing. pp. 682-691. IEEE, Washington, D.C., USA (2015). https://doi.org/10.1109/PDP.2015.108

32. Payne, B.D., Carbone, M., Sharif, M., Lee, W.: Lares: An architecture for secure active monitoring using virtualization. In: IEEE Symposium on Security and Privacy. pp. 233-247. IEEE, Washington, D.C., USA (2008). https://doi.org/10.109/SP.2008.24

33. Pham, D.P., Marion, D., Heuser, A.: Ultra: Ultimate rootkit detection over the air. In: Proceedings of the 25th International Symposium on Research in Attacks, Intrusions and Defenses. p. 232-251. ACM, New York, NY, USA (2022). https://doi.org/10.1145/3545948.3545962

34. phoronix test suite. https://www.phoronix-test-suite.com/ (2019)

35. Ruan, X.: Boot with Integrity, or Don't Boot. In: Platform Embedded Security Technology Revealed: Safeguarding the Future of Computing with Intel Embedded Security and Management Engine, chap. 6, pp. 143-163. Apress (Aug 2014). https://doi.org/10.1007/978-1-4302-6572-6_6

36. Rutkowska, J.: Introducing stealth malware taxonomy (2006)

37. Samsung: Real-time kernel protection. https://docs.samsungknox.com/admin/fundamentals/whitepaper/core-platform-security/real-time-kernel-protection/ (2024)

38. Linux rootkits part 3: A backdoor to root. https://xcellerator.github.io/posts/linux_rootkits_03/ (2020)

39. Stühn, J., Hilgert, J.N., Lambertz, M.: The hidden threat: Analysis of linux rootkit techniques and limitations of current detection tools. Digital Threats 5(3) (Oct 2024). https://doi.org/10.1145/3688808

40. UEFI Forum: Unified Extensible Firmware Interface Specification (Mar 2019), https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf, version 2.8

41. Wavestone Cybersecurity and Digital Trust practice: EDRSandBlast. https://gihub.com/wavestone-cdt/EDRSandblast (2022)

42. Wei, M., Amit, N.: Leveraging hyperupcalls to bridge the semantic gap: An application perspective. IEEE Data Eng. Bull. 42(1), 22-35 (2019)

43. Westphal, F., Axelsson, S., Neuhaus, C., Polze, A.: Vmi-pl: A monitoring language for virtual platforms using virtual machine introspection. Digital Investigation 11, S85-S94 (2014). https://doi.org/10.1016/j.diin.2014.05.016, fourteenth Annual DFRWS Conference

44. Whitaker, A., Shaw, M., Gribble, S.D., et al.: Denali: Lightweight virtual machines for distributed and networked applications. Tech. rep., University of Washington (02 2002)

45. Xiao, J., Lu, L., Wang, H., Zhu, X.: Hyperlink: Virtual machine introspection and memory forensic analysis without kernel source code. In: International Conference on Autonomic Computing. pp. 127-136. IEEE, Washington, D.C., USA (2016). https://doi.org/10.1109/ICAC.2016.46

46. Xvisor hypervisor. https://github.com/xvisor/xvisor (2024)

47. Zhao, S., Ding, X., Xu, W., Gu, D.: Seeing through the same lens: introspecting guest address space at native speed. In: 26th USENIX Security Symposium (USENIX Security 17). pp. 799-813. USENIX Association, Vancouver, BC (2017)