截断系统调用

linux常见的hook技术

内核模块、Inline Hook、Got Hook、Preload Hook、系统文件修改

需求需要将所有的进程的系统调用都截断

可选的有内核模块、Preload Hook、系统文件修改

内核模块Hook

  通常是从内核源码特殊位置,修改回调、修改中断表;或修改重编译内核,导出内部函数,从而跳转到自定义函数,开发内核模块实现hook。

  • 可以拦截到所有应用层系统调用,应用层无法绕过
  • 开发调试复杂,测试周期长,升级和卸载内核模块带来稳定性问题

应用层Preload Hook

  对于Preload的解释,详见[Preload Hook原理](#Preload Hook原理)。Preload Hook是指利用系统支持的preload能力,将模块自动注入进程实现hook。可以通过以下手段使用Preload技术:一种是环境变量配置(LD_PRELOAD);另一种是文件配置:(/etc/ld.so.preload)。

  • 若使用命令行指定LD_PRELOAD则只影响该新进程及子进程;若写入全局环境变量则LD_PRELOAD对所有新进程生效;父进程可以控制子进程的环境变量从而取消preload
  • 文件preload方式影响所有新进程且无法被取消
  • 可以拦截到系统调用和普通库函数
  • 实现和操作最为简单,只需要编写同名系统调用函数即可实现hook
  • 可以使用动态调用方式或自定义实现方式绕过

需要快速的实现选择开发难度比较低的Preload Hook

Preload技术有自身的作用范围,如果将Preload环境变量到全局环境(/etc/profile)或使用Preload文件时,Preload便在所有在此(配置)时间节点之后运行的进程生效,而无法影响已经在运行的进程;若将Preload环境变量作为启动进程的环境变量(如shell中执行LD_PREOAD=/lib64/inject.so ./myprocess),则Preload只对当前进程生效。默认情况下进程创建的子进程也会继承Preload环境变量,而父进程可以在子进程初始化前修改子进程的环境变量,从而避免往子代传播。Preload具体使用方式如下:

  • LD_PRELOAD环境变量 指定动态库全路径,触发时机:启动时
  • /etc/ld.so.preload 指定动态库全路径,触发时机:启动时

  进程在启动后,会按照一定顺序加载动态库:

  • 加载环境变量LD_PRELOAD指定的动态库
  • 加载文件/etc/ld.so.preload指定的动态库
  • 搜索环境变量LD_LIBRARY_PATH指定的动态库搜索路径
  • 搜索路径/lib64下的动态库文件

  如前述,模块通过Preload技术添加到模块链后,进程加载器在解析导入表时,会按照链表顺序依次查找符号,因此如果在Preload动态库中实现并导出了同名(而不是C++修饰名)库函数,即可实现Hook。

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>

//直接看open函数


//如果试图打开这些文件将要验证 账号和密码(用户自定义)
#define PROCESS_FILE_PATH "自己的文件路径"
#define FILE_PATH "自己的文件路径"
#define LOG "自己的文件路径"
#define PATHLENGTH 128

#define MAX_PATH_LEN   256
#define ERR_ERROR -1
#define ERR_SUCCESS 0
bool m_start = false; //标志位 防止一直hook形成递归

//暂时全部没有close


//获取进程的路径 写入process_path中
int get_process_path(char *process_path)
{

char *p = NULL;
int n = 0;

memset(process_path,0x00,MAX_PATH_LEN);

n = readlink("/proc/self/exe",process_path,MAX_PATH_LEN);//   /proc/self/exe代表当前程序

if (NULL != (p = strrchr(process_path,'/'))){

    *p = '\0';
}else{

    printf("wrong process path");
    return ERR_ERROR;
}

return ERR_SUCCESS;

}


//返回true表示不是保护文件
bool no_protect(const char* pathname) //pathname试图打开的文件
{
    //1.打开  会递归
    //int fd = open(FILE_PATH,O_RDONLY);//打开指定存放保护文件路径的文件
    FILE* fp = fopen(FILE_PATH,"r");
    //2.解析
    //分配存储空间
    char buf[PATHLENGTH];//先用定长 之后优化 用指针 先判断文件的大小 然后malloc分配合适内存
    memset(buf,0,sizeof(buf));
    //读文件
    fread(buf,sizeof(char),sizeof(buf),fp);
    //size_t fsize = read(fd,buf,PATHLENGTH);
    //这里需要判段 暂时没判断
    //3.判断 返回
    fclose(fp);
    if(!strstr(buf,pathname))//没有查到
    {
        printf("不是保护文件\n");
        return true;
    }else
    {
        printf("是保护文件\n");
        return false;
    }
}


//如果是允许进程返回true
bool primit_process()
{
    //将进程的启动路径记录在path中
    char path[MAX_PATH_LEN];
    if (get_process_path(path)){

        printf("get process path error!\n");
        return ERR_ERROR;
    }

    //1.打开  不能用open会递归
    //int fd = open(PROCESS_FILE_PATH,O_RDONLY);//打开指定存放保护文件路径的文件
    FILE* fp = fopen(PROCESS_FILE_PATH,"r");
    //2.解析
    //分配存储空间
    char buf[PATHLENGTH];//先用定长 之后优化 用指针 先判断文件的大小 然后malloc分配合适内存
    memset(buf,0,sizeof(buf));
    //读文件
    fread(buf,sizeof(char),sizeof(buf),fp);
    //size_t fsize = read(fd,buf,PATHLENGTH);
    fclose(fp);
    //这里需要判段 暂时没判断
    //3.判断 返回
    if(!strstr(buf,path))//没有查到
    {
        printf("不是允许进程\n");
        return false;
    }else
    {
        printf("是允许进程\n");
        return true;
    }

}


// 定义一个函数指针,用于保存原始的 open 函数地址
int (*original_open)(const char *pathname, int flags, ...);

void encode(char** point,const char* str)
{
    strncpy(*point,str,strlen(str));
    *point = *point+strlen(str);
    **point=' ';
    *point+=1;
}

//记录时间 调用函数 被访问文件pathname  访问的状态 此处false 访问的进程 get_process_path
int file_open_log(const char* pathname,bool state)
{   
    
    time_t current_time;
    char* c_time_string;
    
    // 获取当前时间
    current_time = time(NULL);
    c_time_string = ctime(&current_time);

    //获取执行文件的路径
    char path[MAX_PATH_LEN];
    if (get_process_path(path)){

        printf("get process path error!\n");
        return ERR_ERROR;
    }

    
    char* point;//记录位置
    char log_buf[1024];//strncpy到这个路径下

    point = log_buf;
    memset(log_buf,0,sizeof(log_buf));
    
    //时间    此步骤可以写一个函数 代码复用性高
    encode(&point,c_time_string);
    
    //路径
    encode(&point,path);

    //被访问的文件 写入内存
    encode(&point,pathname);


    if(state)
    {
        encode(&point,"true");

    }
    else
    {
        encode(&point,"false");
    }
    encode(&point,"\n");

    FILE* fd = fopen(LOG,"a");
    //path只是执行文件的路径 在之前要统一
    fputs(log_buf,fd);//介绍符为\0
    
    fclose(fd);
    
}


int open(const char *pathname, int flags, ...) {
    va_list args;
    va_start(args, flags);
    mode_t mode = va_arg(args, mode_t);
    va_end(args);


    bool noProtect;//判断是否在保护文件中
    noProtect = no_protect(pathname);  


    //获取原来的open函数 如果之前没有hook 这次hook
    if(!m_start)
    {
    original_open = dlsym(RTLD_NEXT, "open");
    m_start = true;
    printf("Intercepted open call for file: %s\n", pathname);
    }


    //不是保护文件
    if(noProtect)
    {   
        //此处open已经被hook 所以里面用的fopen
        file_open_log(pathname,true);//成功打开
        return original_open(pathname, flags, mode);
    }else//是保护文件
    {
        //判断是否是允许的进程
        if(primit_process())//open改掉
        {   
            file_open_log(pathname,true);
            return original_open(pathname, flags, mode);
        }else{
            //记录时间 调用函数 被访问文件pathname 访问的状态 此处false 访问的进程 get_process_path
            file_open_log(pathname,false);
            return -1;
        }
    }
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值