前面的文章有聊到怎么拿到linux sys_call_table,高版本和低版本内核都有对应的方式,那么这篇文章讲怎么去hook Linux 系统调用,建议在虚拟机中尝试,此篇文章内核如下所示:
curtis@curtis-virtual-machine:~/Desktop/test$ uname -a
Linux curtis-virtual-machine 4.2.0-42-generic #49~14.04.1-Ubuntu SMP Wed Jun 29 20:22:11 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
代码:hook_read.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/delay.h>
#include <asm/paravirt.h>
unsigned long **sys_call_table;
unsigned long original_cr0;
asmlinkage long (*ref_sys_read)(unsigned int fd, char __user *buf, size_t count);
asmlinkage long new_sys_read(unsigned int fd, char __user *buf, size_t count)
{
/*do everything what you want*/
printk("sys_read has benn hook!\n");
return ref_sys_read(fd, buf, count);
}
//set a page writeable
int make_rw(unsigned long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
pte->pte |= _PAGE_RW;
return 0;
}
//set a page read only
int make_ro(unsigned long address)
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
pte->pte = pte->pte & ~_PAGE_RW;
return 0;
}
//Consider race condition on SMP systems.
void
disable_wp(void)
{
unsigned long cr0;
preempt_disable();
cr0 = read_cr0();
clear_bit(X86_CR0_WP_BIT, &cr0);
write_cr0(cr0);
preempt_enable();
return;
}
//Consider race condition on SMP systems.
void
enable_wp(void)
{
unsigned long cr0;
preempt_disable();
cr0 = read_cr0();
set_bit(X86_CR0_WP_BIT, &cr0);
write_cr0(cr0);
preempt_enable();
return;
}
//find sys_call_table address
static unsigned long **acquire_sys_call_table(void)
{
unsigned long int offset = (unsigned long int) sys_close;
unsigned long **sct;
printk(KERN_INFO "finding syscall table from: %p\n", (void*)offset);
while (offset < ULLONG_MAX)
{
sct = (unsigned long **)offset;
if (sct[__NR_close] == (unsigned long *)sys_close)
{
printk(KERN_INFO "sys call table found: %p\n", (void*)sct);
return sct;
}
offset += sizeof(void *);
}
return NULL;
}
static int __init read_hook_start(void)
{
if(!(sys_call_table = acquire_sys_call_table()))
return -1;
printk("sysc_call_table address:%p\n",(void *)sys_call_table);
// original_cr0 = read_cr0();
printk("cr0 value is:0x%ld\n",read_cr0());
// disable_wp();
// write_cr0(original_cr0 & ~0x000100000);
make_rw((long unsigned int)sys_call_table);
ref_sys_read = (void *)sys_call_table[__NR_read];
printk("sys_open address is :%p\n",ref_sys_read);
sys_call_table[__NR_read] = (unsigned long *)new_sys_read;
printk("Nes sys_open address is :%p\n",(void*)sys_call_table[__NR_read]);
make_ro((long unsigned int)sys_call_table);
// write_cr0(original_cr0);
// enable_wp();
return 0;
}
static void __exit read_hook_end(void)
{
if(!sys_call_table) {
return;
}
// disable_wp();
// write_cr0(original_cr0 & ~0x00010000);
make_rw((long unsigned int)sys_call_table);
sys_call_table[__NR_read] = (unsigned long *)ref_sys_read;
make_ro((long unsigned int)sys_call_table);
// write_cr0(original_cr0);
// enable_wp();
printk("Bye bye test!\n");
}
module_init(read_hook_start);
module_exit(read_hook_end);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Curtis li");
Makefile:
obj-m :=hook_read.o
KERNEL := /lib/modules/$(shell uname -r)/build
all:
make -C $(KERNEL) M=$(shell pwd) modules
install:
make -C $(KERNEL) M=$(shell pwd) modules_install
depmod -A
clean:
make -C $(KERNEL) M=$(shell pwd) clean
简要代码分析:
初始化函数通过以sys_close为基址遍历内存找到sys_call_table地址,拿到地址后,需要解决的是内核内存写保护的问题,代码中分别给到三种方法来关闭内存的写保护,实际上是两种,一种是改pte参数,一种是修改cr0寄存器的值(两种方法实现),先要定义好open的原始函数和open_hook函数,在open_hook函数中可以做自己想做的事情,但是一定要记得返回到原始open函数入口地址。
效果展示:
[ 584.867416] sys_read has benn hook!
[ 584.867416] sys_read has benn hook!
[ 584.867417] sys_read has benn hook!
[ 584.867418] sys_read has benn hook!
[ 584.867418] sys_read has benn hook!
[ 584.868939] sys_read has benn hook!
[ 584.868951] sys_read has benn hook!
[ 584.869104] sys_read has benn hook!
[ 584.869259] sys_read has benn hook!
[ 584.869261] sys_read has benn hook!
[ 584.869277] sys_read has benn hook!
[ 584.869279] sys_read has benn hook!
[ 584.869288] sys_read has benn hook!
[ 584.869289] sys_read has benn hook!
[ 584.869294] Bye bye test!
open函数被成功Hook住,此文章介绍的是低内核版本的open_hook,高版本获取sys_call_table的值方式不一样,可以参考之前文章!!!