5.7 KiB
The process of kernel modules compilation depends heavily on accessibility of header files provided by kernel. There are exist no guarantees on ABI compatibility even between minor kernel versions. This is deliberate design choice made by kernel developers. Kernel module compilation depends on kernel headers.
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 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 Linux kernel module. And embed it with ELF symbols collected from some existing kernel module (see /vendor/lib/modules/*.ko
). In such a way, that the LKM loader would be able to recognise and resolve all necessary dependencies and definitions.
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 really on compiler keyword __attribute__
for this.
// 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
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 collection of 'TAG=VALUE' entries separated by NULL character '\0'. The most important part is vermagic
, since loader is using it do identify which kernel headers were used for module compilation.
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
objdump -h $KMODULE | grep .gnu.linkonce.this_module
.gnu.linkonce.this_module 00000340 0000000000000000 0000000000000000 ....
^^^^^^^^ section size
name offset
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
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 encoded function signature
- a ASCII encoded function name
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 ................
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:
- Use a C-style function declaration in it's source code
int printk(const char *fmt, ...);
- Add another entry to the
__versions
section
Misc commands
- Module loading
insmod module_name.ko
- List loaded modules
lsmod
- Logs
dmesg | grep insmod
- Module unloading
rmmod MODULE_NAME # from .gnu.linkonce.this_module section