standalone-kernel-module/README.md

161 lines
6.8 KiB
Markdown
Raw Normal View History

2024-04-17 14:26:30 +02:00
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.
2024-04-17 12:10:24 +02:00
> But what to do if, for the variety of reasons, for the kernel you are interest in, headers are unavailable?
2024-04-17 14:26:30 +02:00
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.
2024-04-17 12:10:24 +02:00
# ELF symbols stealing
2024-04-17 14:26:30 +02:00
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.
2024-04-17 12:34:47 +02:00
## PLT section
2024-04-17 14:26:30 +02:00
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.
2024-04-17 12:34:47 +02:00
```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
```
2024-04-17 12:10:24 +02:00
## .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
```
2024-04-17 14:26:30 +02: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.
2024-04-17 12:10:24 +02:00
```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
2024-04-17 14:26:30 +02:00
```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,
};
```
2024-04-17 12:10:24 +02:00
2024-04-17 12:13:15 +02:00
# Misc commands
2024-04-17 12:10:24 +02:00
2024-06-23 23:01:13 +02:00
```shell
insmod module_name.ko # Module loading
2024-04-17 12:10:24 +02:00
2024-06-23 23:01:13 +02:00
rmmod MODULE_NAME # Module unload
# MODULE_NAME can be found in `.gnu.linkonce.this_module` section
2024-04-17 12:10:24 +02:00
2024-06-23 23:01:13 +02:00
lsmod # List loaded modules
2024-04-17 12:10:24 +02:00
2024-06-23 23:01:13 +02:00
dmesg | grep insmod # See logs
2024-04-17 12:10:24 +02:00
```