How to inject code into the Linux kernel without source code and debug symbols
Imagine this scenario: you have a ready-made Linux kernel binary for a mobile device or embedded ARM64 board. No source code is available, debug information was stripped during the build, and you need to intercept a system call or add your own logic right at the kernel level.
The usual approach in such cases is lengthy and tedious. You have to reverse engineer the binary in IDA Pro or Ghidra, manually find function offsets, and write fragile patches. A developer under the nickname bmax121 decided to simplify this process and released the KernelPatch project.
Why KernelPatch is needed
KernelPatch is a framework for patching and hooking the Linux kernel when all you have to work with is a stripped kernel image like vmlinux or boot.img.
When a kernel is built without debug information, traditional debugging tools like kprobes refuse to work without additional workarounds. KernelPatch solves this problem on its own. It recovers the symbol table directly from the kernel binary and provides a convenient API for modifying executable code.
The project is useful for reverse engineers, Android security researchers, and low-level software developers for embedded systems.
What the framework can do
The developer built several important capabilities into the project for working with the kernel:
- Symbol search without source code. The tool parses internal
kallsymsstructures within the ready-made image. It doesn't need externalSystem.mapfiles or source code. A similar approach is used in thevmlinux-to-elfutility, from which the author borrowed some ideas. - Two code injection modes. You can make a static patch to the image (for example, overwrite
boot.imgon disk) or load code dynamically at runtime. - Function interception via inline hook. For the ARM64 architecture, call interception is based on the work of
android-inline-hook. The framework correctly handles relative addressing instructions (b,bl,adrp) so as not to break execution of the original code. - System call hooks. Replacing addresses in the
sys_call_tabletable takes just a couple of commands. - Built-in SU module. For Android devices, the project includes its own superuser implementation.
How it works internally
To load user modules (KPM — Kernel Patch Modules) on the fly, the framework needs memory inside the kernel. For this, KernelPatch includes a compact TLSF allocator. It helps allocate executable memory pages (ROX) directly in the kernel address space.
When performing an inline hook, the framework reads the first instructions of the target function, forms a so-called trampoline from them, and writes a jump to your handler. When your code finishes executing, control returns to the original function via the trampoline.
+-------------------+ +---------------------+ +-------------------+
| Целевая функция | ---> | Ваш обработчик KPM | ---> | Трамплин (Orig) |
| (инструкция JMP) | | (пользовательский) | | (продолжение) |
+-------------------+ +---------------------+ +-------------------+
System requirements and limitations
The project has clear boundaries of applicability that are important to know in advance:
- Only the ARM64 architecture is supported. There are no versions for x86_64 or RISC-V yet.
- Kernel version range — from Linux 3.18 to 6.6.
- The main requirement is that the kernel must be built with the
CONFIG_KALLSYMS=yflag.
If the firmware vendor completely removed CONFIG_KALLSYMS at the build stage, KernelPatch won't be able to automatically parse function addresses. Fortunately, this flag is left enabled in most Android and ARM device firmwares.
Practical usage example
Let's say you need to track all process launches via the sys_execve call on a test Android device, but you don't want to rebuild the kernel from vendor source code.
Previously, you would have had to build a full kernel loadable module (LKM), deal with digital signature verification issues, and the absence of a module loader in the kernel.
With KernelPatch, the process looks different:
- You write a small C code with the interception logic.
- Compile it into KPM format.
- Feed the KernelPatch utility your
boot.imgand the compiled KPM module. - Get a modified
boot.imgand flash it via fastboot.
After the device boots, every call to execve first goes through your handler.
The project's README is quite laconic, and all the detailed documentation will have to be studied from the source code and files in the doc/ folder. Nevertheless, the project is actively developing and has already gathered over 14,000 stars on GitHub.
By the way, if you don't need the framework itself for experiments, but rather a ready-made root for Android based on it, the author recommends looking at his adjacent project — APatch. This is a higher-level alternative to Magisk and KernelSU, using KernelPatch under the hood.
For security researchers and ARM64 developers, KernelPatch is a great find, saving dozens of hours when analyzing closed-source kernels.
Related projects