commit d45ceace5d6ea114d9fab0dca1e66d08b64661ba Author: Ciro Santilli Date: Sat Jul 30 09:38:06 2016 +0100 0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32d3ca5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.cmd +*.ko +*.mod.c +*.o +*.out +*.tmp +.tmp_versions +Module.symvers +modules.order diff --git a/README.md b/README.md new file mode 100644 index 0000000..4cb330e --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Linux Kernel Module Cheat + +1. [Introduction](introduction.md) +1. [Build](build.md) +1. [kmod](kmod.md) +1. Examples + 1. [Host](host/) diff --git a/build.md b/build.md new file mode 100644 index 0000000..8943a3d --- /dev/null +++ b/build.md @@ -0,0 +1,15 @@ +# Build + +The module building system. + +Kernel modules are built using a makefile located at: + + /lib/modules/$(uname -r)/build + +## includes + +Header files come from the same directory as the makefile: `/lib/modules/$(uname -r)/build`. + +TODO how is that different from: `/usr/src/linux-headers-$(uname -r)/` ? + +Those come directly from the kernel source tree. diff --git a/host/Makefile b/host/Makefile new file mode 100644 index 0000000..7697721 --- /dev/null +++ b/host/Makefile @@ -0,0 +1,33 @@ +OUT_EXT := .ko +OBJ_EXT := .o +RUN := hello +RUN_EXT := $(RUN)$(OUT_EXT) + +obj-m += hello.o +ccflags-y := -Wno-declaration-after-statement -std=gnu99 + +.PHONY: clean ins log rm run my-ins + +all: $(RUN_EXT) ins_rm_mod.out + +hello.ko: hello.c + make -C /lib/modules/$(shell uname -r)/build M="$(PWD)" modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M="$(PWD)" clean + rm -f *.out + +ins: all + sudo insmod '$(RUN_EXT)' + +log: + dmesg + +rm: + if lsmod | grep -Eq '^$(RUN) '; then sudo rmmod '$(RUN_EXT)'; fi + +ins_rm_mod.out: ins_rm_mod.c + gcc -Wall -std=gnu99 -o '$@' '$<' + +ins_rm_run: ins_rm_mod.out $(RUN_EXT) + sudo ./ins_rm_mod.out diff --git a/host/README.md b/host/README.md new file mode 100644 index 0000000..53b2c3f --- /dev/null +++ b/host/README.md @@ -0,0 +1,34 @@ +# Host + +Simple things that can be demonstrated by inserting a module into the currently running host. Tested on Ubuntu 16.04. + +1. [hello](hello.c) +1. [ins_rm_mod.c](ins_rm_mod.c) + +## Rationale + +This method easier to setup, but it is not recommended for development, as: + +- it may break your system. +- you can't control which kernel version to use + +Use VMs instead. + +## Usage + +We only use it for super simple examples. + +Build, insert and remove a hello world module: + + make ins + make rm + make log + +The last lines of the log should contain: + + init_module + cleanup_module + +Insert and remove a module from a C program: + + make ins_rm_run diff --git a/host/hello.c b/host/hello.c new file mode 100644 index 0000000..716c628 --- /dev/null +++ b/host/hello.c @@ -0,0 +1,13 @@ +#include +#include + +int init_module(void) +{ + printk(KERN_INFO "init_module\n"); + return 0; +} + +void cleanup_module(void) +{ + printk(KERN_INFO "cleanup_module\n"); +} diff --git a/host/ins_rm_mod.c b/host/ins_rm_mod.c new file mode 100644 index 0000000..5ab3c6a --- /dev/null +++ b/host/ins_rm_mod.c @@ -0,0 +1,36 @@ +/* +http://stackoverflow.com/questions/5947286/how-can-linux-kernel-modules-be-loaded-from-c-code/38606527#38606527 +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts) +#define delete_module(name, flags) syscall(__NR_delete_module, name, flags) + +int main(void) { + int fd = open("hello.ko", O_RDONLY); + struct stat st; + fstat(fd, &st); + size_t image_size = st.st_size; + void *image = malloc(image_size); + read(fd, image, image_size); + close(fd); + if (init_module(image, image_size, "") != 0) { + perror("init_module"); + return EXIT_FAILURE; + } + free(image); + if (delete_module("hello", O_NONBLOCK) != 0) { + perror("delete_modul"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/introduction.md b/introduction.md new file mode 100644 index 0000000..22dab89 --- /dev/null +++ b/introduction.md @@ -0,0 +1,66 @@ +# Introduction + +There are things which are hard to do from regular user programs such as directly talking to hardware. + +Some operations can be done via system calls, but if you want flexibility and speed, using the kernel ring is fundamental + +However: + +- it would be very complicated to recompile the kernel and reboot every time you make some modification +- the kernel would be huge if it were to support all possible hardware + +Modules overcome those two problems exactly because they can be loaded into the kernel *while it is running* and use symbols that the kernel chooses to export TODO which + +It then runs in the same address space as the kernel and with the same permissions as the kernel (basically do anything) + +Compiled modules are special object files that have a `.ko` extension instead of `.o` they also contain module specific metadata + +Device drivers (programs that enables the computer to talk to hardware) are one specific type of kernel modules + +Two devices can map to the same hardware! + +## Stable kernel interface + +Kernel modules can use any internal interface of the kernel, although some are more visible than others. + +But there is no stable kernel API for modules: if you don't add your driver to the kernel tree, it can break any time: + +## Configuration files + +If file it gets read, if dir, all files in dir get read: + + sudo ls /etc/modprobe.d + sudo ls /etc/modprobe.conf + +Modules loaded at boot: + + sudo cat /etc/modules + +## Hardware communication + +Talking to hardware always comes down to writing bytes in specific registers at a given memory addresses. + +Some processors implement a single address space for memory and other hardware devices, and others do not. + +However, since x86 is the most popular and it separates address spaces, every architecture must at least mimic this separation. + +On x86, the following specialized instructions exist for port IO: + +- `IN`: Read from a port +- `OUT`: Write to a port +- `INS/INSB`: Input string from port/Input byte string from port +- `INS/INSW`: Input string from port/Input word string from port +- `INS/INSD`: Input string from port/Input `doubleword` string from port +- `OUTS/OUTSB`: Output string to port/Output byte string to port +- `OUTS/OUTSW`: Output string to port/Output word string to port +- `OUTS/OUTSD`: Output string to port/Output `doubleword` string to port + +However, you should avoid using those instructions directly in your device driver code since Linux functions abstract over multiple architectures (when possible) making your code more portable. + +Those instructions cannot be used from an user space program since the kernel prevents those from accessing hardware directly. + +The memory space for non-memory locations is called I/O ports or I/O space. + +To use a port, you must first reserve it. To see who reserved what: + + sudo cat /proc/ioports diff --git a/kmod.md b/kmod.md new file mode 100644 index 0000000..b719cc8 --- /dev/null +++ b/kmod.md @@ -0,0 +1,105 @@ +# kmod + +Implements `lsmod`, `insmod`, `rmmod`, and other tools. + +The other tools are just symlinks to it. + +## module-init-tools + +Name of a predecessor set of tools. + +## package version + +From any of the commands, `--version`: + + modinfo --version + +Package that provides utilities + +## lsmod + +List loaded kernel modules. + +Info is taken from `/proc/modules` + + lsmod + +Sample output: + + cfg80211 175574 2 rtlwifi,mac80211 + ^^^^^^^^ ^^^^^^ ^ ^^^^^^^,^^^^^^^^ + 1 2 3 4 5 + +1. Name. + +2. Size. + +3. Number of running instances. + + If negative, TODO + +4. Depends on 1. + +5. Depends on 2. + +To get more info: + + cat /proc/modules + +Also contains two more columns: + +- status: Live, Loading or Unloading +- memory offset: 0x129b0000 + +## modinfo + +Get info about a module by filename or by module name: + + modinfo ./a.ko + modinfo a + +TODO must take a `.ko` file? + +## insmod + + sudo insmod hello.ko + +Loads the module. + +Does not check for dependencies. + +## rmmod + +Remove a module. Takes either the module name or the `.ko` file: + + sudo rmmod hello + sudo rmmod ./hello.ko + +## modprobe + +List available modules relative path to `/lib/modules/VERSION/`: + + sudo modprobe -l + +Load the module: + + sudo modprobe $m + +Checks for dependencies. + +Load module under different name to avoid conflicts: + + sudo modprobe vmhgfs -o vm_hgfs + +Remove module: + + sudo modprobe -r $m + +Check if dependencies are OK: + + sudo depmod -a + +Get info about given `.ko` module file: + + m=a + sudo rmmod $m