/*
** Copyright (C) 2013 - Jonathan Salwan - http://twitter.com/JonathanSalwan
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
**
** For more information about this module,
** see : http://shell-storm.org/blog/Simple-Hook-detection-Linux-module/
**
*/
#include
#include
#include
#include
#include
#include
#include
#include
#define PATH_PYTHON "/usr/bin/python2.7"
#define PATH_SCRIPT "/opt/scripts/hook_detected.py"
#define TIME_SLEEP 30000 /* in msec */
static struct timer_list timer_s;
static struct workqueue_struct *wq;
static unsigned int syscall_table_size;
static unsigned long *addr_syscall_table;
static unsigned long *dump_syscall_table;
static int exec_python_script(unsigned int sys_num)
{
char s_num[32];
char *argv[] = {PATH_PYTHON, PATH_SCRIPT, s_num, NULL};
static char *envp[] = {"HOME=/", "TERM=linux", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL};
struct subprocess_info *sub_info;
sprintf(s_num, "%d", sys_num);
sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC);
if (sub_info == NULL)
return -ENOMEM;
call_usermodehelper_exec(sub_info, UMH_WAIT_PROC);
return 0;
}
static unsigned long *get_syscalls_table(void)
{
unsigned long *start;
/* hack :/ */
for (start = (unsigned long *)0xc0000000; start < (unsigned long *)0xffffffff; start++)
if (start[__NR_close] == (unsigned long)sys_close){
return start;
}
return NULL;
}
static unsigned int get_size_syscalls_table(void)
{
unsigned int size = 0;
while (addr_syscall_table[size++]);
return size * sizeof(unsigned long *);
}
static void check_diff_handler(struct work_struct *w)
{
unsigned int sys_num = 0;
while (addr_syscall_table[sys_num]){
if (addr_syscall_table[sys_num] != dump_syscall_table[sys_num]){
printk(KERN_INFO "hook_detection: Hook detected ! (syscall %d)\n", sys_num);
write_cr0(read_cr0() & (~0x10000));
addr_syscall_table[sys_num] = dump_syscall_table[sys_num];
write_cr0(read_cr0() | 0x10000);
exec_python_script(sys_num);
printk(KERN_INFO "hook_detection: syscall %d is restored\n", sys_num);
}
sys_num++;
}
}
static DECLARE_DELAYED_WORK(check_diff, check_diff_handler);
static void timer_handler(unsigned long data)
{
unsigned long onesec;
onesec = msecs_to_jiffies(1000);
queue_delayed_work(wq, &check_diff, onesec);
if (mod_timer(&timer_s, jiffies + msecs_to_jiffies(TIME_SLEEP)))
printk(KERN_INFO "hook_detection: Failed to set timer\n");
}
static int __init hook_detection_init(void)
{
addr_syscall_table = get_syscalls_table();
if (!addr_syscall_table){
printk(KERN_INFO "hook_detection: Failed - Address of syscalls table not found\n");
return -ECANCELED;
}
syscall_table_size = get_size_syscalls_table();
dump_syscall_table = kmalloc(syscall_table_size, GFP_KERNEL);
if (!dump_syscall_table){
printk(KERN_INFO "hook_detection: Failed - Not enough memory\n");
return -ENOMEM;
}
memcpy(dump_syscall_table, addr_syscall_table, syscall_table_size);
wq = create_singlethread_workqueue("hook_detection_wq");
setup_timer(&timer_s, timer_handler, 0);
if (mod_timer(&timer_s, jiffies + msecs_to_jiffies(TIME_SLEEP))){
printk(KERN_INFO "hook_detection: Failed to set timer\n");
return -ECANCELED;
}
printk(KERN_INFO "hook_detection: Init OK\n");
return 0;
}
static void __exit hook_detection_exit(void)
{
if (wq)
destroy_workqueue(wq);
kfree(dump_syscall_table);
del_timer(&timer_s);
printk(KERN_INFO "hook_detection: Exit\n");
}
module_init(hook_detection_init);
module_exit(hook_detection_exit);
MODULE_AUTHOR("Jonathan Salwan");
MODULE_DESCRIPTION("Hook Detection");
MODULE_LICENSE("GPL");