Detecting 
Loadable Kernel Modules 
(LKM)
                  
 
 
      The purpose of 
this paper is cover LKM basics, detecting "trojaned" LKM's and figuring out 
which LKM is installed on your machine.
 
LKM
 
      What is a LKM? 
Loadable Kernel Modules (LKM) are files that contain dynamically loadable kernel 
components. LKM's are normally used to load device drivers and other hardware 
drivers. LKM's can be found on Linux, Solaris and BSD (Open, Free and Net). This 
paper will focus on Linux. 
      Linux comes with 
various tools to assist the system administrator in loading, listing and 
unloading of the kernel modules. Before we cover the tools, lets look at some 
important files and directories associated with Kernel Modules. The first 
directory we want to look at is /lib/modules/"kernel_version". Lets look and see 
what files we can find under 
/lib/modules."Kernel_version":
 
| block build cdrom fs ipv4 misc modules.dep modules.isapnpmap modules.pcimap modules.usbmap net pcmcia scsi usb video      
         | 
         
Table 1. /lib/modules 
listing
 Table 1 shows us both directories and 
files related to LKM's. The only ones listed that are not directories are 
modules.dep, modules.isapnpmap, modules.pcimap and mosules.usbmap. Those are 
actual files that list modules within the system (combined). Lets take a quick 
look at the net directory (This is 
not a complete listing, do to space):
| 3c59x.o 3c90x.o 82596.o 8390.o ac3200.o acenic.o arlan-proc.o | 
                        
Table 2. net directory  
There are 
a couple of important bits of information we can gather from this table. The 
first bit is, modules are listed as .o. Why? Because they are object files that 
contain the actual module itself. The second bit is, the listing is not 
complete. I did not have room to put all of the modules. 
      The second 
important directory/file we want to look at it /etc/conf.modules. Conf.modules 
is the configuration file that allows the system administrator to specify a 
variety of parameters that control the loading of the modules. The conf.modules 
file typically looks like this:
| alias parport_lowlevel 
      parport_pc     | 
 
 
 
 
                                
Table 
3. conf.modules file
                
The 
conf.modules file allows the administrator to assign alias to commonly used 
modules. Note: This file is not required. A system administrator can create 
his/her own configuration file. A system administrator can do that by using the 
modprobe –C command. 
      Since I mentioned 
modprobe earlier, lets briefly take a look at modprobe and the other tools 
available in Linux that load and unload modules. The first tool is modprobe. 
Modprobe can load single and multiple modules. It uses the modules.dep file to 
look up dependencies. Depmod creates a “Makefile” like dependency file. This 
file is called modules.dep.  The 
last three commands I will cover are lsmod, insmod and rmmod. lsmod provides the 
administrator a listing of all modules currently loaded in the kernel. This list 
can also be found at /proc/modules. This is helpful if you want to figure out 
what modules are currently running. You will see a little later that lsmod is 
not always a good tool to use for the detection of rootkit LKM’s. insmod loads 
modules(seems simple). Some of the “trojaned” LKM code I have seen uses insmod 
-f to load the “trojaned” module. rmmod is normally used to remove modules from 
the kernel. Normally rmmod will NOT remove any “trojaned” LKM. We will discuss 
details of that a little bit later. Hopefully, this section provided everybody 
the basics of Loadable Kernel Modules. If this was not detailed enough see http://www.kernel.org/LDP/.
 
 
LKM rootkits and kstat     
 
 
      Recently, there 
has been a lot of press about adore. Well, the worm adore not the LKM adore. So 
I decided to look into the LKM adore(everyone else has looked at the worm). BTW, 
if want to download adore go to http://packetstorm.securify.com/filedesc/adore-0.34.html.
      Adore is a Linux 
LKM rootkit. It is easy to install and only requires a few minor adjustments 
when configuring. Adore can be installed with a default configuration or the 
user can make changes to some of the code. The readme file recommends that the 
user change the settings for ELITE_CMD and for HIDDEN_PORT. When you run 
./configure, adore asks you for a password. This is for the backdoor port 
(hence, change the HIDDEN_PORT) If you want more information on the install you 
will have to download it. I ran the default installation, which consisted of 
running./configure and make. After running make, you will see two files (ava, 
startadore). In order for adore too execute you have to run startadore. Once you 
run startadore the user can then run ava. Table 4 is the output from 
./ava.
 
| Usage: ./ava 
      {h,u,r,R,i,v,U} [file, PID or dummy (for U)]          h 
      hide file        u 
      unhide file        r 
      execute as root        R 
      remove PID forever        U 
      uninstall adore        i 
      make PID invisible        v 
      make PID visible     | 
Table 4. Ava 
output
 
Table 4 shows us the options 
that adore provides to us. I will not cover each switch and what it does only 
because it does not help us detect this rootkit. Now that we have covered the 
basics of adore (LKM) lets look at how we detect adore and other rootkits. 
      Many rootkits 
hide processes, directories, files and even connections. But many of them do so 
by modifying the source code of binaries such as ps, df, netstat, top and lsof. 
There are a couple of ways to detect these types of rootkits (i.e. 
t0rn):
 
1) 
md5 checksums
 
2) 
Compiling these binaries from a known good source (i.e. cd-rom, disk).       
 
3) 
Some rootkits have a default port that an administrator can focus in on. 
 
4) 
Running a program such as chkrootkit
 
Many of the techniques used 
to detect rootkits like t0rn are not effective against LKM rootkits (chkrootkit 
can detect some of them). Since LKM rootkits access the kernel, they can hide 
processes, connections, directories and files without modifying the binaries. 
MD5 checksums become useless because there are no files being modified. This 
means the checksums will not change. Checking ports MIGHT be an option BUT that 
only tells you that you have been “rooted”.
      Well, how can I 
detect these monsters? Easy. There is a program I highly recommend to everyone 
concerned with LKM rootkits. This program is called KSTAT. You can find it at 
http://s0ftpj.org/en/site.html. KSTAT 
works by checking the memory (/dev/kmem) for information about the 
host(including LKMs). Want to see what kstat looks like? Good. Here is the 
output for kstat: 
 
Usage: kstat [-i iff] [-P] 
[-p pid] [-M] [-m addr] [-s]
 
-i    iff may be specified as 'all' 
or as name (e.g. eth0)
      displays info 
about the queried interface
 
-P    displays all 
processes
 
-p    pid is the process id of the 
queried task
 
-M    displays the kernel's LKMs' 
linked list
 
-m    addr is the hex address of 
the queried module
      displays info 
about the module to be found at addr
 
-s    displays info about the 
system calls' table
 
As you can see, kstat 
provides a person with many options for detecting these rootkits. Lets go 
through some of the options and from these options we will learn how to detect 
the following LKM rootkits:
 
1) 
knark
 
2) 
adore
 
3) 
rkit
 
BTW, if there’s any rootkits 
I have omitted please let me know and I will update the paper. : ) Kstat –s 
seems to be the best way to detect LKM rootkits. The other options are really 
great, but –s works all the time. Here is an output from kstat 
–s:
| SysCall                         
      Address sys_exit                        
      0xc0117ce4 sys_fork                        
      0xc0108ebc sys_read                        
      0xc012604c sys_write                       0xc0126110 sys_open                        
      0xc0125c10 sys_close                       
      0xc0125d60 sys_waitpid                     
      0xc0117ff8 sys_creat                       
      0xc0125ca4 sys_link                        
      0xc012de60 sys_unlink                      0xc012dc90 sys_execve                      
      0xc0108f18 sys_chdir                       
      0xc01254a0 sys_time                        
      0xc01184b4 sys_mknod                       
      0xc012d77c sys_chmod                       
      0xc01256e4 | 
        
   Table 5. kstat 
–s 
 
Table 5 is not a complete 
output from kstat –s, but it does give us an idea as too what the output looks 
like. Remember –s provides us with a picture of the sys_call_table. Keep this 
information fresh as we will revisit this again later.
      Kstat –P is 
another switch that is very effective. Kstat -P shows us all of the processes 
running at that time. This includes the processes hidden by an LKM rootkit. 
Table 6 is an example of 
kstat –P:
 
| PID  PPID  UID   GID   COMMAND  1     0     0   0     
      init  2     1     0   0     
      kflushd  3     1     0   0     
      kupdate  4     1     0   0     
      kpiod  5     1     0   0     
      kswapd  6     1     0   0     
      mdrecoveryd 241   1      1   0     
      portmap    256   1      0   0     
      lockd 257  256     0   0     
      rpciod 266   1      0   0     
      rpc.statd 280   1      0   0     
      apmd 331   1      0   0     
      syslogd   | 
Table 6. kstat –P 
 
When I first ran this 
program I wasn’t sure kstat could do what it said but…it proved my doubts WRONG. 
Here is what I did to verify this switch. I used ava -i 241(portmap). I then ran 
kstat –P. As you can see from table 6,  
portmap(bold) shows up. I then ran ps-ef(Table 7)and could not find it. 
Lsof also did not show it. Table 7 is the output from ps 
–ef:
 
UID        
PID  PPID  C STIME TTY          
TIME CMD
root         
1     0  0 Mar30 ?        
00:00:06 init [3]
root        
 2     1  0 Mar30 ?        
00:00:00 [kflushd]
root         
3     1  0 Mar30 ?        
00:00:00 [kupdate]
root         
4     1  0 Mar30 ?        
00:00:00 [kpiod]
root         
5     1  0 Mar30 ?        
00:00:00 [kswapd]
root         
6     1  0 Mar30 ?        
00:00:00 [mdrecoveryd]
root       256     1  0 Mar30 ?        
00:00:00 [lockd]
root       257   256  0 Mar30 ?        
00:00:00 [rpciod]
root       266     1  0 Mar30 ?        
00:00:00 rpc.statd
root       280     1  0 Mar30 ?        
00:00:00 /usr/sbin/apmd -p 10 -w 5 -W -s 
root       331     1  0 Mar30 ?        
00:00:00 syslogd -m 0
root       340     1  0 Mar30 ?        
00:00:00 klogd
nobody     354     1  0 Mar30 ?        
00:00:00 identd -e -o
nobody     357   354  0 Mar30 ?        
00:00:00 identd -e -o
nobody     359   357  0 Mar30 ?        
00:00:00 identd -e -o
nobody     360   357  0 Mar30 ?        
00:00:00 identd -e -o
nobody     361   357  0 Mar30 ?        
00:00:00 identd -e -o
daemon     372     1  0 Mar30 ?        
00:00:00 /usr/sbin/atd
root       386     1  0 Mar30 ?        
00:00:00 crond
root       404     1  0 Mar30 ?        
00:00:00 inetd
root       418     1  0 Mar30 ?        
00:00:00 lpd
root       462     1  0 Mar30 ?        
00:00:00 sendmail: accepting connections 
root       477     1  0 Mar30 ?        
00:00:00 gpm -t ps/2
root       491     1  0 Mar30 ?        
00:00:01 httpd
xfs        
531     1  0 Mar30 ?        
00:00:00 xfs -droppriv -daemon -port -1
root       571     1  0 Mar30 tty1     00:00:00 login -- 
root    
root       572     1  0 Mar30 tty2     00:00:00 
/sbin/mingetty tty2
root       573     1  0 Mar30 tty3     00:00:00 
/sbin/mingetty tty3
root       574     1  0 Mar30 tty4     00:00:00 
/sbin/mingetty tty4
root       575     1  0 Mar30 tty5     00:00:00 
/sbin/mingetty tty5
root       576     1  0 Mar30 tty6     00:00:00 
/sbin/mingetty tty6
nobody    4290   491  0 Apr01 ?        
00:00:00 httpd
nobody    4291   491  0 Apr01 ?        
00:00:00 httpd
nobody    4292   491  0 Apr01 ?        
00:00:00 httpd
nobody    4293   491  0 Apr01 ?        00:00:00 
httpd
nobody    4294   491  0 Apr01 ?        
00:00:00 httpd
nobody    4295   491  0 Apr01 ?        
00:00:00 httpd
nobody    4298   491  0 Apr01 ?        
00:00:00 httpd
nobody    4299   491  0 Apr01 ?        
00:00:00 httpd
root      8073   571  0 Apr02 tty1     00:00:00 
-bash
root     10659  8073  0 11:24 tty1     00:00:00 ps 
-ef
            
Table 7. ps –ef output
There are two other switches 
we will go over, they are kstat –p and kstat –M. First, lets look at kstat –p. 
kstat –p gives us more information about a process. In order to run kstat –p you 
will need to provide a process id. For example: kstat –p 241. This checks 
process 241 (portmap) and provide an output. Sometimes it can provide us with 
more information about a LKM rootkit. TABLE 8 is the output from kstat –p 
241:
 
| Name:     
      portmap State:    S 
      (sleeping) Pid:      
      241 Ppid:     1 
      (init) Uid:      1     1     1     
      1 Gid:      0     0     0     
      0 Flags:    PF_FORKNOEXEC 
      PF_SUPERPRIV  Crucial Capabilities 
      Check Open 
      Files     0     CHAR        
      /dev/null     1     CHAR        
      /dev/null     2     CHAR        
      /dev/null     3     0.0.0.0:111 0.0.0.0:0     4     0.0.0.0:111 0.0.0.0:0     7     FIFO        
      ///     8     FIFO        
      ///     21    CHAR        
      /dev/null     
   | 
Table 8. kstat –p 
output
 
Finally, lets look at kstat 
–M. Under normal conditions an administrator can perform lsmod or more 
/proc/modules and find out what modules he/she are running. When a machine has 
been “rooted” you can’t trust lsmod for accurate information. Kstat –M will 
catch many of the basic LKM rootkits that I have tested. Kstat –M list all of 
the modules loaded. I have seen it where kstat –M did list anything for knark, 
but…nothings perfect. The output from kstat –M is similar to 
lsmod.
 
 
So far we have covered 
a lot of material about LKM’s, rootkits and kstat. Now we are going to put all 
of that together and learn how to detect some of the LKM rootkits available 
today.  
The first LKM rootkit 
we want to look at is knark. Knark is probably the best-known LKM rootkit and 
one of the best written as well. Detecting it with kstat is fairly straight up 
as well. Remember Table 5? Well in order to detect a knark LKM rootkit you will 
need to run kstat –s. Once ran you need to look for the 
following:
 
 
|    sys_fork       
      0xc284652c WARNING! Should be at 0xc0108c88 sys_read       
      0xc2846868 WARNING! Should be at 0xc012699c sys_execve     0xc2846bb8 
      WARNING! Should be at 0xc0108ce4 sys_kill       
      0xc28465d4 WARNING! Should be at 0xc01106b4 sys_ioctl      0xc2846640 
      WARNING! Should be at 0xc012ff78 sys_settimeofday 
      0xc2846a8c WARNING! Should be at 0xc0118364 sys_clone      0xc2846580 
      WARNING! Should be at 0xc0108ca4   | 
 Table 9. knark 
detection
 
Lets take a closer look at 
this table and see what all of this mess means to us. First, we see that there 
are seven (7) sys_call_table entries that have warnings. Lets look at 
sys_settimeofday, here we see that currently it is making it’s home in memory at 
0xc2846a8c. kstat –s tells us that this is wrong and it should be at 0xc0118364. 
When knark  is installed it changes 
the sys_call_table and the memory locations where you can find: sys_fork, 
sys_read, sys_execve, sys_kill, sys_ioctl, sys_settimeofday and sys_clone. This 
is how you could detect knark. Knowing which sys_call_table entries have been 
changed could help you identify the actual rootkit itself. Knark changes 
seven(7) total. After running kstat –s, the next step would be to run ps-ef 
along with kstat –P and compare the two. If the are any differences in the two 
you can then take the correct action. Keep in mind that memory locations can 
change from box to box. As I said earlier, detecting LKM rootkits with kstat 
is quite simple. 
      Lets look at 
adore. Table 10 will show us what adore changes:
 
 
| sys_fork       
      0xc4051428 WARNING! Should be at 0xc0108c88 sys_write      0xc4051590 
      WARNING! Should be at 0xc01269b8 sys_close      0xc405163c 
      WARNING! Should be at 0xc01264a4 sys_kill       
      0xc40514d0 WARNING! Should be at 0xc011060c sys_mkdir      0xc405172c 
      WARNING! Should be at 0xc012e540 sys_clone      0xc405147c 
      WARNING! Should be at 0xc0108ca4 sys_getdents   0xc40512a4 WARNING! Should 
      be at 0xc013022c   | 
Table 10. Adore detection 
Adore changes seven (7) 
entries as well (just like knark). Knark and adore only change three (3) of the 
same sys_call_table entries. They are sys_fork, sys_kill, sys_clone. This is a 
good thing because it allows us the ability to detect each one separately. 
Again, once this has been done, the administrator can then run kstat –P and 
ps-ef and compare the two to figure out what processes are running and hiding. 
The last rootkit we will 
look at is rkit. This rootkit does not hide itself as well as the other two do. 
As a matter of fact it only changes sys_setuid and can be found using kstat –M. 
 
                
LKM 
rootkits can make a system administrator’s life a nightmare. They are hard to 
detect, but using tools like kstat and understanding what the rootkit changes 
can make our life easier. Since tools like kstat are available, it would help 
systems administrators if they took a “picture” of the sys_call_table after a 
fresh install and any upgrades. This will help in identifying the good from the 
bad.  Although I have not had the 
time, one could write a shell | perl script to automate this process and check 
weekly, daily, every 10 minutes or whatever. 
 
 
http://s0ftpj.org/en/site.html
http://members.prestige.net/tmiller12/papers/KNARK.htm
 
LKM 
rootkits
 
http://packetstorm.securify.com/groups/thc/LKM_HACKING.html
 
Toby 
Miller is a GIAC Certified Analyst and MCP. He is currently working towards his 
CISSP and RHCE. Toby has contributed to 2 books, written papers for SANS, 
Securityfocus, performs Risk Assessments and runs a lab for a living. For 
entertainment Toby likes to analyze exploit signatures on his home network. Toby 
can be reached at tmiller@va.prestige.net.