Hook--基于LD_PRELOAD方法的文档拦截实现

本文介绍了一种基于LD_PRELOAD的文档拦截方法,通过动态链接库实现对文件访问和命令执行的控制,仅允许白名单进程访问,并记录详细的访问日志。文中详细描述了需求、代码实现、部署步骤以及遇到的bug和解决方案。

Hook–基于LD_PRELOAD方法的文档拦截实现:

1、需求

  1. 只允许白名单的进程(真实存在的进程)访问文档,其余拦截;
  2. 用日志(进程名+时间+访问对象+结果 的形式)记录白名单进程访问记录及其他被拦截的访问记录;
  3. 定期检查白名单是否发生变化。

2、需求分析

主要方法:基于LD_PRELOAD注入方式

实现一个动态链接库(DLL)的功能,其主要目的是拦截文件访问操作和命令执行操作,检查当前进程是否在白名单中,记录访问记录到日志,并定期检查白名单是否发生变化。

以下是对代码的需求分析:

  1. 白名单定义和管理
    • 代码维护了一个白名单,其中包含允许的进程名称,白名单定义在 whitelist 数组中。
    • 白名单的内容可以存储在一个文本文件(whitelist.txt)中,定期检查文件内容是否发生变化。
  2. 日志记录
    • 代码提供了一个 logAccess 函数,用于记录访问记录到日志文件。
    • 记录包括进程名称、访问对象(文件路径或命令类型)以及是否允许访问的信息。
    • 访问记录以特定的格式(时间戳 + 进程名称 + 访问对象 + 是否允许)保存在 access_log.txt 文件中。
  3. 定期检查白名单变化
    • checkWhitelistChanges 函数用于定期检查白名单是否发生变化。
    • 时间间隔由常量 3600 秒(1小时)定义,可以根据需要进行调整。
    • 如果白名单文件(whitelist.txt)发生变化,新的进程名称会被添加到白名单中。
  4. 文件访问拦截和记录
    • open 函数用于拦截文件访问操作。
    • 检查当前进程是否在白名单中,如果在白名单中,允许文件打开操作,否则拒绝。
    • 记录文件访问记录,包括进程名称、文件路径、以及是否允许访问。

总之,这段代码实现了一个安全控制和访问记录的功能,可用于确保只有特定的进程可以访问文件和执行命令,同时记录了所有访问活动。

代码部署:
1、确保当前目录下有白名单文件:whitelist.txt
2、确保当前目录下有日志文件:access_log.txt
3、创建一个可供测试的文件进行命令的测试:cat xxx.txt
4、使用以下命令 将file2.c文件编译成动态链接库
   gcc file2.c -fpic -shared -ldl -o file2.so
5、使用以下命令将file2.so动态库注入当前目录
   export LD_PRELOAD=./file2.so
6、测试:
   cat xxx.txt
   more xxx.txt
   tail xxx.txt
   head xxx.txt
   open xxx.txt
   vim xxx.txt

3、代码

#define _GNU_SOURCE // 启用GNU扩展特性
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h> // 包含动态链接库操作相关头文件
#include <string.h>
#include <time.h>

// 定义白名单,包含允许的进程名称
const char *whitelist[] = {};
// 记录上次白名单检查的时间
static time_t lastWhitelistCheckTime = 0;
// 记录白名单进程个数
static time_t numWhitelist = sizeof(whitelist) / sizeof(whitelist[0]);

// 定义白名单文件的路径
static const char *whitelistFile = "whitelist.txt";

// 检查白名单是否发生变化
void checkWhitelistChanges();

// 记录访问记录到日志文件
void logAccess(const char *process_name, const char *access_object, int is_allowed) {
    time_t current_time;
    struct tm *tm_info;
    time(&current_time);
    tm_info = localtime(&current_time);

    // 构建日志记录格式
    char log_message[128];
    strftime(log_message, sizeof(log_message), "[%Y-%m-%d %H:%M:%S] ", tm_info);
    strcat(log_message, process_name);
    strcat(log_message, " accessed ");
    strcat(log_message, access_object);
    if (is_allowed) {
        strcat(log_message, " - Allowed\n");
    } else {
        strcat(log_message, " - Denied\n");
    }

    // 打开日志文件以附加方式写入
    FILE *log_file = fopen("access_log.txt", "a");
    if (log_file) {
        fprintf(log_file, "%s", log_message);
        fclose(log_file);
    }
}

// 定义Hook的open函数
int open(const char *pathname, int flags, mode_t mode){
    checkWhitelistChanges();
    // 获取原始的open函数
    int (*original_open)(const char *, int, mode_t) = dlsym(RTLD_NEXT, "open");
    // 获取正在执行的进程名称
    char *process_name = strrchr(getenv("_"), '/') + 1;
    // 检查当前进程是否在白名单中
    int is_allowed = 0;
#if 0
    printf("process_name = %s\n", process_name);
#endif
    for (int i = 0; i < numWhitelist; ++i) {
        if (strcmp(process_name, whitelist[i]) == 0) {
#if 0
            printf("whiltelist[%d] = %s\n", i, whitelist[i]);
#endif
            is_allowed = 1;
            break;
        }
    }
    // 记录访问记录到日志
    logAccess(process_name, pathname, is_allowed);
    if (is_allowed) {
        return original_open(pathname, flags, mode); // 如果在白名单中,允许文件打开操作
    } else {
        // 拦截非白名单进程的文件访问操作
        return -1;
    }
}
// 检查白名单是否发生变化
void checkWhitelistChanges() {
    time_t current_time;
    time(&current_time);
    // 检查白名单变化的时间间隔,这里设置为10秒
    if (current_time - lastWhitelistCheckTime >= 10) {
        FILE *file = fopen(whitelistFile, "r");
        if (file) {
            // 读取白名单文件中的内容
            char line[256];
            int whitelistCount = 0; // 跟踪白名单中的进程名称数量
            while (fgets(line, sizeof(line), file)) {
                // 去掉行末的换行符
                line[strcspn(line, "\n")] = '\0';
#if 0
                printf("line = %s\n", line);
#endif
                // 将新的进程名称加入白名单
                whitelist[whitelistCount] = strdup(line);
                whitelistCount++;
            }
            numWhitelist = whitelistCount;
#if 0
            printf("whitelistCount = %d\n", whitelistCount);
            printf("numWhitelist = %ld\n", numWhitelist);
#endif
            fclose(file);
            // 更新白名单中的进程名称数量
            for (int i = whitelistCount; i < sizeof(whitelist) / sizeof(whitelist[0]); ++i) {
                whitelist[i] = NULL;
            }
            lastWhitelistCheckTime = current_time; // 更新上次检查时间
        } else {
            perror("Error reading whitelist file");
        }
    }
}

#if 0
// 定义Hook的cat命令
int system(const char *command)
{
    // 获取正在执行的进程名称
    char *process_name = strrchr(getenv("_"), '/') + 1;
    int is_allowed = 0;
    for (int i = 0; i < sizeof(whitelist) / sizeof(whitelist[0]); i++)
    {
        if (strcmp(process_name, whitelist[i]) == 0)
        {
            is_allowed = 1;
            break;
        }
    }
    if (strstr(command, "cat ") != NULL)
    {
        // 记录访问记录到日志(针对cat命令)
        logAccess(process_name, "cat command", is_allowed);

        if (is_allowed)
        {
            int (*original_system)(const char *) = dlsym(RTLD_NEXT, "system");
            return original_system(command);
        }
        else
        {
            return -1;
        }
    }
    else
    {
        // 记录访问记录到日志(针对system命令)
        logAccess(process_name, "system command", is_allowed);
        if (is_allowed)
        {
            int (*original_system)(const char *) = dlsym(RTLD_NEXT, "system");
            return original_system(command);
        }
        else
        {
            return -1;
        }
    }
}
#endif

4、开发过程中遇到的bug

1、代码bug

定义一个空的白名单数组,然后从txt白名单文件中读取内容(进程名)存入到白名单数组中,

结果出现如下bug:自定义的open函数里获取了进程名

char *process_name = strrchr(getenv("_"), '/') + 1;

然后就自动跳过了后面的for循环语句进行判断进程名是否与白名单中的进程名相同这一步,判断出这是由于for循环语句判断为false因此导致的跳过for循环,

原来for循环是这样的

for (int i = 0; i < sizeof(whitelist)/sizeof(whitelist[0]); ++i) {
        if (strcmp(process_name, whitelist[i]) == 0) {
#if 0
            printf("whiltelist[%d] = %s\n", i, whitelist[i]);
#endif
            is_allowed = 1;
            break;
        }
    }

修正后:在检查白名单变化的函数中将遍历whitelist.txt白名单文件后所记录的白名单进程个数whitelistCount赋值给一个静态全局变量numWhitelist,因此代码如下:

for (int i = 0; i < numWhitelist; ++i) {
    if (strcmp(process_name, whitelist[i]) == 0) {
        #if 0
        printf("whiltelist[%d] = %s\n", i, whitelist[i]);
        #endif
        is_allowed = 1;
        break;
    }
}

bug解决。

2、未知bug(水平不够暂时解决不了)
  1. 白名单中只添加了cat进程名,按道理说应该只有cat命令打开的文件才被允许执行,但是出现了bug,无法拦截open、more这两个命令打开文件,初步推测是因为LD_PRELOAD这种方法的局限性,还无法拦截open、more这些底层更为复杂的操作,需要考虑别的方法。

    但是还是可以拦截tail、less、head、gcc等大部分命令

  2. 白名单中添加了cat、open、more进程名,使用tail、more、less命令打开文件时出现了未知的段错误,错误如下:

1698716885981

1698716921807

1698716965403

1698717006993

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霜晨月c

谢谢老板地打赏~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值