This is my note on basic android kernel development, covering the following aspects:
- Building the kernel from source
- Flashing a custom kernel to Pixel 6
- Basic development settings for android linux kernel
- Adding custom syscalls
- Writing loadable kernel modules
- Rust module on Android [TBD]
- KGDB through UART [TBD]
The source code of this post could be found at here.
0x00 Environment Preparation
Make sure the Android SDK
and google’s repo
tool are installed and available in PATH
:
0x01 Build the kernel from source
Fetch the kernel repository
- Find the
BRANCH
corresponding to the target device here. For example,android-gs-raviole-5.10-android13-qpr3
for Pixel 6. - Use the
repo
tool to fetch the source code and required tools/firmware for the target device.
Build
Run the following command to build the kernel.
If there is no error we will get the packed images under /out/BRANCH/dist
(the vmlinux
and other binaries are also available in this folder.). For example:
Note: in my laptop (latest Arch linux in 2023-12-31), the source code of branch will not compile successfully when using
libc 2.38
. An older libc solved this issue.
0x02 Flashing the custom kernel to the device
We should get an OEM-unlocked device and get it rooted. There are many tutorials on the XDA forum so I omit it here.
Before you start, make sure you back up all the data on your phone.
- Connnect Pixel 6 to the computer with a USB cable.
- Run
adb reboot bootloader
to restart the phone and enter the bootloader mode. - If we only made modifactions to the kernel, run the following command to flash the boot.img:
If we want to replace everything:
- Reboot into
Recovery mode
(for Pixel 6, pressing thevol +
button together with thepower
button). Clear all the data in the recovery mode. - Reboot and it’s done. We may have to re-root the phone since we replaced the patched
boot.img
with a new one, if we did not modify the kernel to give us root power.
0x03 Modify the kernel for fun and profit
Build the kernel for fuzzing
Just like Linux, enable the KCOV
option in android-kernel/private/gs-google/arch/arm64/configs/gki_defconfig
Defeat the anti-debug detection
A common method to detect whether the process is being debugged is to check the /proc/self/status
. We can just modify the source code of the proc fs
to bypass it, for example, masking the TracePid
:
0x04 Android kernel source auditing and development
Env setting
For me the kernel source is too complex to read in a pure text editor (yeah I’m
stupidlazy).
Follow my gist on turning vscode to an IDE for Linux kernel, but replace the command to specify the out
path explicitly:
and then clangd
will give us a comfortable development experience(auto-completion, macro parsing, etc).
Adding custom syscall
This is exactly same as the linux kernel. Some references:
– https://www.kernel.org/doc/html/v4.10/process/adding-syscalls.html
– https://redirect.cs.umbc.edu/courses/undergraduate/421/spring21/docs/project0.html
– https://android.blogs.rice.edu/2017/05/16/adding-a-system-call-to-aarch64-linux/
For convenience I omit the CONFIG
adding step and just integrat the new syscall directly:
- Adding the
sys_??
prototype ininclude/linux/syscall.h
:
- Adding a
syscall table entry
ininclude/uapi/asm-generic/unistd.h
. Modify the upper bound of syscall number__NR_syscalls
if necessary.
- Add the source code for the syscall, for example wrting a
mysyscall.c
underkernel
folder. Note that the linux kernel useC99
standard:
- Add the object
mysyscall.o
underobj-y
inkernel/Makefile
. - Rebuild the kernel.
Full patch:
Mofidied files:
Test the syscall with a binary program
We could test the syscall by a user mode ELF using NDK, which can be easily invoked from the adb as a regular linux binary.
Source:
Android.mk:
Application.mk:
Build:
– Add NDK folder to your $PATH
– Run ndk-build
in this directory
– The output locates in libs/
Run:
Writing loadable kernel modules (LKM)
Required files
Create a folder for the custom LKM, containing the LKM’s source code .c
, a Makefile
and a Kconfig
, for example:
Kconfig:
m
means compiled as a loadable kernel module.
Makefile:
Test code: lkm_test.c
Add the LKM to the building system
- Add the source path to
drivers/Kconfig
. - Add the folder path to
drivers/Makefile
.
Build the LKM
Rebuild the kernel image, and lkm_test.ko
could be found at the same folder of boot.img
(see the above kernel buiding section).
Sign
According the docs in AOSP, LKM should be signed. However, IDK why the LKMs compiled together with the kernel by build/build.sh
could be installed successfully without siging.
I would appreciate it if someone could add clarification.
On the latest branch of Pixel kernel (android-gs-raviole-5.10-android14-qpr3 for Pixel 6), google’s doc indicates that we should set
BUILD_AOSP_KERNEL=1
and use./build_slider.sh
to build the Pixel kernel. But when I try it, I can’t find boot.img. I find a patch that change the boot.img logic: c155169d34d56df62fb7ec1ce8b13af5cdec186a. I don’t understand the purpose of this patch, which contradicts the doc. Now we have to editbuild.config.gs101
file and revert this patch.