拦截 open
文章目录
- 拦截 open
- 第一个需求
- 文件结构
- 进程白名单
- 文件白名单
- 测试代码
- 第一个版本
- 版本二
- 代码演示
- 增加一个日志记录
- 代码解释
gcc -shared -fPIC -o libintercept.so intercept.c -ldl
LD_PRELOAD=./libintercept.so ./processA
在Linux中,我们可以使用LD_PRELOAD环境变量来拦截并替换系统调用,如open函数。这是一个简单的示例,展示了如何创建一个新的open函数来替换系统的open函数:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
// 定义原始的open函数
static int (*original_open)(const char*, int, ...);
// 我们的新open函数
int open(const char *pathname, int flags, ...) {
printf("Intercepted open call for %s\n", pathname);
// 获取原始的open函数
original_open = dlsym(RTLD_NEXT, "open");
// 调用原始的open函数
return original_open(pathname, flags);
}
~~~
你可以将这段代码保存为一个.c文件,然后使用以下命令编译为共享库:
gcc -shared -fPIC -o libintercept.so intercept.c -ldl
然后,你可以使用LD_PRELOAD环境变量来运行你的程序,如下所示:
```bash
LD_PRELOAD=./libintercept.so your_program
这样,每当你的程序调用open函数时,都会调用我们的新open函数,而不是系统的open函数。
请注意,这只是一个基本的示例,你可能需要根据你的具体需求来修改这个新的open函数。此外,使用LD_PRELOAD拦截系统调用应该谨慎进行,因为它可能会影响到其他库的行为。
第一个需求
第一个需求
1.白名单进程 保存在 进程白名单文件 , 受保护文件的文件名保存在 文件白名单文件 中,
2.白名单 进程 能访问 所有文件(受保护文件和非保护文件)
3.对所有进程访问文件的 过程进行日志记录.(进程名,文件名,访问时间,成功与否)
文件结构
.
├── Files
│ ├── t1
│ ├── t2
│ ├── t3
│ ├── t4
│ ├── t5
│ ├── t6
│ ├── t7
│ ├── t8
│ └── t9
├── WHITELIST
│ ├── file_whitelist
│ ├── file_whitelist_backup
│ └── process_whitelist
├── intercept.c
├── libintercept.so
├── log.txt
├── p1
└── p1.c
进程白名单
白名单文件的路径名是/home/xxx/Test/WHITELIST/process_whitelist
/home/xxx/Test/p1
/home/xxx/Test/p2
/home/xxx/Test/p3
/home/xxx/Test/p4
/home/xxx/Test/p5
文件白名单
非白名单文件的路径名是/home/xxx/Test/WHITELIST/file_whitelist
/home/xxx/Test/Files/t1
/home/xxx/Test/Files/t2
/home/xxx/Test/Files/t3
/home/xxx/Test/Files/t4
/home/xxx/Test/Files/t5
测试代码
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main() {
DIR *dir;
struct dirent *entry;
int fd;
// 打开目录
dir = opendir("/home/xxx/Test/Files");
if (dir == NULL) {
perror("无法打开目录");
return 1;
}
// 遍历目录中的每个文件
while ((entry = readdir(dir)) != NULL) {
// 检查文件名是否以"t"开头
if (strncmp(entry->d_name, "t", 1) == 0) {
char filepath[1024];
// 构造文件路径
snprintf(filepath, sizeof(filepath), "/home/xxx/Test/Files/%s", entry->d_name);
// 使用open函数打开文件
fd = open(filepath, O_RDWR);
if (fd == -1) {
perror("无法打开文件");
continue;
}
printf("打开文件: %s\n", filepath);
// 写入文件
const char *message = "我访问了这个文件\n";
if (write(fd, message, strlen(message)) == -1) {
perror("无法写入文件");
}
// 关闭文件
if (close(fd) == -1) {
perror("无法关闭文件");
}
}
}
// 关闭目录
if (closedir(dir) == -1) {
perror("无法关闭目录");
}
return 0;
}
第一个版本
gcc -shared -fPIC -o libintercept.so intercept.c -ldl
当然,这是修改后的完整代码:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
// 定义原始的open函数
static int (*original_open)(const char*, int, ...);
// 根据进程ID获取进程名
char *get_process_name(pid_t pid) {
char path[256];
static char name[256];
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
FILE *file = fopen(path, "r");
if (file == NULL) {
return NULL;
}
if (fgets(name, sizeof(name), file) == NULL) {
fclose(file);
return NULL;
}
fclose(file);
// 将程序名称转换为绝对路径
char *absolute_path = realpath(name, NULL);
if (absolute_path != NULL) {
strncpy(name, absolute_path, sizeof(name));
free(absolute_path);
}
return name;
}
// 检查进程是否在白名单中
int is_whitelisted_process(const char *whitelist) {
FILE *file = fopen(whitelist, "r");
if (file == NULL) {
return 0;
}
char *process_name = get_process_name(getpid());
if (process_name == NULL) {
fclose(file);
return 0;
}
char line[256];
while (fgets(line, sizeof(line), file)) {
line[strcspn(line, "\n")] = '\0'; // 去掉换行符
if (strcmp(line, process_name) == 0) {
printf("匹配进程白名单成功,进程名为%s\n",process_name);
fclose(file);
return 1;
}
}
fclose(file);
return 0;
}
// 检查文件是否在白名单中
int is_whitelisted_file(const char *whitelist, const char *filename) {
FILE *file = fopen(whitelist, "r");
if (file == NULL) {
return 0;
}
char line[256];
while (fgets(line, sizeof(line), file)) {
line[strcspn(line, "\n")] = '\0'; // 去掉换行符
if (strcmp(line, filename) == 0) {
printf("匹配文件白名单成功,访问文件名为%s\n",filename);
fclose(file);
return 1;
}
}
fclose(file);
return 0;
}
// 我们的新open函数
int open(const char *pathname, int flags, ...) { //pathname 是打开的文件地址
// 获取原始的open函数
original_open = dlsym(RTLD_NEXT, "open");
char real_path[PATH_MAX];
realpath(pathname, real_path); // 将打开的文件路径转换为绝对路径
// 检查进程是否在白名单中
if (is_whitelisted_process("/home/xxx/Test/WHITELIST/process_whitelist")) { // 传入的是进程白名单列表的绝对地址
// 白名单进程可以访问所有文件
return original_open(real_path, flags);
}
// 检查文件是否在白名单中, 传入文件白名单的绝对地址,和要访问文件的绝对地址
if (!is_whitelisted_file("/home/xxx/Test/WHITELIST/file_whitelist", real_path)) {
printf("非白名单文件的路径名是%s\n",real_path);
// 非白名单进程可以访问非白名单文件
return original_open(real_path, flags);
}
// 非白名单进程不能访问白名单文件
fprintf(stderr, "拒绝访问: %s\n", real_path);
return -1;
}
这段代码首先获取进程名,并将其转换为绝对路径。然后,它将这个绝对路径与进程白名单进行比较。如果进程在白名单中,它就可以访问所有文件。如果进程不在白名单中,但要访问的文件也不在白名单中,那么进程仍然可以访问该文件。如果进程和文件都不在白名单中,那么访问将被拒绝。
之前的版本一没有获取进程名绝对值
版本二
- 使用哈希表存储白名单:每次检查文件或进程是否在白名单中时,都需要打开白名单文件并逐行读取。这在白名单很大时会非常低效。相反,可以在程序启动时将白名单读入内存中的哈希表。这样,检查一个文件或进程是否在白名单中只需要在哈希表中查找,这将大大提高效率。
- 避免重复计算:
is_whitelisted_process
和is_whitelisted_file
函数都调用了get_process_name
函数来获取进程名。这意味着在一次open
调用中,可能会多次计算同一个进程的名字。可以通过将进程名缓存到全局变量中来避免这种重复计算。 - 减少内存分配:在
get_process_name
函数中,每次调用都会分配一个新的字符串来存储进程名。这可能会导致大量的内存分配和释放。可以考虑使用静态缓冲区来存储进程名,从而避免频繁的内存分配。 - 关闭文件描述符:在
is_whitelisted_process
和is_whitelisted_file
函数中,如果在打开文件后发生错误,函数会直接返回,可能会导致文件描述符泄漏。应确保在所有可能的代码路径上都正确关闭了文件描述符。
gcc -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include/ -shared -fPIC -o libintercept.so intercept.c -ldl -lglib-2.0
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <glib.h> // 需要安装glib库
// 定义原始的open函数
static int (*original_open)(const char*, int, ...);
// 进程名和白名单的全局变量
static char process_name[256] = {0};
static GHashTable *process_whitelist = NULL;
static GHashTable *file_whitelist = NULL;
// 根据进程ID获取进程名
char *get_process_name(pid_t pid) {
if (process_name[0] == '\0') {
char path[256];
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
FILE *file = fopen(path, "r");
if (file == NULL) {
return NULL;
}
if (fgets(process_name, sizeof(process_name), file) == NULL) {
fclose(file);
return NULL;
}
fclose(file);
}
// 将程序名称转换为绝对路径
char *absolute_path = realpath(process_name, NULL);
if (absolute_path != NULL) {
strncpy(process_name, absolute_path, sizeof(process_name));
free(absolute_path);
// printf("绝对路径不为空");
// printf("进程绝对路径为 %s",process_name);
}
else{
printf("进程名 %s 绝对路径为空\n",process_name);
}
return process_name;
}
// 从文件中加载白名单到哈希表
void load_whitelist(const char *filename, GHashTable **whitelist) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
return;
}
char line[256];
while (fgets(line, sizeof(line), file)) {
line[strcspn(line, "\n")] = '\0'; // 去掉换行符
g_hash_table_insert(*whitelist, g_strdup(line), NULL);
}
fclose(file);
}
// 检查进程是否在白名单中
int is_whitelisted_process() {
if (process_whitelist == NULL) {
process_whitelist = g_hash_table_new(g_str_hash, g_str_equal);
load_whitelist("/home/xxx/Test/WHITELIST/process_whitelist", &process_whitelist);
}
return g_hash_table_contains(process_whitelist, get_process_name(getpid()));
}
// 检查文件是否在白名单中
int is_whitelisted_file(const char *filename) {
if (file_whitelist == NULL) {
file_whitelist = g_hash_table_new(g_str_hash, g_str_equal);
load_whitelist("/home/xxx/Test/WHITELIST/file_whitelist", &file_whitelist);
}
return g_hash_table_contains(file_whitelist, filename);
}
// 我们的新open函数
int open(const char *pathname, int flags, ...) {
// 获取原始的open函数
original_open = dlsym(RTLD_NEXT, "open");
char real_path[PATH_MAX];
realpath(pathname, real_path); // 将打开的文件路径转换为绝对路径
// 检查进程是否在白名单中
if (is_whitelisted_process()) {
// 白名单进程可以访问所有文件
printf("白名单进程访问%s 访问文件%s\n",get_process_name(getpid()),real_path);
return original_open(real_path, flags);
}
// 检查文件是否在白名单中
if (!is_whitelisted_file(real_path)) {
// 非白名单进程可以访问非白名单文件
printf("非白名单进程访问%s 访问 非白名单文件%s\n",get_process_name(getpid()),real_path);
return original_open(real_path, flags);
}
printf("非白名单进程访问%s 访问 白名单文件%s\n",get_process_name(getpid()),real_path);
// 非白名单进程不能访问白名单文件
fprintf(stderr, "拒绝访问: %s\n", real_path);
return -1;
}
这个代码使用了glib
库的哈希表实现。在程序启动时,它会将白名单文件读入内存中的哈希表。然后,检查一个文件或进程是否在白名单中只需要在哈希表中查找,这将大大提高效率。此外,它还缓存了进程名,避免了重复计算。请注意,这个代码需要安装glib
库才能编译和运行。
代码演示
白名单进程
能访问所有文件
非白名单进程
只能访问非白名单文件
增加一个日志记录
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <time.h>
#include <glib.h> // 需要安装glib库
// 定义原始的open函数
static int (*original_open)(const char*, int, ...);
// 进程名和白名单的全局变量
static char process_name[256] = {0};
static GHashTable *process_whitelist = NULL;
static GHashTable *file_whitelist = NULL;
// 日志文件名
static const char *log_filename = "/home/xxx/Test/LOG";
// 根据进程ID获取进程名
char *get_process_name(pid_t pid) {
if (process_name[0] == '\0') {
char path[256];
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
FILE *file = fopen(path, "r");
if (file == NULL) {
return NULL;
}
if (fgets(process_name, sizeof(process_name), file) == NULL) {
fclose(file);
return NULL;
}
fclose(file);
}
// 将程序名称转换为绝对路径
char *absolute_path = realpath(process_name, NULL);
if (absolute_path != NULL) {
strncpy(process_name, absolute_path, sizeof(process_name));
free(absolute_path);
// printf("绝对路径不为空");
// printf("进程绝对路径为 %s",process_name);
}
else{
printf("进程名 %s 绝对路径为空\n",process_name);
}
return process_name;
}
// 从文件中加载白名单到哈希表
void load_whitelist(const char *filename, GHashTable **whitelist) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
return;
}
char line[256];
while (fgets(line, sizeof(line), file)) {
line[strcspn(line, "\n")] = '\0'; // 去掉换行符
g_hash_table_insert(*whitelist, g_strdup(line), NULL);
}
fclose(file);
}
// 检查进程是否在白名单中
int is_whitelisted_process() {
if (process_whitelist == NULL) {
process_whitelist = g_hash_table_new(g_str_hash, g_str_equal);
load_whitelist("/home/xxx/Test/WHITELIST/process_whitelist", &process_whitelist);
}
return g_hash_table_contains(process_whitelist, get_process_name(getpid()));
}
// 检查文件是否在白名单中
int is_whitelisted_file(const char *filename) {
if (file_whitelist == NULL) {
file_whitelist = g_hash_table_new(g_str_hash, g_str_equal);
load_whitelist("/home/xxx/Test/WHITELIST/file_whitelist", &file_whitelist);
}
return g_hash_table_contains(file_whitelist, filename);
}
// 记录日志
void log_access(const char *process, const char *file, int success) {
FILE *log_file = fopen(log_filename, "a");
if (log_file == NULL) {
return;
}
time_t now = time(NULL);
char *time_str = ctime(&now);
time_str[strlen(time_str) - 1] = '\0'; // 去掉换行符
fprintf(log_file, "[%s] Process: %s, File: %s, Access: %s\n",
time_str, process, file, success ? "SUCCESS" : "FAILURE");
fclose(log_file);
}
// 我们的新open函数
int open(const char *pathname, int flags, ...) {
// 获取原始的open函数
original_open = dlsym(RTLD_NEXT, "open");
char real_path[PATH_MAX];
realpath(pathname, real_path); // 将打开的文件路径转换为绝对路径
// 检查进程是否在白名单中
if (is_whitelisted_process()) {
// 白名单进程可以访问所有文件
if (is_whitelisted_file(real_path)) {
// 记录白名单进程访问白名单文件的日志
log_access(get_process_name(getpid()), real_path, 1);
}
return original_open(real_path, flags);
}
// 检查文件是否在白名单中
if (is_whitelisted_file(real_path)) {
// 记录非白名单进程访问白名单文件的日志
log_access(get_process_name(getpid()), real_path, 0);
fprintf(stderr, "拒绝访问: %s\n", real_path);
return -1;
}
// 非白名单进程可以访问非白名单文件
return original_open(real_path, flags);
}
代码解释
这段代码主要包含以下几个函数:
get_process_name(pid_t pid)
:此函数根据进程ID获取进程名。它首先检查全局变量process_name
是否已经被设置。如果没有,它会打开/proc/<pid>/cmdline
文件并读取第一行,这就是进程名。然后,它会将进程名转换为绝对路径。load_whitelist(const char *filename, GHashTable **whitelist)
:此函数从指定的文件中加载白名单,并将其存储在一个哈希表中。每一行都被视为一个独立的条目。is_whitelisted_process()
和is_whitelisted_file(const char *filename)
:这两个函数检查给定的进程或文件是否在相应的白名单中。如果白名单哈希表尚未创建,它们会首先调用load_whitelist
函数来加载白名单。log_access(const char *process, const char *file, int success)
:此函数将访问记录写入日志文件。每条记录包含时间戳、进程名、文件名和访问结果(成功或失败)。open(const char *pathname, int flags, ...)
:这是一个拦截open
系统调用的函数。它首先检查调用进程是否在进程白名单中。如果在,那么它可以打开任何文件。否则,它只能打开文件白名单中的文件。如果一个非白名单进程试图打开一个白名单文件,访问将被拒绝,并记录一条失败的访问记录。
这个拦截器可以用来限制某些进程访问敏感文件,或者跟踪某些进程的文件访问行为。但是,它也可能被用于恶意目的,比如阻止某些进程正常工作。因此,使用这种拦截器需要谨慎。
这段代码是一个Linux系统调用拦截器,它拦截了open
系统调用。这个拦截器的目的是限制某些进程访问特定的文件。它的工作原理如下:
- 获取进程名:
get_process_name
函数通过读取/proc/<pid>/cmdline
文件来获取进程名。这个文件包含了启动该进程的命令行参数。 - 加载白名单:
load_whitelist
函数从指定的文件中读取白名单,并将其存储在一个哈希表中。白名单中的每一行都被视为一个独立的条目。 - 检查白名单:
is_whitelisted_process
和is_whitelisted_file
函数检查给定的进程或文件是否在相应的白名单中。 - 记录日志:
log_access
函数将访问记录写入日志文件。每条记录包含时间戳、进程名、文件名和访问结果(成功或失败)。 - 拦截
open
系统调用:新的open
函数首先检查调用进程是否在进程白名单中。如果在,那么它可以打开任何文件。否则,它只能打开文件白名单中的文件。如果一个非白名单进程试图打开一个白名单文件,访问将被拒绝,并记录一条失败的访问记录。
这个拦截器可以用来限制某些进程访问敏感文件,或者跟踪某些进程的文件访问行为。但是,它也可能被用于恶意目的,比如阻止某些进程正常工作。因此,使用这种拦截器需要谨慎。
记录。
这个拦截器可以用来限制某些进程访问敏感文件,或者跟踪某些进程的文件访问行为。但是,它也可能被用于恶意目的,比如阻止某些进程正常工作。因此,使用这种拦截器需要谨慎。
这段代码是一个Linux系统调用拦截器,它拦截了open
系统调用。这个拦截器的目的是限制某些进程访问特定的文件。它的工作原理如下:
- 获取进程名:
get_process_name
函数通过读取/proc/<pid>/cmdline
文件来获取进程名。这个文件包含了启动该进程的命令行参数。 - 加载白名单:
load_whitelist
函数从指定的文件中读取白名单,并将其存储在一个哈希表中。白名单中的每一行都被视为一个独立的条目。 - 检查白名单:
is_whitelisted_process
和is_whitelisted_file
函数检查给定的进程或文件是否在相应的白名单中。 - 记录日志:
log_access
函数将访问记录写入日志文件。每条记录包含时间戳、进程名、文件名和访问结果(成功或失败)。 - 拦截
open
系统调用:新的open
函数首先检查调用进程是否在进程白名单中。如果在,那么它可以打开任何文件。否则,它只能打开文件白名单中的文件。如果一个非白名单进程试图打开一个白名单文件,访问将被拒绝,并记录一条失败的访问记录。
这个拦截器可以用来限制某些进程访问敏感文件,或者跟踪某些进程的文件访问行为。但是,它也可能被用于恶意目的,比如阻止某些进程正常工作。因此,使用这种拦截器需要谨慎。
待改进之处
程序一开始就应该加载白名单文件,而不是在open函数调用一次就加载一次。但是如果一开始就加载文件白名单,之后如果对白名单文件进行修改,就要将 该so 库文件重新编译一次。 如果能监控白名单文件是否修改,如果修改了就再加载白名单文件 就好了