首页 > 编程知识 正文

Linux syscall Hook

时间:2023-05-05 09:18:21 阅读:274455 作者:1840

简介

Linux程序基本都是靠系统调用(syscall)来实现的, 所以可以控制到syscall, 就可以对程序进行监控或修改了;

前置知识

我们知道现在系统中程序分为64位和32位的, 系统为了兼容这些程序对系统函数的调用方法进行了简单的区分,最直观的就是64位程序使用syscall来调用系统函数,而32位的程序使用的是int 0x80, 比如这里的sleep函数的系统调用:
64位程序:

32位程序:

当然, 不是简单一个syscall命令或者int 0x80命令就可以完成一个系统调用, 还有一个系统调用号以及相应的参数;
当然我们最关心的是系统调用号, 通过上面的图片我们可以看到同样是sleep函数的调用但是64位和32位的程序的系统调用号是不一样的, 这里64位程序是0xe6,而32位程序是0x197;
具体的系统调用号我们可以通过/usr/include/x86_64-linux-gnu/asm/unistd_32.h和/usr/include/x86_64-linux-gnu/asm/unistd_64.h来查看32位和64位的系统调用号:
/usr/include/x86_64-linux-gnu/asm/unistd_32.h部分:


/usr/include/x86_64-linux-gnu/asm/unistd_64.h部分:

有了这些系统调用号, 这些函数具体的地址是被两个不同的系统调用表保存起来的,保存64位系统调用的系统调用表叫sys_call_table,保存32位系统调用的系统调用表叫ia32_sys_call_table;
这两个表的地址我们可以通过/boot/System.map-5.4.0-42-generic来查看:

有了这些表以及系统调用号,我们就可以找到想要的系统call的地址了;
比如要找64位的系统调用nanosleep,就可以用sys_call_table[35]来表示, 因为64位nanosleep的系统调用号是35;而32位的系统调用nanosleep是用ia32_sys_call_table[162]来表示了;

代码实践

现在我们就通过实践的例子来修改一个系统调用, 这里就以sleep函数出发;
sleep函数在底层64位程序通常调用的系统调用是clock_nanosleep, 而32位程序通常调用的系统调用是clock_nanosleep_time64;

内核中查找sys_call_table的地址

在内核查找sys_call_table的地址很容易,通过kallsyms_lookup_name找符号表就可以了:

unsigned long *sys_call_table_64 = 0;unsigned long *sys_call_table_32 = 0;sys_call_table_64 = (unsigned long *)(kallsyms_lookup_name("sys_call_table"));sys_call_table_32 = (unsigned long *)(kallsyms_lookup_name("ia32_sys_call_table")); 修改表中对应的函数

因为内核有安全保护, 我们要修改内存, 需要先将cr0寄存器值中的第17位清0:

asm volatile("mov %%cr0,%%rax" : "=a"(cr0)); ret = cr0; cr0 &= 0xfffeffff; //将cr0变量值中的第17位清0 asm volatile("mov %%rax,%%cr0" ::"a"(cr0));

然后定义我们自己的函数:

asmlinkage long sys_mycall1(void){ msleep(1000); return 0;}

修改表中对应的函数:

#define NUM2 230 //__NR_clock_nanosleep 64位系统调用号为230#define NUM5 407 //__NR_clock_nanosleep_time64 32位系统调用号为407sys_call_table_64[NUM2] = (unsigned long)&sys_mycall1;sys_call_table_32[NUM5] = (unsigned long)&sys_mycall1;

基本的思路就是这样, 然后再做一些完善就可以了;

完整代码

sysfix.c:

#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/unistd.h>#include <linux/sched.h>#include <linux/kallsyms.h>#include <linux/delay.h>MODULE_LICENSE("Dual BSD/GPL");// 64bit#define NUM1 35 //__NR_nanosleep 系统调用号为35#define NUM2 230 //__NR_clock_nanosleep 系统调用号为230//32bit#define NUM3 162 //__NR_nanosleep 系统调用号为162#define NUM4 267 //__NR_clock_nanosleep 系统调用号为230#define NUM5 407 //__NR_clock_nanosleep_time64 系统调用号为407int orig_cr0;unsigned long *sys_call_table_64 = 0;unsigned long *sys_call_table_32 = 0;static int (*nanosleep64_addr)(void);static int (*clock_nanosleep64_addr)(void);static int (*clock_nanosleep_time64_addr)(void);static int (*nanosleep32_addr)(void);static int (*clock_nanosleep32_addr)(void);static int clear_cr0(void){ unsigned int cr0 = 0; unsigned int ret; asm volatile("mov %%cr0,%%rax" : "=a"(cr0)); ret = cr0; cr0 &= 0xfffeffff; //将cr0变量值中的第17位清0 asm volatile("mov %%rax,%%cr0" ::"a"(cr0)); return ret;}static void setback_cr0(int val){ asm volatile("mov %%rax,%%cr0" ::"a"(val));}asmlinkage long sys_mycall1(void){ msleep(1000); return 0;}static int __init call_init(void){ sys_call_table_64 = (unsigned long *)(kallsyms_lookup_name("sys_call_table")); sys_call_table_32 = (unsigned long *)(kallsyms_lookup_name("ia32_sys_call_table")); printk("The sys_call_table_64 address is:%lxn", (unsigned long)sys_call_table_64); printk("The sys_call_table_32 address is:%lxn", (unsigned long)sys_call_table_32); printk("call_init......n"); nanosleep64_addr = (int (*)(void))(sys_call_table_64[NUM1]); clock_nanosleep64_addr = (int (*)(void))(sys_call_table_64[NUM2]); nanosleep32_addr = (int (*)(void))(sys_call_table_32[NUM3]); clock_nanosleep32_addr = (int (*)(void))(sys_call_table_32[NUM4]); clock_nanosleep_time64_addr = (int (*)(void))(sys_call_table_32[NUM5]); orig_cr0 = clear_cr0(); sys_call_table_64[NUM1] = (unsigned long)&sys_mycall1; sys_call_table_64[NUM2] = (unsigned long)&sys_mycall1; sys_call_table_32[NUM3] = (unsigned long)&sys_mycall1; sys_call_table_32[NUM4] = (unsigned long)&sys_mycall1; sys_call_table_32[NUM5] = (unsigned long)&sys_mycall1; setback_cr0(orig_cr0); return 0;}static void __exit call_exit(void){ printk("call_exit......n"); orig_cr0 = clear_cr0(); sys_call_table_64[NUM1] = (unsigned long)nanosleep64_addr; //将系统调用恢复 sys_call_table_64[NUM2] = (unsigned long)clock_nanosleep64_addr; sys_call_table_32[NUM3] = (unsigned long)nanosleep32_addr; sys_call_table_32[NUM4] = (unsigned long)clock_nanosleep32_addr; sys_call_table_32[NUM5] = (unsigned long)clock_nanosleep_time64_addr; setback_cr0(orig_cr0);}module_init(call_init);module_exit(call_exit);MODULE_AUTHOR("cc_sir");MODULE_VERSION("BETA 1.0");MODULE_DESCRIPTION("a module for replace a syscall");

Makefile:

KVERS = $(shell uname -r)# Kernel modules nameobj-m += sysfix.o# Many file.c#modulename-objs := file1.o file2.o# Specify flags for the module compilation.#EXTRA_CFLAGS=-g -O0build: kernel_moduleskernel_modules:make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modulesclean:make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。