Dynamic kernel patching
Could we implement a new Linux
system-call without modifying any
officially released kernel sources?
Upcoming Lab Project
• 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 around 200 system calls
• To understand how system calls work, we
want to try building one of our own (LAB5)
‘Open 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 the novice these steps are arduous!
• Any error risks data-loss and down-time
Alternative to static patching
• Linux modules offer an alternative method
for modifying the OS kernel’s functionality
• It’s 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
Invoking kernel services
user-mode
(restricted privileges)
kernel-mode
(unrestricted privileges)
application
program
installable module
call
call
ret
standard
runtime
libraries
ret
int 0x80
Linux kernel
iret
The system-call jump-table
• There are approximately 200 system-calls
• Any specific system-call is selected by its
ID-number (it’s placed into register %eax)
• It would be inefficient to use if-else tests or
even a switch-statement to transfer to the
service-routine’s entry-point
• Instead an array of function-pointers is
directly accessed (using the ID-number)
• This array is named ‘sys_call_table[]’
Assembly language (.data)
.section .data
sys_call_table:
.long
sys_ni_syscall
.long
sys_exit
.long
sys_fork
.long
sys_read
.long
sys_write
// …etc
Assembly 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)
Changing 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
Which 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’
Finding 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 table’s 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
Using ‘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.4.26
Exporting ‘sys_call_table’
• Once you know the address of your kernel’s
‘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;
int init_module( void)
{
sys_call_table = (unsigned long *)0xC025F580;
return 0;
}
Avoid hard-coded constant
• You probably don’t want to ‘hard code’ the
sys_call_table’s value in your module – if
you ever recompile your kernel, or use a
differently configured kernel, you’d have to
remember to edit your module and then
recompile it – or risk a corrupted system!
• There’s a way to suply the required value
as a module-parameter during ‘insmod’
Module paramerers
char *svctable;
// declare global variable
MODULE_PARM( svctable, “s” );
// Then you install your module like this:
$ /sbin/insmod myexport.o svctable=c025f580
// Linux will assign the address of your input
string “c025f580” to the ‘svctable’ pointer
simple_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;
}
Shell scripts
• It’s inconvenient – and risks typing errors –
if you must manually search ‘System.map’
and then type in sys_call_table[]’s 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’
shell-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
The ‘cut’ command
• You can use the ‘cut’ operation on a line of
text to remove the parts you don’t 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
Finishing 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 doesn’t happen to
have the ‘System.map’ file in it, you can
extract the ‘sys_call_table[]’ address from
the uncompressed ‘vmlinux’ kernel-binary
The ‘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
In-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’
(it’s the value found initially in location 17)
In-class exercise #2
• Another important “hidden” kernel object is
the Interrupt Descriptor Table
• It’s an array of ‘struct desc_struct’ entries
and it’s named ‘idt_table[]’
• Try modifying our ‘myexport.c’ demo so it
will export the address for ‘idt_table[]’
• Try modifying our ‘myscript’ so that it will
automate your module-installation steps
© Copyright 2026 Paperzz