Rooting, Flashing, & Bootloader Exploits

Reverse Engineering KernelSU: Dissecting the Userspace-Kernel Driver Communication Protocol

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to KernelSU and its Architecture

KernelSU stands as a modern, powerful root solution for Android devices, leveraging kernel-level capabilities to provide systemless root. Unlike traditional root methods that often modify the `boot` partition or system files, KernelSU integrates directly into the kernel, allowing for greater control and stealth. At its core, KernelSU operates through a tripartite architecture:

  • Kernel Module: The heart of KernelSU, loaded directly into the Linux kernel. It intercepts system calls, manages root permissions, and provides the low-level functionalities.
  • Userspace Daemon: A persistent process running in userspace, responsible for managing root requests, enforcing policies, and communicating with the kernel module.
  • ksu Utility/Manager App: The primary interface for users and applications to interact with KernelSU, sending commands to the userspace daemon, which then relays them to the kernel module.

The critical element enabling this sophisticated interaction is the communication protocol between the userspace daemon/utility and the kernel module. This guide delves into reverse engineering this protocol, primarily focusing on the `ioctl` interface, which is the cornerstone of userspace-kernel interaction for device drivers.

Identifying the KernelSU Communication Channel

The most common and robust method for userspace applications to communicate with kernel modules in Linux is through character devices and the `ioctl` (input/output control) system call. KernelSU follows this established pattern. Our first step is to identify the character device that KernelSU registers.

Locating the Character Device

On a rooted Android device with KernelSU installed, you can often find clues by inspecting kernel messages or the `/dev` directory. The KernelSU source code reveals the device name, but for a black-box approach, we can look for newly created character devices.

adb shell
ls -l /dev | grep ksu

You will likely find an entry similar to:

crw-rw-rw- 1 root root 246, 0 2023-10-27 10:00 /dev/ksu

The `crw-rw-rw-` indicates a character device, and `246, 0` are the major and minor device numbers. This `/dev/ksu` entry is our target for interaction.

Dissecting the `ioctl` Interface

Once the device file is identified, the next step is to understand the `ioctl` commands it accepts. The `ioctl` system call has the following signature:

int ioctl(int fd, unsigned long request, ...);

Here, `fd` is the file descriptor for `/dev/ksu`, and `request` is a unique command code. The third argument is typically a pointer to a data structure passed between userspace and kernel, or an integer value.

Finding `ioctl` Command Codes

Without source code, reverse engineering involves static analysis of the userspace binaries (the `ksu` manager app or the `kernelsu` daemon) and dynamic analysis using tools like `strace`.

Static Analysis (Binary Disassembly)

If you have the `ksu` binary (e.g., from the KernelSU manager app’s APK), you can decompile it using tools like Ghidra or IDA Pro. Look for calls to `ioctl` and inspect the `request` argument. These `request` values are typically defined using kernel macros like `_IO`, `_IOR`, `_IOW`, `_IOWR`, which encode the magic number, command number, size, and direction of data transfer.

Dynamic Analysis with `strace`

The `strace` utility is invaluable for observing system calls. You can attach `strace` to the `kernelsu` daemon or run the `ksu` utility under `strace` to see the `ioctl` calls in action.

adb shell
# Find the PID of the KernelSU daemon
ps -A | grep kernelsu
# Let's assume PID is 1234
strace -e ioctl -p 1234

# Or, to trace ksu utility commands:
strace -e ioctl ksu shell whoami

You’ll see output similar to this, revealing the `ioctl` calls and their arguments:

ioctl(3, _IOC(_IOC_READ|_IOC_WRITE, 'K', 0x1, 0x18 /* KSU_IOC_GET_VERSION */), {0x...})
ioctl(3, _IOC(_IOC_WRITE, 'K', 0x2, 0x0 /* KSU_IOC_SET_POLICY */), {0x...})

The `_IOC` macro values are crucial. For instance, `_IOC(_IOC_READ|_IOC_WRITE, ‘K’, 0x1, 0x18)` translates to: read/write access, magic number ‘K’, command number 0x1, and data size 0x18 bytes. The `KSU_IOC_GET_VERSION` and `KSU_IOC_SET_POLICY` are symbolic names often found in KernelSU’s source, mapping to specific command numbers.

Example `ioctl` Commands and Structures

Let’s consider a few hypothetical (but realistic, based on common driver patterns) KernelSU `ioctl` commands:

  • KSU_IOC_GET_VERSION: Retrieves the KernelSU module version.
  • KSU_IOC_SET_POLICY: Sets the root access policy for a specific UID/PID.
  • KSU_IOC_EXEC_CMD: Executes a command with elevated privileges.

Each of these would typically involve a specific data structure:

1. Getting Version (Userspace)

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

// Assuming these are defined in a shared header or reverse engineered
#define KSU_IOC_MAGIC   'K'
#define KSU_IOC_GET_VERSION _IOR(KSU_IOC_MAGIC, 1, int)

int main() {
    int fd = open("/dev/ksu", O_RDWR);
    if (fd < 0) {
        perror("Failed to open /dev/ksu");
        return 1;
    }

    int version = 0;
    if (ioctl(fd, KSU_IOC_GET_VERSION, &version) < 0) {
        perror("ioctl KSU_IOC_GET_VERSION failed");
        close(fd);
        return 1;
    }

    printf("KernelSU Version: %dn", version);
    close(fd);
    return 0;
}

2. Setting Policy (Kernel Module Handler)

On the kernel side, there would be a handler function associated with the `/dev/ksu` character device, typically named `ksu_ioctl` or similar, which uses a `switch` statement to dispatch based on the `request` code.

// Example kernel-side `ioctl` handler snippet
// From drivers/misc/kernelsu.c or similar
long ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    void __user *argp = (void __user *)arg;
    struct ksu_policy_config policy_cfg;
    int ret = 0;

    switch (cmd) {
        case KSU_IOC_GET_VERSION:
            // ... handle getting version ...
            if (copy_to_user(argp, &current_ksu_version, sizeof(int)))
                return -EFAULT;
            break;
        case KSU_IOC_SET_POLICY:
            if (copy_from_user(&policy_cfg, argp, sizeof(policy_cfg)))
                return -EFAULT;
            // Apply policy_cfg.uid, policy_cfg.allow_root, etc.
            printk(KERN_INFO "KSU: Setting policy for UID %d to %dn",
                   policy_cfg.uid, policy_cfg.allow_root);
            // ... update internal policy structure ...
            break;
        // ... other commands ...
        default:
            ret = -ENOTTY; // No such ioctl command
    }
    return ret;
}

The `ksu_policy_config` structure would be defined in a shared header, specifying fields like `uid`, `allow_root` (boolean), `allow_mount_namespaces`, etc.

Practical Reverse Engineering Workflow

To systematically reverse engineer the KernelSU protocol:

  1. Obtain Components:

    Extract the `kernelsu.ko` kernel module from a device (e.g., from `/data/adb/modules/kernelsu/kernelsu.ko` or the boot image). Get the `ksu` binary from the manager app’s APK or directly from `/data/adb/ksu/bin/ksu`.

  2. Dynamic Analysis (`strace`):

    Run `strace -e ioctl` on the `kernelsu` daemon (if running as a persistent process) and the `ksu` utility. Observe different commands (e.g., `ksu shell id`, `ksu su -c ls`, `ksu policy set <uid> allow`). Note down the `ioctl` command codes and the sizes of the data structures passed.

  3. Static Analysis (Kernel Module):

    Use `readelf -s kernelsu.ko` to inspect symbols, looking for `ksu_ioctl` or similar. Decompile `kernelsu.ko` using Ghidra/IDA Pro. Locate the `ioctl` handler function. Analyze the `switch` statement that dispatches based on the `cmd` argument. This will reveal the kernel-side implementation for each `ioctl` code and the expected data structures (using `copy_from_user`/`copy_to_user`).

  4. Static Analysis (Userspace Binary):

    Decompile the `ksu` utility. Look for calls to `open(“/dev/ksu”, …)` and `ioctl(fd, …)`. Match the `ioctl` command codes found in the kernel module. Reconstruct the userspace data structures based on how they are populated before the `ioctl` call.

  5. Reconstruct Shared Headers:

    Based on the analysis from both kernel and userspace, you can reconstruct the `ioctl` command definitions (magic number, command numbers, direction, size) and the structures passed as arguments. This effectively reverse engineers the communication protocol’s API.

Security Implications and Further Exploration

Understanding the KernelSU communication protocol is not merely an academic exercise. It has several practical implications:

  • Custom Tooling: Developers can create custom tools or automation scripts that interact directly with the KernelSU driver, bypassing the standard `ksu` utility if needed.
  • Debugging and Analysis: Deeper insight into how KernelSU manages permissions and executes commands can aid in debugging issues or analyzing its behavior in specific scenarios.
  • Security Research: Identifying potential vulnerabilities in the `ioctl` interface (e.g., improper input validation, buffer overflows in data structures, time-of-check-to-time-of-use race conditions) could lead to privilege escalation or bypasses.
  • Interoperability: Creating modules or applications that integrate more seamlessly with KernelSU by understanding its native interaction mechanism.

Conclusion

Reverse engineering the userspace-kernel driver communication protocol of KernelSU provides a profound understanding of its internal workings. By meticulously analyzing the character device interface, `ioctl` commands, and the data structures involved, we can demystify how KernelSU orchestrates its powerful systemless root capabilities. This knowledge empowers developers, security researchers, and enthusiasts to better utilize, debug, and secure Android’s root ecosystem.

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner