Kernel module [compilation](https://www.kernel.org/doc/html/v5.6/kbuild/modules.html) *depends* on kernel headers. This is a [deliberate design choice](https://github.com/torvalds/linux/blob/v4.9/Documentation/stable_api_nonsense.txt) made by kernel developers. As result, there is no even a slight guarantee of an ABI compatibility even between minor kernel versions. > But what to do if, for the variety of reasons, for the kernel you are interest in, headers are unavailable? The code in this repo gives an answer to this question. It focuses on Android, but most of the information and techniques discussed here can be easily applied to the generic Linux kernel as well. # ELF symbols stealing The main idea, is to use Android NDK to compile generic a Linux kernel module with pre-embed ELF symbols collected from some existing kernel module. See, for example, `/vendor/lib/modules/*.ko` for a possible donor. The idea is to provide just enough information, so that the LKM loader would be able to recognise and resolve all the necessary dependencies and default, and in the end to load our kernel agnostic module. ## PLT section This part is pretty straight forward, we simply have to define a few ELF symbols, that Android LKM is expecting to find within *normal* module. We are going to use `__attribute__` [compiler keyword](https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Variable-Attributes.html) for this. ```c // insmod error: module PLT section(s) missing __attribute__((section(".plt"))) char plt = 0; __attribute__((section(".init.plt"))) char initplt = 0; __attribute__((section(".text.ftrace_trampoline"))) char textftrace_trampoline = 0; // TODO: probably an overkill ``` ## .modinfo ```bash objdump -s -j .modinfo $KMODULE met.ko: file format elf64-little Contents of section .modinfo: 0000 7061726d 74797065 3d6d6574 5f6d696e parmtype=met_min 0010 6f723a69 6e740061 7574686f 723d4454 or:int.author=DT 0020 5f444d35 00646573 63726970 74696f6e _DM5.description 0030 3d4d4554 5f434f52 45006c69 63656e73 =MET_CORE.licens 0040 653d4750 4c007665 726d6167 69633d34 e=GPL.vermagic=4 0050 2e31342e 3138362d 70657266 2d303032 .14.186-perf-002 0060 38382d67 37646633 34643261 65366462 88-g7df34d2ae6db 0070 20534d50 20707265 656d7074 206d6f64 SMP preempt mod 0080 5f756e6c 6f616420 6d6f6476 65727369 _unload modversi 0090 6f6e7320 61617263 68363400 6e616d65 ons aarch64.name 00a0 3d6d6574 00646570 656e6473 3d007372 =met.depends=.sr 00b0 63766572 73696f6e 3d353333 42423745 cversion=533BB7E 00c0 35383636 45353246 36334239 41434342 5866E52F63B9ACCB 00d0 00 ``` This ELF section essentially is a collection of 'TAG=VALUE' entries separated by NULL character '\0'. The most important part is `vermagic`, since the loader is using it do identify which kernel headers were used for the module compilation and compares it against his own `vermagic` variable. They must be identical. ```bash cat /proc/version ``` ## .gnu.linkonce.this_module This ELF section contains information that the LKM loader used to identify: - name of the module - a pointer to the initialization procedure `module_init` - a pointer to the de-initialization procedure `module_cleanup` ### section size ```bash objdump -h $KMODULE | grep .gnu.linkonce.this_module .gnu.linkonce.this_module 00000340 0000000000000000 0000000000000000 .... ^^^^^^^^ section size ``` ### name offset ```bash objdump -s $KMODULE -j .gnu.linkonce.this_module Contents of section .gnu.linkonce.this_module: 0000 00000000 00000000 00000000 00000000 ................ 0010 00000000 00000000 6d657400 00000000 ........met..... ^^^^^^^^ offset 0x18 bytes ``` ### entry points ```bash readelf -a $KMODULE -W Relocation section '.rela.gnu.linkonce.this_module' at offset 0x109b40 contains 2 entries: <--- Offset ---> Info Type Symbol's Value Symbol's Name + Addend 0000000000000150 000006c500000101 R_AARCH64_ABS64 0000000000000000 init_module + 0 0000000000000310 000006ee00000101 R_AARCH64_ABS64 0000000000000000 cleanup_module + 0 ``` ## \_\_versions In its essence is a byte-array for declaring external dependencies required by the module: - first 4 bytes of [CRC](https://mjmwired.net/kernel/Documentation/kbuild/modules.txt#429) encoded function signature - a ASCII encoded function name ```bash objdump -s $KMODULE -j '__versions' #dump in binary form met.ko: file format elf64-little Contents of section __versions: 0000 e8d6fc68 00000000 6d6f6475 6c655f6c ...h....module_l 0010 61796f75 74000000 00000000 00000000 ayout........... 0020 00000000 00000000 00000000 00000000 ................ 0030 00000000 00000000 00000000 00000000 ................ 0040 6c9bdf85 00000000 73747273 65700000 l.......strsep.. 0050 00000000 00000000 00000000 00000000 ................ 0060 00000000 00000000 00000000 00000000 ................ 0070 00000000 00000000 00000000 00000000 ................ 0080 1ee414e9 00000000 73747263 70790000 ........strcpy.. 0090 00000000 00000000 00000000 00000000 ................ 00a0 00000000 00000000 00000000 00000000 ................ 00b0 00000000 00000000 00000000 00000000 ................ ``` ```bash modprobe --dump-modversions $KMODULE #dump in readable form ``` ### module_layout A default first entry of a section. Used by LKM loader to determine ABI of a module (the one used during it's compile time) ### printk() In order for module to use any externally declared symbol module must: 1. Use a C-style function declaration in it's source code ```c int printk(const char *fmt, ...); ``` 2. Add another entry to the `__versions` section ```c char __attribute__((section("__versions"))) ____versions[] = { /* module_layout */ // <<-- MUST BE PROVIDED 0xe8, 0xd6, 0xfc, 0x68, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* printk */ 0xa1, 0x58, 0x55, 0x98, 0x00, 0x00, 0x00, 0x00, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; ``` # Misc commands * Module loading ```bash insmod module_name.ko ``` * List loaded modules ```bash lsmod ``` * Logs ```bash dmesg | grep insmod ``` * Module unloading ```bash rmmod MODULE_NAME # from .gnu.linkonce.this_module section ```