initial commit
This commit is contained in:
parent
6c6af00e2a
commit
859ca1d5a2
22
Makefile
Normal file
22
Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
TOOLCHAIN=$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64
|
||||
TARGET=aarch64-linux-android
|
||||
API=30
|
||||
AR=$(TOOLCHAIN)/bin/llvm-ar
|
||||
CC=$(TOOLCHAIN)/bin/$(TARGET)$(API)-clang
|
||||
AS=$(CC)
|
||||
CXX=$(TOOLCHAIN)/bin/$(TARGET)$(API)-clang++
|
||||
LD=$(TOOLCHAIN)/bin/ld.lld
|
||||
RANLIB=$(TOOLCHAIN)/bin/llvm-ranlib
|
||||
STRIP=$(TOOLCHAIN)/bin/llvm-strip
|
||||
|
||||
.PHONY: install clean
|
||||
|
||||
all: bmod.o
|
||||
$(CC) -fno-pic -flto=thin -fvisibility=default -fsanitize=cfi -fsanitize-cfi-cross-dso -fno-common -c bmod.c
|
||||
$(LD) -r -o bmod.ko bmod.o
|
||||
|
||||
install:
|
||||
adb push bmod.ko /data/local/tmp
|
||||
|
||||
clean:
|
||||
rm bmod.o bmod.ko
|
146
README.md
146
README.md
@ -1,3 +1,145 @@
|
||||
# standalone-kernel-module
|
||||
|
||||
Compile kernel module without kernel headers
|
||||
The process of [kernel modules compilation](https://www.kernel.org/doc/html/v5.6/kbuild/modules.html) 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](https://github.com/torvalds/linux/blob/v4.9/Documentation/stable_api_nonsense.txt) 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?
|
||||
|
||||
This article focuses on Android, but most of information and techniques discussed here can be easily applied to 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 loader would be able to recognise and resolve all necessary dependencies and definitions.
|
||||
|
||||
## .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 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.
|
||||
|
||||
```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
|
||||
|
||||
# Misc
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
|
132
bmod.c
Normal file
132
bmod.c
Normal file
@ -0,0 +1,132 @@
|
||||
/************************/
|
||||
/** **/
|
||||
/** PLT section **/
|
||||
/** **/
|
||||
/************************/
|
||||
// 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 irrelevant
|
||||
|
||||
|
||||
/************************/
|
||||
/** **/
|
||||
/** .modinfo **/
|
||||
/** **/
|
||||
/************************/
|
||||
// objdump -s -j .modinfo $KMODULE
|
||||
#define AUTHOR "someone"
|
||||
#define DESCRIPTION "Simple =^.^= kernel module"
|
||||
#define NAME "bmod"
|
||||
#define LICENSE "GPL"
|
||||
#define VERMAGIC "4.14.186-perf-00288-g7df34d2ae6db SMP preempt mod_unload modversions aarch64"
|
||||
|
||||
__attribute__((section(".modinfo")))
|
||||
char modinfo_strings[] =
|
||||
"author=" AUTHOR "\0"
|
||||
"description=" DESCRIPTION "\0"
|
||||
"license=" LICENSE "\0"
|
||||
"vermagic=" VERMAGIC "\0"
|
||||
"name=" NAME "\0"
|
||||
;
|
||||
|
||||
|
||||
/*******************************/
|
||||
/** **/
|
||||
/** .gnu.linkonce.this_module **/
|
||||
/** **/
|
||||
/*******************************/
|
||||
// 1 - section size
|
||||
// objdump -h $KMODULE | grep .gnu.linkonce.this_module
|
||||
// .gnu.linkonce.this_module 00000340 0000000000000000 0000000000000000 00109800 2**6
|
||||
// ^^^^^^^^ section size
|
||||
|
||||
// 2 - section content
|
||||
// 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..... // <<-- NAME offset 0x18
|
||||
// 0020 00000000 00000000 00000000 00000000 ................
|
||||
|
||||
// 3 - entry points offsets
|
||||
// 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
|
||||
|
||||
#define L_ONCE_SIZE 0x340
|
||||
#define L_ONCE_OFFSET_NAME 0x18
|
||||
#define L_ONCE_OFFSET_INIT 0x150
|
||||
#define L_ONCE_OFFSET_CLEANUP 0x310
|
||||
|
||||
int init_module(void);
|
||||
void cleanup_module(void);
|
||||
|
||||
__attribute__((section(".gnu.linkonce.this_module"), aligned(8)))
|
||||
struct module {
|
||||
char __pad0[L_ONCE_OFFSET_NAME];
|
||||
char name[sizeof(NAME)];
|
||||
|
||||
char __pad1[L_ONCE_OFFSET_INIT - L_ONCE_OFFSET_NAME - sizeof(NAME)];
|
||||
int (*init_module)(void);
|
||||
|
||||
char __pad2[L_ONCE_OFFSET_CLEANUP - L_ONCE_OFFSET_INIT - sizeof(void*)];
|
||||
void (*cleanup_module)(void);
|
||||
|
||||
char __pad3[L_ONCE_SIZE - L_ONCE_OFFSET_CLEANUP - sizeof(void*)];
|
||||
} __attribute__((packed))
|
||||
__this_module = {
|
||||
.name = NAME,
|
||||
.init_module = &init_module,
|
||||
.cleanup_module = &cleanup_module,
|
||||
};
|
||||
|
||||
|
||||
/*******************************/
|
||||
/** **/
|
||||
/** __versions **/
|
||||
/** **/
|
||||
/*******************************/
|
||||
// objdump -s $KMODULE -j '__versions' #dump binary
|
||||
// modprobe --dump-modversions $KMODULE #dump readable
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The Module
|
||||
*/
|
||||
// load: insmod module_name.ko
|
||||
// logs: dmesg | grep insmod
|
||||
// unload: rmmod MODULE_NAME # from .gnu.linkonce.this_module section
|
||||
|
||||
int printk(const char *fmt, ...);
|
||||
#define MTAG "["NAME"] "
|
||||
|
||||
int init_module(void) {
|
||||
printk( MTAG"Hello, world!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cleanup_module(void) {
|
||||
printk( MTAG"Goodbye, world!\n");
|
||||
}
|
Loading…
Reference in New Issue
Block a user