迟来的生活 发表于 2013-12-16 21:20

Linux下拦截特定进程发出的系统调用,遇到惊悚的问题,大家帮忙看看

如何在Linux系统下实现对特定进程发出的系统调用进行拦截?
实验环境:RedHat linux,内核版本2.6.32,32位处理器。
查到有两种比较好的方法,一种是修改中断向量表法,代码如下:
有两个问题,在代码中添加了注释,希望大家能帮忙解答!
==========================================================
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <asm/unistd.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/dirent.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/fs.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("SHY");
MODULE_DESCRIPTION("test");

void my_stub();
static unsigned long old_stub;
static unsigned long stub_eax,stub_ebx,stub_ecx,stub_edx,stub_esi,stub_edi;

struct{
        unsigned short limit;
        unsigned long base;
}__attribute__((packed)) idtr;

//中断描述符
struct descriptor_idt{
        unsigned short offset_low;
      unsigned short selector;
      unsigned char reserved;
      unsigned char type:4;
      unsigned char segmentflag:1;
      unsigned char DPL:2;
      unsigned char present:1;
      unsigned short offset_high;
};

//拦截处理函数
void my_handler(){
//        if(current_comm=="ls"){                           //问题1:想实现对特定系统调用的拦截,最好是根据进程名称来区分,应该如何做?
                __asm__("movl %%eax,%0\n"
                        "movl %%ebx,%1\n"
                        "movl %%ecx,%2\n"
                        "movl %%edx,%3\n"
                        "movl %%esi,%4\n"
                        "movl %%edi,%5\n"
                        "popl %%ebx\n"
                        "popl %%ecx\n"
                        "popl %%edx\n"
                        "popl %%esi\n"
                        "popl %%edi\n"
                        "popl %%ebp\n"
                        "popl %%eax\n"
                        "popl %%ds\n"
                        "popl %%es\n"
                        "jmp *old_stub"
                        ::"m" (stub_eax),"m" (stub_ebx),"m" (stub_ecx),"m" (stub_edx),"m" (stub_esi),"m" (stub_edi));
                printk("syscall number == %d",stub_eax);
//        }else{
//                __asm__("popl %%ebx\n"
//                     "popl %%ecx\n"
//                        "popl %%edx\n"
//                        "popl %%esi\n"
//                        "popl %%edi\n"
//                        "popl %%ebp\n"
//                        "popl %%eax\n"
//                        "popl %%ds\n"
//                        "popl %%es\n"
//                        "jmp *old_stub");
//        }
}

void stub_kad(void){
        __asm__(".globl my_stub \n"
                ".align 4,0x90 \n"
                "my_stub: \n"
                "pushl %es\n"
                "pushl %ds\n"
                "pushl %eax\n"
                "pushl %ebp\n"
                "pushl %edi\n"
                "pushl %esi\n"
                "pushl %edx\n"
                "pushl %ecx\n"
                "pushl %ebx\n"
                "call my_handler \n");
}

static void disable_page_protection(){
        unsigned long value;
        asm volatile("mov %%cr0, %0":"=r" (value));
        if(!(value&0x00010000)){
                return;
        }
        asm volatile("mov %0, %%cr0"::"r" (value&~0x00010000));
}

static void enable_page_protection(){
      unsigned long value;
      asm volatile("mov %%cr0, %0":"=r" (value));
      if(!(value&0x00010000)){
                return;
      }
      asm volatile("mov %0, %%cr0"::"r" (value|0x00010000));
}

static void replace_system_call(void){
        struct descriptor_idt* idte;
       
        __asm__ volatile("sidt %0":"=m" (idtr));
        idte=(struct descriptor_idt*)(idtr.base+8*0x80);
        old_stub=(idte->offset_high+16|idte->offset_low);

        unsigned long new_addr=(unsigned long)my_stub;
        idte->offset_high=(unsigned short)(new_addr>>16);               //问题2:关键问题!这条语句的执行会导致系统死机,怀疑是因为没有对该地址的修改权限
        idte->offset_low=(unsigned short)(new_addr & 0x0000ffff);
}

static int __init _init_module(void){
        disable_page_protection();
        replace_system_call();
        printk("Intercept Module Installed!\n");
        return 0;
}

static void __exit _cleanup_module(void){
        struct descriptor_idt *idte;
        __asm__ volatile("sidt %0":"=m" (idtr));
      idte=(struct descriptor_idt*)(idtr.base+8*0x80);

        idte->offset_high=(unsigned short)(old_stub>>16);            //与问题2同样的问题
        idte->offset_low=(unsigned short)(old_stub & 0x0000ffff);
        enable_page_protection();

        printk("Intercept Module Uninstalled!\n");
}

module_init(_init_module);
module_exit(_cleanup_module);
=======================================================

另外一种拦截方法,是通过修改sysenter指令的跳转目的地址,这种方法运行通过了,但是在获取进程信息时存在问题,同样在代码中注释,代码如下:
功能是统计特定进程发出的不同的系统调用数量
=======================================================
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <asm/thread_info.h>
#include <asm/unistd.h>
#include <asm/msr.h>
#include <asm/pgtable.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("SHY");
MODULE_DESCRIPTION("system call interception");

asmlinkage char* my_function();
unsigned long handler_code=(unsigned long)&my_function;
unsigned long old_sysenter;
extern asmlinkage void my_stub();
unsigned long handled_times;
unsigned long counts;

void stub_trtr(void){
        __asm__(".globl my_stub        \n"
                ".align 4,0x90        \n"
                "my_stub:        \n"
                "        call *%0        \n"
                "        jmp *%1                \n"
                ::"m"(handler_code),"m"(old_sysenter));
}

asmlinkage char* my_function(){
      struct thread_info *info=current_thread_info();
      struct task_struct *task=info->task;
//      if(task->pid==1000){                                    //问题:拦截特定进程发出的系统调用,但是当加入这条if语句时加载模块会发生死机现象
        int my_eax;
        handled_times++;
        __asm__("movl %%eax,%0;":"=r"(my_eax));
        counts++;
//      }
        return;
}

void my_hook(){
        unsigned int a;
        rdmsr(MSR_IA32_SYSENTER_EIP,old_sysenter,a);
        wrmsr(MSR_IA32_SYSENTER_EIP,my_stub,0);
}

static int __init trtr_init(void){
        printk("interception module enter!\n");
        int i;
        for(i=0;i<300;i++){
                counts=0;
        }
        my_hook();
        handled_times=0;
        return 0;
}

static void trtr_exit(void){
        int j;
        wrmsr(MSR_IA32_SYSENTER_EIP,old_sysenter,0);
        for(j=0;j<300;j++){
                if(counts!=0){
                        printk("System call %d has been handled %ld times!\n",j,counts);
                }
        }
        printk("interception module exit!\n");
}

module_init(trtr_init);
module_exit(trtr_exit);
=======================================================

迟来的生活 发表于 2013-12-16 21:21

附上makefile:
=========================================

obj-m := intercept.o
modules-objs := intercept.o

KDIR := /lib/modules/`uname -r`/build
PWD := $(shell pwd)

all:default clean

default:
        make -C $(KDIR) M=$(PWD)

clean:
        rm -f *.o *.unsigned *.mod.c *.elf modules.order *.symvers *~

井蛙夏虫 发表于 2013-12-17 18:25

回复 1# 迟来的生活
我觉得这个问题到内核版块去问比较合适


对这些我不太了解,对你的程序提出些疑惑之处。
问题1:想实现对特定系统调用的拦截,最好是根据进程名称来区分,应该如何做?
这里是否还是进程上下文。如果是,应当能够得到task_struct,这里面应当有命令名吧。
调用系统调用时使用eax寄存器存储系统调用号,通过系统调用号应当能够知道是什么系统调用。

问题2:关键问题!这条语句的执行会导致系统死机,怀疑是因为没有对该地址的修改权限?
对这句话上两行处old_stub=(idte->offset_high+16|idte->offset_low);中的+16不太理解,我认为应当为右移操作。

问题:拦截特定进程发出的系统调用,但是当加入这条if语句时加载模块会发生死机现象?
这个函数调用没有保存和恢复寄存器内容,在调用前后寄存器中的内容是否会发生变化?调用这个函数时,系统调用的参数在什么位置?

   

迟来的生活 发表于 2013-12-17 19:21

第一个问题是我自己没说清楚,是想实现对特定进程发出的系统调用进行拦截,系统调用号可以得到,进程ID也可以得到,但是程序每次运行ID是不一样的,我希望能通过名称判断进程;
第二个问题我也不大理解,看的别人的代码。。
第三个问题,系统调用的参数还是保存在寄存器中的,你的意思是我的if语句会导致寄存器内容发生改变,所以会死机是吗?

井蛙夏虫 发表于 2013-12-18 19:30

回复 4# 迟来的生活
我只是猜测这样可能有问题,你测试一下就知道可不可以了。

   

迟来的生活 发表于 2013-12-18 19:38

试过了,跟寄存器的东西有没有入栈保护应该是没关系。。貌似遇到点具体的复杂的问题就没什么人能给出正解啊:((
页: [1]
查看完整版本: Linux下拦截特定进程发出的系统调用,遇到惊悚的问题,大家帮忙看看