Linux x64 Hook系统调用

Linux x64 Hook系统调用

实验环境:

Linux ubuntu 4.4.0-31-generic

#50~14.04.1-Ubuntu

参考文档:

https://blog.csdn.net/zuihaobushi/article/details/72729850 内核访问用户空间

https://www.cnblogs.com/arnoldlu/p/8879800.html 内核访问用户空间文件

https://www.cnblogs.com/bittorrent/p/3804141.html 导出sys_call_table地址

https://blog.csdn.net/daizhongyin/article/details/86567610 内核函数hook

https://blog.csdn.net/T146lLa128XX0x/article/details/79102775 hook系统调用

http://mcns.blog.chinaunix.net/uid-22964557-id-5757996.html __must_check


系统调用劫持:

修改内核符号表,来达到一个劫持的作用。因为系统调用实际上是触发了一个0x80的软中断,然后转到了系统调用处理程序的入口system_call()。system_call()会检查系统调用号来得出到底是调用哪种服务,然后会根据内核符号表跳转到所需要调用的内核函数的入口地址,所以,如果我们这个时候修改了内核符号表,使其跳转到我们自己的函数上,就可以完成劫持。

版本变化:2.6版本内核之前内核符号表直接导出,之后便不再导出

网上教程多为按照一下方式导出sys_call_table地址

  • 利用sidt指令获取中断向量描述表的地址
  • 找寻第0x80位描述符地址,记录了调用sys_call的地址
  • 源码中sys_call调用的100字节内具有实sys_call_table的地址

但是在实验环境下,sys_call_table获取错误。原因待分析

因此,采取另一种获取sys_call_table地址的方法

kallsyms_lookup_name读取法

kallsyms_lookup_name本身也是一个内核符号,如果这个符号被导出了,那么就可以在内核模块中调用kallsyms_lookup_name(“sys_call_table”)来获得sys_call_table的符号地址。

经过实验可成功获取sys_call_table地址。

内核权限修改

注意修改系统调用表时,由于内核中的很多东西,比如这里的系统调用表sys_call_table是只读的,我们需要修改一下权限才能修改。由于控制寄存器CR0的第16位若置位,则表示禁止系统进程写那些只有只读权限的文件,所以我们在修改系统调用表sys_call_table之前先将CR0的第16位清零,在修改完后再恢复置位就好了。如代码里的close_cr()函数,即是将CR0第16位清零,open_cr()函数是将CR0第16位恢复。

unsigned int close_cr(void){
    unsigned int cr0 = 0;
    unsigned int ret;
    asm volatile("movq %%cr0,%%rax":"=a"(cr0));
    ret = cr0;
    cr0 &= 0xfffeffff;
    asm volatile("movq %%rax,%%cr0"::"a"(cr0));
    return ret;
}
void open_cr(unsigned int oldval){
    asm volatile("movq %%rax,%%cr0"::"a"(oldval));
}

Hook地址修改

先获得系统调用表的地址,将系统调用表中的原有地址保存下来,再将hooked_open()函数的地址赋到系统调用表中.

((unsigned long * ) (sys_call_table_addr))[__NR_open]= (unsigned long) hooked_open;

Hook函数编写

功能:记录打开文件和时间

在编写hook函数时出现了几个问题:

  • 内核访问用户空间
asmlinkage long hooked_open(const char __user * filename, int flags, unsigned short mode)

sys_open函数参数为用户空间的参数,因此内核访问用户空间时需使用专用函数,具体可参考博客https://blog.csdn.net/zuihaobushi/article/details/72729850

否则,一访问系统即会崩溃

  • 内核操作文件

内核没有了用户空间的封装函数,所以需采用内核专用函数,如filp_open等,具体可参考博客https://www.cnblogs.com/arnoldlu/p/8879800.html

 

HOOK结果

挂载驱动

insmod hook.ko

并执行dmesg命令,可观察到驱动加载成功

dmesg

 

查看日志文件


特殊的问题:

1、__must_check

查看内核代码,copy_from_user 函数前面具有一个__must_check宏定义,

 

linux-4.2\arch\x86\include\asm\uaccess.h static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n) { …… }

继续跟踪其定义,其指调用函数一定要处理该函数的返回值,否则编译器会给出警告。但在编译时编译器并没有提示错误,但在挂载执行时会导致系统崩溃。

#if GCC_VERSION >= 30400 #define __must_check __attribute__((warn_unused_result)) #endif

因此,对其返回值进行检查,编译后挂载成功运行。


HOOK.C

/*
* This kernel module locates the sys_call_table by kallsyms_lookup_name("sys_call_table");
*/ 
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
#include<linux/unistd.h>
#include<linux/sched.h>
#include<linux/syscalls.h>
#include<linux/string.h>
#include<linux/fs.h>
#include<linux/fdtable.h>
#include<linux/uaccess.h> 
#include <linux/kallsyms.h> 
#include<linux/rtc.h>
#include<linux/vmalloc.h>
#include <linux/slab.h>

 /*
** module macros
*/ 
//MODULE_LICENSE("GPL"); 
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("geeksword"); 
MODULE_DESCRIPTION("hook sys_open"); 
/*
** module constructor/destructor
*/ 

unsigned long sys_call_table_addr = 0; 
long g_oldcr0 = 0;                  //save address of cr0
typedef asmlinkage long (*orig_open)(const char __user *filename, int flags, unsigned short mode);
orig_open g_old_open= NULL;

unsigned int close_cr(void){

    unsigned int cr0 = 0;

    unsigned int ret;

    asm volatile("movq %%cr0,%%rax":"=a"(cr0));

    ret = cr0;

    cr0 &= 0xfffeffff;

    asm volatile("movq %%rax,%%cr0"::"a"(cr0));

    return ret;

}

 

void open_cr(unsigned int oldval){

    asm volatile("movq %%rax,%%cr0"::"a"(oldval));

}


asmlinkage long hooked_open(const char __user * filename, int flags, unsigned short mode){
    struct file *fp;
    mm_segment_t fs;
    loff_t pos;
    int filename_size;
    char * temp_str = NULL; 
    char * file_str = NULL;
    struct timex  txc;
    struct rtc_time tm;

    filename_size = 1024;  
        
    temp_str = kmalloc(filename_size , GFP_KERNEL);

    if(temp_str == NULL)
    {       
        goto end;
    }

    file_str = kmalloc(filename_size , GFP_KERNEL);

    if(file_str == NULL)
    {       
        goto end;
    }
    memset(temp_str, 0, filename_size );
    memset(file_str, 0, filename_size );

    if (NULL != temp_str) {
        //copy_from_user(exec_str,filename, strnlen_user(filename,1000));
        if(0 != copy_from_user(temp_str,filename, strnlen_user(filename,1000)))
            goto end;
    }
    
    do_gettimeofday(&(txc.time));
    rtc_time_to_tm(txc.time.tv_sec,&tm);
    
    sprintf(file_str,"UTC time :%d-%02d-%02d %02d:%02d:%02d   Filename :%s \n",tm.tm_year+1900,tm.tm_mon, tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec,temp_str);
    
    fp = filp_open("/home/log_file",O_RDWR |O_CREAT| O_APPEND ,0777);
    if (IS_ERR(fp)){
        goto end;
    }
    pos = 0;
    fs = get_fs();
    set_fs(KERNEL_DS);
    vfs_write(fp,file_str, strlen(file_str),&pos);
        filp_close(fp,NULL);    
        set_fs(fs);
end:
    if(temp_str != NULL)
    {   
        kfree(temp_str);
    }
    if(file_str != NULL)
    {   
        kfree(file_str);
    }

    return g_old_open(filename,flags,mode);

}

/*******************************************************************
* Name:     obtain_sys_call_table_addr
* Description:  Obtains the address of the `sys_call_table` in the
*       system.
*******************************************************************/ 

static int obtain_sys_call_table_addr(unsigned long * sys_call_table_addr) { 
    int ret = 1; 
    unsigned long temp_sys_call_table_addr;
    temp_sys_call_table_addr = kallsyms_lookup_name("sys_call_table"); 
    /* Return error if the symbol doesn't exist */ 
    if (0 == sys_call_table_addr) { 
        ret = -1; 
        goto cleanup; 
    } 
    printk("Found sys_call_table: %p", (void *) temp_sys_call_table_addr); 
    *sys_call_table_addr = temp_sys_call_table_addr; 
cleanup: 
    return ret; 
} 

// initialize the module 
static int hooked_init(void) {
    printk("+ Loading hook_mkdir module\n"); 
    int ret = -1; 
    ret = obtain_sys_call_table_addr(&sys_call_table_addr); 
    if(ret != 1){ 
        printk("- unable to locate sys_call_table\n"); 
        return 0; 
    } 
    if(((unsigned long * ) (sys_call_table_addr))[__NR_close]!= (unsigned long)sys_close){

        printk("Incorrect sys_call_table address!\n");

        return 1;

    }

    // print out sys_call_table address 
    // now we can hook syscalls ...such as uname 
    // first, save the old gate (fptr) 
    g_old_open = ((unsigned long * ) (sys_call_table_addr))[__NR_open];
    // unprotect sys_call_table memory page 
    g_oldcr0 = close_cr();

    printk("+ unprotected kernel memory page containing sys_call_table\n"); 
    
    // now overwrite the __NR_uname entry with address to our uname 
    ((unsigned long * ) (sys_call_table_addr))[__NR_open]= (unsigned long) hooked_open; 
    
    open_cr(g_oldcr0);  
    
    printk("+ sys_execve hooked!\n"); 
    return 0; 
} 
static void hooked_exit(void) { 
    if(g_old_open != NULL) { 
        // restore sys_call_table to original state 
        g_oldcr0 = close_cr();

        ((unsigned long * ) (sys_call_table_addr))[__NR_open] = (unsigned long) g_old_open; 
        
        // reprotect page 
        open_cr(g_oldcr0);
    } 
    printk("+ Unloading hook_execve module\n"); 
} 
/*
** entry/exit macros
*/ 
module_init(hooked_init); 
module_exit(hooked_exit);

Makefile

obj-m += hook.o

all:
	make -C /lib/modules/`uname -r`/build M=$(PWD) modules

clean:
	make -C /lib/modules/`uname -r`/build M=$(PWD) clean

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
x64进程远程hook,x64_远程调用函数,源码更新V1.8.2:2021/4/12 源码为下方连接帖子后续更新内容: 浅谈64位进程远程hook技术: https://bbs.125.la/forum.php?mod=viewthreadtid=14666356extra= 不管您是转载还是使用请保留版权,源码在精益论坛免费发布本人未获利,请不要用于非法途径。 --------------------------------------------------------------- 2021/4/12 模块源码 v1.8.2更新 1:修复 x64_远调用函数()在 易语言 主线程调用时造成消息无法回调,导致易语言主线程窗口卡死的问题。      感谢楼下易友发现的BUG,已经第一时间更新 2021/4/12 模块源码 v1.8.1更新 1:修复 hook全部卸载时的流程写法的一个错误,由于句柄的提前关闭导致多个hook点卸载不干净的问题 2:改写了消息回调时线程传参的代码优化,优化了其他一些小问题 3:  鉴于很多朋友需要,改写了模块自带实列,对TCP,UDP的两组封包函数做了hook实列写法 4:列子中同样增加对x64_远调用函数()的应用写了几个列子,如使用套接字取得本地或远端IP端口API调用的的应用实列 5:本hook模块不支持非模块内存区hook,如申请的动态分配页等,不是不能支持,只是觉得没有任何意义,对这方面有需求的,自行改写模块源码使用 提醒:hook回调函数中尽量减少耗时代码,时间越长返回越慢,回调中谨慎操作控件,如必须要用到可参考源码中实列写法采用线程操作 历史更新 --------------------------------------------------------------- 2021/3/1   模块源码v1.6更新: 1:修复  x64_远程调用函数()命令,在没有提供 寄存器 参数时,没有返回值的BUG。 --------------------------------------------------------------- 2021/2/28 模块源码v1.5更新: 一:修复win7 64位系统下枚举模块 出现部分模块长度出现负数的问题,从而导致部分win7用户不能使用 二:强化 远程hook64指令_安装 的稳定性:        1,穿插代码中增加对标志位的保护,避免hook位置长度下一条指令为跳转时产生跳转错乱的问题,强化了hook任意位置的定位        2,因为穿插代码中会调用API函数,而64位汇编必须遵守栈指针16字节对齐,故对穿插代码进行栈指针16字节对齐,增强稳定性        3,hook指令安装支持长度由6-127字节 变动 为 6-119字节,原因么没必要说了,代码优化造成的,稍微少了一点无所谓了        4,对模块回调进行了适当优化处理,增强稳定性 三:应支持的朋友需要故增加 x64_远程调用函数()命令,易语言可以直接远call64进程,且无需写汇编代码或机器码指令,支持15个参数,支持返回值,支持16个通用寄存器全部取得返回值       该功能调用即16字节栈对齐,不要用户管堆栈,代码内部构成,远线程执行,你只需要知道call有几个参数,需要什么寄存器,对应提供即可。 四:有朋友说原模块x64英文看了烦,那好吧就给改成了中文标识,弄得我自己也不习惯 五:源码内列子改了改,可以自己看,需要注意的是模块注释的很详细,使用前最好看一看,尤其是hook回调接口的写法和安装的写法最好按照模块列子中的写法来,除非你能把64hook模块组看懂一遍,对于一些对本模块一知半解的朋友请不要乱改乱发,这个模块我会继续增强的,只是工作原因时间有限,只能一点一点来
### 回答1: Linux Hook 系统调用是指在 Linux 操作系统中,通过修改系统调用表来拦截和修改系统调用的行为。这种技术可以用于实现各种功能,如监控系统调用、实现安全策略、实现虚拟化等。Hook 系统调用的实现方式有多种,如使用内核模块、使用 LD_PRELOAD 环境变量、使用 ptrace 等。但是,Hook 系统调用也可能会带来一些安全风险,因此需要谨慎使用。 ### 回答2: Linux系统中的hook是一种通过修改系统内核来实现对系统或应用程序行为的改变的技术手段。hook技术的实现方式有很多种,比如LD_PRELOAD,在程序运行前拦截系统调用,重定向到用户编写的函数中,实现对系统调用进行Hook;也可以修改内核源代码,在内核中添加对指定系统调用Hook函数,并重新编译部署内核。 在Linux系统中,Hook技术被广泛运用于安全防护、性能优化、监控记录等领域。安全防护方面,通过Hook技术可以拦截进程的系统调用,实现对安全攻击的检测和防范;在性能优化方面,Hook技术可以记录系统资源使用情况,进行优化分析;在监控记录方面,Hook技术可以通过修改系统调用返回值,实现对应用程序的状态监控和记录等功能。 Hook技术的实现需要一定的计算机专业知识和技能,同时也需要了解目标系统的内部运行机制和系统调用的使用方法。在使用时需要注意,Hook技术对系统稳定性和性能会产生影响,需要谨慎使用和调试。同时,在使用Hook技术时需要考虑到实现方式的兼容性、对日后维护的影响等问题,从而保证Hook技术的稳健性和长期有效性。 综上所述,在Linux系统中,Hook技术是一种应用广泛、有效性高的技术手段,可以实现对系统和应用程序行为的灵活定制和改变,通过对系统调用的拦截和修改,实现安全防护、性能优化、监控记录等多种功能。不过,在使用时需要注意风险和影响,选择合适的实现方式,从而确保Hook技术的有效性和稳健性。 ### 回答3: Linux 系统调用是操作系统的一个重要组成部分,它提供了应用程序与操作系统之间通信的桥梁,使得应用程序可以利用操作系统的功能进行各种操作。然而,在某些情况下,用户可能需要修改某些系统调用的行为或者监控它们的使用情况,这就需要使用 Linux hook 系统调用Linux hook 系统调用,也称为系统调用钩子,是一种在系统调用进行时拦截和修改系统调用的技术。其主要作用是通过程序实现拦截和修改系统调用的行为,以达到特定的目的。比如,通过修改文件访问系统调用的行为,可以对文件进行加密、解密和防止恶意软件操作等。 Linux hook 系统调用的实现方式有很多种,其中最常见的方式就是使用内核模块实现。内核模块是一种动态加载到内核中的代码,可以通过内核提供的对模块的管理函数进行加载、卸载和修改等操作。利用内核模块,可以实现对指定系统调用的拦截与修改,具体实现方法是通过在模块中添加指定系统调用的替代函数来实现。 除此之外,还可以使用 LD_PRELOAD 环境变量或者 ptrace 系统调用来实现 Linux hook 系统调用。其中,LD_PRELOAD 环境变量是一种可以动态加载库文件的方法,通过在 LD_PRELOAD 中指定某个库文件,可以在程序运行时优先加载该库文件,从而实现对系统调用的拦截;而 ptrace 系统调用则是一种常用的系统调用监控方法,可以用来检测系统调用调用次数、参数和返回结果等信息。 总体来说,Linux hook 系统调用是一种非常实用的技术,可以用来实现各种监控和安全保障功能。但是,在使用时需要注意保证系统的稳定性和安全性,避免出现非预期的系统崩溃或安全漏洞。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值