Title: Dynamic kernel patching
1Dynamic kernel patching
- How you could add your own system-calls to Linux
without editing and recompiling the kernel
2System calls
- System Calls are the basic OS mechanism for
providing privileged kernel services to
application programs (e.g., fork(), clone(),
execve(), read(), write(), signal(), getpid(),
waitpid(), gettimeofday(), setitimer(), etc.) - Linux implements over 300 system calls
- To understand how system calls work, we can try
creating one of our own design
3Open Source philosophy
- Linux source-code is publicly available
- In principle, anyone could edit the sources to
add their own new functions into Linux - In practice, it is inconvenient to do this
- The steps needed involve reconfiguring,
recompiling, and reinstalling your kernel - For novices these steps are treacherous!
- Any error risks data-loss and down-time
4Alternative to edit/recompile
- Linux modules offer an alternative method for
modifying the OS kernels functionality - Its safer -- and vastly more convenient since
error-recovery only needs a reboot, and minimal
system knowledge suffices - The main hurdle to be overcome concerns the issue
of linking module code to some non-exported
Linux kernel data-structures
5Invoking kernel services
user-mode (restricted privileges)
kernel-mode (unrestricted privileges)
application program
installable module
ret
call
call
ret
Linux kernel
standard runtime libraries
int 0x80
iret
6The system-call jump-table
- There are approximately 300 system-calls
- Any specific system-call is selected by its
ID-number (its placed into register eax) - It would be inefficient to use if-else tests or
even a switch-statement to transfer to the
service-routines entry-point - Instead an array of function-pointers is directly
accessed (using the ID-number) - This array is named sys_call_table
7Assembly language (.data)
- .section .data
- sys_call_table
- .long sys_restart_syscall
- .long sys_exit
- .long sys_fork
- .long sys_read
- .long sys_write
- // etc (from arch/i386/kernel/entry.S)
8The jump-table idea
sys_call_table
sys_restart_syscall
.section .text
0 1 2 3 4 5 6 7 8
sys_exit
sys_fork
sys_read
sys_write
sys_open
sys_close
etc
9Assembly language (.text)
- .section .text
- system_call
- // copy parameters from registers onto stack
- call sys_call_table(, eax, 4)
- jmp ret_from_sys_call
- ret_from_sys_call
- // perform rescheduling and signal-handling
- iret // return to caller (in user-mode)
10Changing the jump-table
- To install our own system-call function, we just
need to change an entry in the Linux
sys_call_table array, so it points to our own
module function, but save the former entry
somewhere (so we can restore it if we remove our
module from the kernel) - But we first need to find sys_call_table --
and there are two easy ways to do that
11Finding the jump-table
- Older versions of Linux (prior to 2.4.18) used to
export the sys_call_table as a global
symbol, but current versions keep this tables
address private (for security) - But often during kernel-installation there is a
System.map file that gets put into the /boot
directory and assuming it matches your compiled
kernel it holds the kernel address for the
sys_call_table array
12Using uname and grep
- You can use the uname command to find out which
kernel-version is running - uname -r
- Then you can use the grep command to find
sys_call_table in your System.map file, like
this - grep sys_call_table /boot/System.map-2.6.22.5c
slabs
13The vmlinux file
- Your compiled kernel (uncompressed) is left in
the /usr/src/linux directory - It is an ELF-format (executable) file
- It contains .text and .data sections
- You can examine your vmlinux kernel with the
objdump system-utility - You can pipe the output through the grep
utility to locate the sys_call_table symbol
14 Executable versus Linkable
ELF Header
ELF Header
Program-Header Table (optional)
Program-Header Table
Segment 1 Data
Section 1 Data
Section 2 Data
Segment 2 Data
Section 3 Data
Segment 3 Data
Section n Data
Segment n Data
Section-Header Table (optional)
Section-Header Table
Linkable File
Executable File
15Where is sys_call_table ?
- This is how you use objdump and grep to find
the sys_call_table address - cd /usr/src/linux
- objdump t vmlinux grep sys_call_table
16Exporting sys_call_table
- Once you know the address of your kernels
sys_call_table, you can write a module to
export that address to other modules, e.g. - // declare global variable
- unsigned long sys_call_table
- EXPORT_SYMBOL(sys_call_table)
- int init_module( void)
-
- sys_call_table (unsigned long )0xC0251500
- return 0
17Avoid hard-coded constant
- You probably dont want to hard code the
sys_call_tables value in your module if you
ever recompile your kernel, or use a differently
configured kernel, youd have to remember to edit
your module and then recompile it or risk a
corrupted system! - Theres a way to suply the required value as a
module-parameter during insmod
18Module paramerers
- char svctable // declare global variable
- module_param( svctable, charp, 0444 )
- // Then you install your module like this
- /sbin/insmod myexport.ko svctablec0251500
- // Linux will assign the address of your input
string c0251500 to the svctable pointer
19simple_strtoul()
- There is a kernel function you can use, in your
init_module() function, that will convert a
string of hexadecimal digits into an unsigned
long - int init_module( void )
-
- unsigned long myval
- myval simple_strtoul( svctable, NULL, 16 )
- sys_call_table (unsigned long )myval
- return 0
-
20Shell scripts
- Its inconvenient and risks typing errors if
you must manually search vmlinux and then type
in the sys_call_tables address every time you
want to install your module - Fortunately this sequence of steps can be readily
automated by using a shell-script - We have created an example myscript
21shell-script format
- First line !/bin/sh
- Some assignment-statements
- version(uname r)
- mapfile/boot/System.map-version
- Some commands (useful while debugging)
- echo version
- echo mapfile
22The cut command
- You can use the cut operation on a line of text
to remove the parts you dont want - An output-line from the grep program can be
piped in as a input-line to cut - You supply a command-line argument to the cut
program, to tell it which parts of the
character-array you wish to retain - For example cut c0-8
- Only characters 0 through 8 will be retained
23Finishing up
- Our myscript concludes by executing the command
which installs our myexport.o module into the
kernel, and automatically supplies the required
module-parameter - If your /boot directory doesnt happen to have
the System.map file in it, you can extract the
sys_call_table address from the uncompressed
vmlinux kernel-binary
24The objdump program
- The vmlinux file contains a Symbol-Table
section that includes sys_call_table - You can display that Symbol-Table using the
objdump command with the t flag objdump t
/usr/src/linux/vmlinux - You can pipe the output into grep to find the
sys_call_table symbol-value - You can use cut to isolate the address
25Which entry can we change?
- We would not want to risk disrupting the normal
Linux behavior through unintended alterations of
some vital system-service - But a few entries in sys_call_table are no
longer being used by the newer kernels - If documented as being obsolete it would be
reasonably safe for us to reuse an array-entry
for our own purposes - For example system-call 17 is obsolete
26newcall.c
- We created this module to demonstrate the
dynamic kernel patching technique - It installs a function for system-call 17
- This function increments the value stored in a
variable of type int whose address is supplied
as a function-argument - We wrote the try17.cpp demo to test it!
27Recently an extra obstacle
- Some recent versions of the Linux kernel
(including ours in the classroom and labs) have
placed the sys_call_table (as the default
configuration-option) in read-only memory
within kernel-space, despite the already existing
protections of ring 0 - What this achieves is creation of an added
obstacle to alterations by privileged-code
28 page-frame attributes
virtual memory address of our sys_call_table
array
1100 0000 00 10 0101 0001 0101 0000 0000
0xC0251500
sys_ call_ table
2 1 0
P
R / W
S / U
frame attributes
CR3
Page-Frame
Page-Directory
Page-Tables
We cannot modify entries in sys_call_table
unless its page-frame is writable
29Tweak page-frame attributes
- Our newcall.c module needs to be sure it can
modify entry 17 in sys_call_table - So it locates the page-table entry for the
page-frame containing sys_call_table and sets
its writable bit to be TRUE - But it preserves the previous value of that
entry, so it can be restored if we remove our
newcall.ko object from the kernel
30In-class exercise 1
- Write a kernel module (named unused.c) which
will create a pseudo-file that reports how many
unimplemented system-calls are still available.
The total number of locations in the
sys_call_table array is given by a defined
constant NR_syscalls so you can just search the
array to count how many entries match
sys_ni_syscall (its the value found initially
in location 17)