A Standalone Linux Kernel Module

栏目: IT技术 · 发布时间: 4年前

内容简介:If you’ve ever compiled a module for the Linux Kernel you’ve probably seen (or assumed there’s) quite a lot of voodoo going on between the stages of runningAs a quick reminder of the black magic involved in the process, take a look at the following simples

If you’ve ever compiled a module for the Linux Kernel you’ve probably seen (or assumed there’s) quite a lot of voodoo going on between the stages of running make and insmod .

As a quick reminder of the black magic involved in the process, take a look at the following simplest of kernel modules, and it’s appropriate makefile:

A super simple module
It takes a while to decipher what’s going on

The makefile is in itself pretty obscure, but the make command is where the real fun occur:

A Standalone Linux Kernel Module
This is just the tip of the iceberg

Right of the start we see no “standard” compilation calls, involving directories mostly untouched by the everyday user. Runing make V=1 reveals a mountain of calls, some of them to non-compilation-related binaries and scripts. What’s going on?

The .ko compilation process may seem scary and complicated, but upon simple inspection, it’s not so hard to figure out exactly what’s going on.

In this post we’ll walk through the journey of compiling a kernel module, without using any kernel headers or complicated makefiles. Our goal would be to write a short, simple, and easy to understand .c file, that when compiled with gcc and a few minimal flags, outputs a valid .ko that can be insmod ed.

This can be achieved using several different approaches:

  • Reading the documentation.
  • Inspecting the standard compilation process.
  • Looking through the kernel sources, attempting to understand what the init_module syscall does (the one responsible for loading a .ko ).

But our method of choice will be a fourth one — The Blackbox Way : Tinkering with the loader, looking at valid modules, writing invalid ones, and attempting to do whatever seems to work until we finally succeed.

IMHO, blackboxing is the most useful and effective skill to have when researching unkown or uncharetd systems, as oftentimes none of former approaches is applicable, or is probably more time-consuming and exhausting. Also, it’s the most rewarding process, as you get quick and fun payoffs along the way, with little effort.

But first, we must understand what a kernel module actually is.

What $(make)s a Kernel Module?

The .ko suffix means “ kernel object ”, hinting at what a module really is — just an ELF object file . In fact, before kernel version 2.6 , kernel modules used the .o suffix, just like standard object files.

So what distinguishes between conventional .o files and .ko ones? Let’s try to load an “empty” object file into our kernel:

Loading an “empty” object file into the kernel fails

Luckily for us, the kernel is pretty descriptive when it comes to errors regarding modules. dmesg reveals that our empty object file is missing a section called .modinfo . Let’s add it to our module!

The simplest way to do that is by creating any variable, and telling gcc to store it in a section of our choice using __attribute__((section(<name>))) . See the gcc documentation for more info on attributes.

A Standalone Linux Kernel Module
`highlight` is like `cat` but with syntax highlighting!

The error changed! Looks like we’re missing some name field in our modinfo section. To learn how to properly set that field, let’s look at a “real” kernel module, from /lib/modules/$(uname -r)/ , dumping the contents of that section using objdump -s -j .modinfo :

A Standalone Linux Kernel Module

In the muggle world, use the handy ` modinfo ` command

Looks like it’s just made up of key=value strings, separated by null bytes. So let’s add the name field and see what happens:

A Standalone Linux Kernel Module
I solemnly swear that I am up to no good

It worked! The kernel successfully identified our module’s name as standalone !

This next error seems a bit obscure. With no useful tips from the kernel, it’s time to look again at a “real” kernel module. Upon examination of its sections, using either readelf -S or objdump -h , we find another candidate with a rather suspicious name, that probably has something to do with the loading process: [.rela].gnu.linkonce.this_module .

(The .rela part is just the relocation data for the values of that section)

On my Ubuntu 18.04, this section is of size 0x380 bytes, and has 2 relocatable symbols: init_module at offset 0x178 , and cleanup_module at offset 0x330 . Dumping the contents of the section ( objdump -s ) shows that it’s mostly zeroed out, except for the module name, at offset 0x18 .

This section is actually the C struct module from the Linux Kernel sources, include/linux/module.h , embedded as a section inside the ELF. It’s usually named __this_module , and during the loading process, the kernel loader initializes the other relevant fields in the struct.

But we’re not using any kernel header files. Luckily, we have all the information we need. Let’s add some init and exit functions, and the relevant section:

almost there!
Lumos Maxima!

(We’re using __attribute__((packed)) to force the compiler to not add any extra padding between the struct’s fields, so as to force our known offsets.)

Rather bafflingly, even though insmod failed, there’s no error log in dmesg . We can verify that the module isn’t in fact loaded using lsmod | grep standalone . So what’s going on?

Usually the kernel alerts us on any missing parameters, but apparantely on some kernel configurations, sometimes no message is printed for some missing fields. Let’s look back at a real kernel module. The only major difference between our module and a real one is the fields in the .modinfo section.

This is the part where we can start to copy the remaining fields one by one until we succeed or get a different error message. However, luckily for us, an enchanted oracle whispered in our ear at night that we should start by copying the .vermagic field first!

vermagic is short for “Version Magic”, a string used by the loader to sanity check that a module was indeed compiled for that kernel release. We can extract its content from a module compiled for the kernel, or alternatively, let the kernel spit it out for us!

Makefiles and Charms are my two favorite classes in Hogwarts
A Standalone Linux Kernel Module
Thank’s for the tip!

Changing the vermagic value one last time yields:

A Standalone Linux Kernel Module
It worked!

…And we’re done! We successfully loaded a module into the kernel, compiled without using any kernel header files!

To see the final standalone.c file in it’s entirety, visit https://github.com/0xEitan/standalone-ko .

The Demystification Of Complex Systems

Many people get overwhelmed when faced with a complex and often uninviting system. Be it some collosal project they’re getting acquainted with, an unknown embedded device they want to program, or the Linux Kernel in general — it’s always intimidating diving into the deep waters.

Even though we didn’t use pure blackboxing in this post, I find methods such as the one shown, to be extremely effective in such scenarios. It may not produce successful results like in the present case, but it can certainly always reveal important insights, and help to overcome the first obstacles when facing unknown code bases.

Nothing is too complex to understand, it just takes the right tool to do it.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

高效算法

高效算法

[法] Christoph Dürr、[法] Jill-Jênn Vie / 史世强 / 人民邮电出版社 / 2018-5 / 55.00元

本书旨在探讨如何优化算法效率,详细阐述了经典算法和特殊算法的实现、应用技巧和复杂度验证过程,内容由浅入深,能帮助读者快速掌握复杂度适当、正确率高的高效编程方法以及自检、自测技巧,是参加ACM/ICPC、Google Code Jam 等国际编程竞赛、备战编程考试、提高编程效率、优化编程方法的参考书目。一起来看看 《高效算法》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具