之前有一点我们没有提及,lsmod会报告装载了knark.o模块。不幸的是,入侵者能轻易的将此信息抹去。knark同时还包括了另一个LKM叫做modhide,能够隐藏自身以及上一个模块。一旦模块隐藏,如果不重启动机器就无法卸载,而且没有简单的方法检测到模块的加载,所有的相关信息都不见了。正如之前介绍的,knark的所有功能令其成为终极秘密武器。
预防方法
阻止LKM破坏显然是最佳解决方案。我们有几种方法能够提前预防LKM。可以通过保护系统调用表来预防大部分的恶毒LKM。我们可以构造一个简单的LKM,定时的或者在其他模块加载时监控系统调用表。如果它发现系统调用表改变了,可以通知系统管理员甚至将调用表修改回原来的值。下面的例子能很好的工作在Linux 2.2和2.4上。如果你的机器有超过一个处理器,可以用如下命令编译:gcc -D __SMP__ -c syscall_sentry.c。如果是单处理器,去掉-D __SMP__就行了。编译成功后,用insmod加载。具体参看下面的例子。
/*
* This LKM is designed to be a tripwire for the sys_call_table.
*/
#define MODULE_NAME "syscall_sentry"
/* This definition is the time between periodic checks. */
#define TIMEOUT_SECS 10
#define MODULE
#define __KERNEL__
#include<linux/module.h>
#include<linux/config.h>
#include<linux/version.h>
#include<linux/kernel.h>
#include<linux/sys.h>
#include<linux/param.h>
#include<linux/sched.h>
#include<linux/timer.h>
#include<sys/syscall.h>
/* This function is a simple string comparison function */
static int mystrcmp( const char *str1, const char *str2)
{
while(*str1 && *str2)
if (*(str1++) != *(str2++))
return -1;
return 0;
}
/* This function builds a timer struct for versions of Linux
* less than Linux 2.4. It is used to set a timer
*/
#if linux_VERSION_CODE < KERNEL_VERSION(2,4,0)
/* Initializes a timer */
void init_timer(struct timer_list * timer)
{
timer->next = NULL;
timer->prev = NULL;
}
#endif
/* This is our timer */
static struct timer_list syscall_timer;
/* This is the system’s syscall table */
extern void *sys_call_table[];
/* This is the saved, valid syscall table */
static void *orig_sys_call_table[ NR_syscalls ];
/* This function is needed to protect yourself */
static unsigned long (*orig_init_module) (const char *, struct module*);
/* This function checks the syscalls for changes
* and changes them back to the original if it has
* been changed.
*/
static int check_syscalls( void )
{
int i;
/* Add a new timer for our next check */
del_timer( &syscall_timer );
init_timer( &syscall_timer );
syscall_timer.function = (void *)check_syscalls;
syscall_timer.expires = jiffies + TIMEOUT_SECS * HZ;
add_timer( &syscall_timer );
for ( i = 0; i < NR_syscalls - 1; i++ )
{
if (orig_sys_call_table[i] != sys_call_table[i])
{
printk(KERN_INFO " SysCallSentry - sys_call_table has been
modified in entry %d! ", i);
sys_call_table[i] = orig_sys_call_table[i];
}
}
return 1;
}
/* Check sys_call_table anytime a new module is loaded. */
static int long sys_init_module_wrapper( const char *name, struct
module *mod )
{
int i;
int res = (*orig_init_module)(name,mod);
for ( i = 0; i < NR_syscalls - 1; i++ )
{
if (orig_sys_call_table[i] != sys_call_table[i])
{
printk( KERN_INFO " SysCallSentry - sys_call_table has been
modified in entry %d! ", i);
sys_call_table[i] = orig_sys_call_table[i];
}
}
return res;
}
/* Module Init Code */
static int init_module (void)
{
int i;
printk(KERN_INFO " SysCallSentry Inserted ");
/* Initiate the periodic timer */
init_timer( &syscall_timer );
/* Save the old values of the sys_call_table */
orig_init_module = sys_call_table[SYS_init_module];
/* Wrap the init_module syscall. This will check to see
* if any calls have been altered when a new module loads.
*/
sys_call_table[SYS_init_module] = sys_init_module_wrapper;
for ( i=0; i < NR_syscalls - 1; i++ )
{
orig_sys_call_table[i] = sys_call_table[i];
}
/* Start our first check */
check_syscalls();
return(0);
}
/* Module Cleanup Code */
static void cleanup_module (void)
{
/* Return system status to the original */
sys_call_table[SYS_init_module] = orig_init_mo
