How-To: Build a Custom Kernel With Kprobes for Android

Before you continue reading, you might want to check whether the kernel on your device already supports kprobes (even though I haven’t seen any stock or popular custom kernel, such as franco.Kernel, which does):

$ adb shell cat /proc/kallsyms | grep register_kprobe

If you receive some output (e.g., 00000000 T register_kprobe), then the answer is yes, your kernel has kprobes enabled; and there is no need to read any further.

Building kernel with kprobes (and boot image) for Nexus 5, Android 5.1.1

I tested the following recipe on Ubuntu 14.04. It should work in a very similar way also for any other (Nexus) device and Android version–assuming you have access to the right kernel sources.

Note: I assume in the following that all commands are executed in the same working directory.

1) Getting the right kernel sources

For all Google Nexus devices there is a git repository available that contains the matching kernel sources. For the Nexus 4, Nexus 5, Nexus 6:

$ git clone https://android.googlesource.com/kernel/msm.git

More specifically, you need to checkout the commit that contains the sources that were used for the kernel running on your phone at hand:

$ adb shell cat /proc/version | grep -e "[0-9.]*-g[a-z0-9]*" -o

The output is 3.4.0-gbebb36b (you find the same info on your phone under Settings -> About phone -> Kernel version). The suffix bebb36b tells you, which commit was used:

$ cd msm; git checkout bebb36b

2) Create kernel configuration with kprobes

First, generate the default configuration file .config:

$ cd msm; ARCH=arm make hammerhead_defconfig

Then, append the following lines to .config (order does not matter):

CONFIG_KPROBES=y
CONFIG_KPROBES_SANITY_TEST=y
# CONFIG_KPROBE_EVENT is not set
# CONFIG_ARM_KPROBES_TEST is not set
CONFIG_NET_TCPPROBE=y # seems necessary otherwise kernel does not boot
CONFIG_MODULES=y # kprobes requires module support and makes no sense without
CONFIG_MODULE_FORCE_LOAD=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y # possible to use modules compiled for different kernels
# CONFIG_MODULE_SRCVERSION_ALL is not set

3) Compile kernel

There a several different toolchains for cross-compiling the kernel, but not all are working for each Android version. For the Nexus 5 and Android >5.0, I used the arm-eabi- and not the arm-linux-android-eabi:

$ git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7

Now, you can cross-compile the kernel:

$ cd msm; ARCH=arm CROSS_COMPILE=../arm-eabi-4.7/bin/arm-eabi- make

You might get asked further questions (which I usually answer all with no) regarding missing configuration options in .config. If later the kernel doesn’t boot, you need to go back to this step and adapt the answers. Good luck!

4) Download boot image

You have by now already baked the new kernel. Congratulations! But to boot your device with it, it needs to be wrapped into a boot image (kernel + ramdisk).

You can get the matching boot image for your device and Android version by downloading the original stock boot image or, which I recommend, directly pull it from your phone (using dd):

$ adb shell su -c "ls -al /dev/block/platform/msm_sdcc.1/by-name/boot"
lrwxrwxrwx root     root              1970-09-29 07:33 boot -> /dev/block/mmcblk0p19

$ adb shell su -c "dd if=/dev/block/mmcblk0p19 of=/sdcard/my_nexus5_boot.img"
$ adb pull /sdcard/my_nexus5_boot.img

The green parts in the command above vary, depending on which device and Android version you use.

5) Unpack and repackage boot image with new kernel

You need the tools unmkbootimg (unpacking) and mkbootimg (packing):

$ git clone https://github.com/pbatard/bootimg-tools.git
$ cd bootimg-tools; make

Unpack the boot image:

$ ./bootimg-tools/mkbootimg/unmkbootimg -i my_nexus5_boot.img

If anything went wrong you receive the output error: not an Android boot image. Otherwise you end up with the two files kernel and ramdisk.cpio.gz in your directory.

Now repackage the new kernel ../msm/arch/arm/boot/zImage-dtb together with the ramdisk ramdisk.cpio.gz into a new boot image. If you are wondering, where the following quite long command comes from; unmkbootimg provides it as part of the output during unpacking.

$ ./bootimg-tools/mkbootimg/mkbootimg --base 0 --pagesize 2048 --kernel_offset 0x00008000 \ 
--ramdisk_offset 0x02900000 --second_offset 0x00f00000 --tags_offset 0x02700000 \
--cmdline 'console=ttyHSL0,115200,n8 androidboot.hardware=hammerhead user_debug=31 \ 
maxcpus=2 \ msm_watchdog_v2.enable=1' --kernel ../msm/arch/arm/boot/zImage-dtb \ 
--ramdisk ramdisk.cpio.gz -o my_nexus5_kprobes_boot.img

6) Finally, boot your device with the boot image that contains the new kernel

Enjoy booting your new boot image with kprobes, my_nexus5_kprobes_boot.img, How, I have described in another article.