拆为多文件结构,规范目录结构
This commit is contained in:
parent
f795943521
commit
8ae3dd720a
|
|
@ -9,6 +9,8 @@
|
|||
"*.json": "jsonc",
|
||||
"string.h": "c",
|
||||
"shm.h": "c",
|
||||
"unistd.h": "c"
|
||||
"unistd.h": "c",
|
||||
"logging.h": "c",
|
||||
"fcntl.h": "c"
|
||||
}
|
||||
}
|
||||
20
Makefile
20
Makefile
|
|
@ -1,22 +1,32 @@
|
|||
CC = gcc
|
||||
CFLAGS = -shared -fPIC -Wall -Wextra -Werror -O2 -fno-strict-aliasing -fPIC -fno-omit-frame-pointer -fno-stack-protector -Wl,-z,relro,-z,now
|
||||
LDFLAGS = -ldl -ljson-c
|
||||
TARGET = intercept.so
|
||||
SRC = execve_intercept.c
|
||||
TARGET_NAME = intercept.so
|
||||
BUILD_DIR = build
|
||||
SRC_DIR = src
|
||||
SRC = $(wildcard $(SRC_DIR)/*.c)
|
||||
OBJ = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRC))
|
||||
TARGET = $(BUILD_DIR)/$(TARGET_NAME)
|
||||
|
||||
# 如果需要开启 debug,只需执行 make DEBUG=1
|
||||
ifeq ($(DEBUG),1)
|
||||
CFLAGS += -DDEBUG
|
||||
CFLAGS += -DDEBUG -g # Add -g for debugging symbols
|
||||
endif
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SRC)
|
||||
$(BUILD_DIR)/$(TARGET_NAME): $(OBJ)
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
@mkdir -p $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
debug:
|
||||
$(MAKE) DEBUG=1
|
||||
|
||||
rebuild: clean all
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,515 +0,0 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <errno.h> // 添加 errno 相关定义
|
||||
#include <fcntl.h>
|
||||
#include <json-c/json.h>
|
||||
#include <pty.h>
|
||||
#include <signal.h> // 添加 SIGCHLD 相关定义
|
||||
#include <stdbool.h> // 引入 bool 类型
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/select.h> // 添加 select 相关定义
|
||||
#include <sys/shm.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
// getpid要用到
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void print_stacktrace() {
|
||||
void *buffer[100];
|
||||
int size = backtrace(buffer, 100); // 注意这里是 void ** 和 int 参数
|
||||
backtrace_symbols_fd(buffer, size, STDERR_FILENO);
|
||||
}
|
||||
|
||||
#define DEBUG_LOG(fmt, ...) \
|
||||
fprintf(stderr, "[DEBUG][PID %d] %s:%d:%s(): " fmt "\n", getpid(), \
|
||||
__FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||
#else
|
||||
|
||||
#define DEBUG_LOG(fmt, ...) ((void)0)
|
||||
|
||||
#endif
|
||||
|
||||
#define CONFIG_FILE "/tmp/exec_hook/config/execve_rules.json"
|
||||
#define LOG_FILE "/tmp/exec_hook/logs/execve.log"
|
||||
#define LOG_OUT_FILE "/tmp/exec_hook/logs/execve_out.log"
|
||||
#define COMMAND_NOT_FOUND "/usr/lib/command-not-found"
|
||||
|
||||
#define ANSI_COLOR_RED "\033[31m"
|
||||
#define ANSI_COLOR_YELLOW "\033[33m"
|
||||
#define ANSI_COLOR_RESET "\033[0m"
|
||||
|
||||
#define SHM_KEY 12345 // 用于标识共享内存的键值,需要确保唯一性
|
||||
#define MAX_RULES 100 // 假设最大规则数量
|
||||
#define MAX_ARGS 10 // 支持最多 10 个参数
|
||||
|
||||
typedef struct {
|
||||
char cmd[256];
|
||||
char type[32];
|
||||
char msg[1024];
|
||||
char args[MAX_ARGS][256]; // 支持最多 MAX_ARGS 个参数
|
||||
int arg_count;
|
||||
} Rule;
|
||||
|
||||
typedef struct {
|
||||
bool enabled;
|
||||
Rule rules[MAX_RULES];
|
||||
int rule_count;
|
||||
} ConfigData;
|
||||
|
||||
// 全局变量,指向共享内存中的配置数据
|
||||
static ConfigData *shared_config = NULL;
|
||||
static int shm_id = -1;
|
||||
static time_t last_modified_time = 0;
|
||||
// static int is_initialized = 0;
|
||||
|
||||
// 加载配置到共享内存
|
||||
int load_config_to_shm() {
|
||||
DEBUG_LOG("Loading configuration from %s to shared memory", CONFIG_FILE);
|
||||
json_object *root = json_object_from_file(CONFIG_FILE);
|
||||
if (!root) {
|
||||
DEBUG_LOG("Failed to parse config file from %s", CONFIG_FILE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ConfigData temp_config;
|
||||
temp_config.enabled = false;
|
||||
temp_config.rule_count = 0;
|
||||
|
||||
json_object *enabled_obj;
|
||||
if (json_object_object_get_ex(root, "enabled", &enabled_obj)) {
|
||||
temp_config.enabled = json_object_get_boolean(enabled_obj);
|
||||
}
|
||||
|
||||
if (!temp_config.enabled) {
|
||||
json_object_put(root);
|
||||
return 0; // 功能未启用,不加载规则
|
||||
}
|
||||
|
||||
json_object *rules_array_obj;
|
||||
if (json_object_object_get_ex(root, "rules", &rules_array_obj) &&
|
||||
json_object_get_type(rules_array_obj) == json_type_array) {
|
||||
int rules_len = json_object_array_length(rules_array_obj);
|
||||
temp_config.rule_count = rules_len < MAX_RULES ? rules_len : MAX_RULES;
|
||||
|
||||
for (int i = 0; i < temp_config.rule_count; i++) {
|
||||
json_object *rule_obj =
|
||||
json_object_array_get_idx(rules_array_obj, i);
|
||||
json_object *cmd, *type, *msg, *args;
|
||||
|
||||
json_object_object_get_ex(rule_obj, "cmd", &cmd);
|
||||
json_object_object_get_ex(rule_obj, "type", &type);
|
||||
json_object_object_get_ex(rule_obj, "msg", &msg);
|
||||
|
||||
if (cmd)
|
||||
strncpy(temp_config.rules[i].cmd, json_object_get_string(cmd),
|
||||
sizeof(temp_config.rules[i].cmd) - 1);
|
||||
if (type)
|
||||
strncpy(temp_config.rules[i].type, json_object_get_string(type),
|
||||
sizeof(temp_config.rules[i].type) - 1);
|
||||
if (msg)
|
||||
strncpy(temp_config.rules[i].msg, json_object_get_string(msg),
|
||||
sizeof(temp_config.rules[i].msg) - 1);
|
||||
|
||||
// 解析 args 参数
|
||||
temp_config.rules[i].arg_count = 0;
|
||||
if (json_object_object_get_ex(rule_obj, "args", &args) &&
|
||||
json_object_get_type(args) == json_type_array) {
|
||||
int args_len = json_object_array_length(args);
|
||||
temp_config.rules[i].arg_count =
|
||||
args_len < MAX_ARGS ? args_len
|
||||
: MAX_ARGS; // 限制最多 MAX_ARGS 个参数
|
||||
|
||||
for (int j = 0; j < temp_config.rules[i].arg_count; j++) {
|
||||
json_object *arg_item = json_object_array_get_idx(args, j);
|
||||
if (arg_item) {
|
||||
strncpy(temp_config.rules[i].args[j],
|
||||
json_object_get_string(arg_item),
|
||||
sizeof(temp_config.rules[i].args[j]) - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_object_put(root);
|
||||
|
||||
// 将临时配置复制到共享内存
|
||||
memcpy(shared_config, &temp_config, sizeof(ConfigData));
|
||||
|
||||
DEBUG_LOG("Loaded %d rules to shared memory", shared_config->rule_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 检查 args 是否匹配
|
||||
int args_match(char *const argv[], Rule *rule) {
|
||||
DEBUG_LOG("Matching args for rule with cmd: %s", rule->cmd);
|
||||
if (rule->arg_count == 0) {
|
||||
return 1; // 没有 args 约束,则直接匹配
|
||||
}
|
||||
|
||||
for (int i = 0; i < rule->arg_count; i++) {
|
||||
int found = 0;
|
||||
for (int j = 1; argv[j] != NULL; j++) { // 跳过 argv[0] (命令本身)
|
||||
if (strcmp(argv[j], rule->args[i]) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) return 0; // 只要有一个参数没有匹配,则不符合规则
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 写入日志
|
||||
void write_log(const char *filename, char *const argv[]) {
|
||||
DEBUG_LOG("Writing exec log for command: %s", filename);
|
||||
time_t now;
|
||||
time(&now);
|
||||
|
||||
FILE *log = fopen(LOG_FILE, "a");
|
||||
if (!log) return;
|
||||
|
||||
fprintf(log, "[%s] Command: %s\n", ctime(&now), filename);
|
||||
|
||||
for (int i = 0; argv[i]; i++) {
|
||||
fprintf(log, "arg[%d]: %s\n", i, argv[i]);
|
||||
}
|
||||
|
||||
fclose(log);
|
||||
}
|
||||
|
||||
// 判断字符是否为 ANSI 转义序列
|
||||
int is_ansi_escape_sequence(const char *str) {
|
||||
// ANSI 转义序列的常见格式是以 ESC 开头,后跟 '[', 'm' 等
|
||||
return str[0] == '\033' && str[1] == '[';
|
||||
}
|
||||
|
||||
// // 保存原始的 write 函数指针
|
||||
// static ssize_t (*original_write)(int fd, const void *buf, size_t count) =
|
||||
// NULL;
|
||||
|
||||
// // 保存日志文件描述符
|
||||
// static int log_fd = -1;
|
||||
|
||||
// ssize_t write(int fd, const void *buf, size_t count) {
|
||||
// ssize_t result = -1;
|
||||
|
||||
// result = original_write(fd, buf, count);
|
||||
// // 如果原始 write 成功,则将相同的内容写入日志文件
|
||||
// if (result > 0 && log_fd != -1) {
|
||||
// ssize_t log_result = original_write(log_fd, buf, count);
|
||||
// if (log_result == -1) {
|
||||
// fprintf(stderr, "Error writing to log file: %s\n",
|
||||
// strerror(errno));
|
||||
// // 注意:这里不应该影响原始 write 的返回值
|
||||
// }
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
|
||||
typedef int (*orig_execve_type)(const char *filename, char *const argv[],
|
||||
char *const envp[]);
|
||||
|
||||
// 原始指针
|
||||
static orig_execve_type orig_execve = NULL;
|
||||
|
||||
// 判断父进程是否为终端 shell (bash, zsh, fish 等)
|
||||
int is_terminal_shell() {
|
||||
pid_t ppid = getppid();
|
||||
char path[64], proc_name[256];
|
||||
FILE *file;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/comm", ppid);
|
||||
file = fopen(path, "r");
|
||||
if (!file) return 0;
|
||||
|
||||
if (fgets(proc_name, sizeof(proc_name), file)) {
|
||||
proc_name[strcspn(proc_name, "\n")] = 0; // 去除换行符
|
||||
if (strcmp(proc_name, "bash") == 0 || strcmp(proc_name, "zsh") == 0 ||
|
||||
strcmp(proc_name, "fish") == 0 || strcmp(proc_name, "sh") == 0) {
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 检查配置文件是否已修改
|
||||
int config_file_modified() {
|
||||
struct stat file_stat;
|
||||
DEBUG_LOG("Checking if config file has been modified: %s", CONFIG_FILE);
|
||||
if (stat(CONFIG_FILE, &file_stat) != 0) {
|
||||
DEBUG_LOG("Cannot get stat for FILE: %s", CONFIG_FILE);
|
||||
return 0;
|
||||
}
|
||||
int isChanged = file_stat.st_mtime != last_modified_time;
|
||||
if (isChanged != 0) {
|
||||
DEBUG_LOG("Updating last_modified_time to: %ld", file_stat.st_mtime);
|
||||
last_modified_time = file_stat.st_mtime;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 加载或重新加载配置到共享内存
|
||||
void load_config_if_needed() {
|
||||
if (shared_config == NULL) {
|
||||
// 首次加载,创建共享内存
|
||||
DEBUG_LOG("Creating shared memory for config data");
|
||||
shm_id = shmget(SHM_KEY, sizeof(ConfigData), IPC_CREAT | 0644);
|
||||
if (shm_id == -1) {
|
||||
perror("shmget failed");
|
||||
return;
|
||||
}
|
||||
shared_config = (ConfigData *)shmat(shm_id, NULL, 0);
|
||||
if (shared_config == (void *)-1) {
|
||||
perror("shmat failed");
|
||||
shared_config = NULL;
|
||||
return;
|
||||
}
|
||||
// 首次加载时读取配置文件
|
||||
DEBUG_LOG("Loading config file for the first time");
|
||||
struct stat file_stat;
|
||||
if (stat(CONFIG_FILE, &file_stat) == 0) {
|
||||
last_modified_time = file_stat.st_mtime;
|
||||
load_config_to_shm();
|
||||
} else {
|
||||
DEBUG_LOG("Cannot get stat for FILE: %s", CONFIG_FILE);
|
||||
// 初始化一个空的配置
|
||||
shared_config->enabled = false;
|
||||
shared_config->rule_count = 0;
|
||||
}
|
||||
} else if (config_file_modified()) {
|
||||
DEBUG_LOG("Config file has been modified.");
|
||||
load_config_to_shm();
|
||||
} else {
|
||||
DEBUG_LOG("Config file has not been modified, skipping reload.");
|
||||
}
|
||||
}
|
||||
|
||||
// 复制 stdout/stderr 到日志文件,同时保留终端颜色
|
||||
void duplicate_output_to_log() {
|
||||
DEBUG_LOG("Duplicating stdout/stderr to log file: %s", LOG_OUT_FILE);
|
||||
|
||||
int log_fd = open(LOG_OUT_FILE, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||
if (log_fd == -1) {
|
||||
perror("Failed to open log file");
|
||||
return;
|
||||
}
|
||||
|
||||
int master_fd, slave_fd;
|
||||
if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) == -1) {
|
||||
perror("openpty failed");
|
||||
close(log_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("fork failed");
|
||||
close(log_fd);
|
||||
close(master_fd);
|
||||
close(slave_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0) { // 子进程
|
||||
close(master_fd);
|
||||
|
||||
// 连接 slave_fd 到标准输入输出错误
|
||||
dup2(slave_fd, STDIN_FILENO);
|
||||
dup2(slave_fd, STDOUT_FILENO);
|
||||
dup2(slave_fd, STDERR_FILENO);
|
||||
close(slave_fd);
|
||||
|
||||
// 子进程不做输出,只保留环境等待 execve
|
||||
return; // 留给你调用 execve
|
||||
}
|
||||
|
||||
// 父进程(主控):读取 master_fd 并写入 stdout + 日志
|
||||
close(slave_fd);
|
||||
|
||||
// 忽略子进程退出信号
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
char buffer[1024];
|
||||
ssize_t n;
|
||||
int has_error = 0;
|
||||
|
||||
while ((n = read(master_fd, buffer, sizeof(buffer))) > 0) {
|
||||
// 检查错误
|
||||
if (memmem(buffer, n, "error", 5) || memmem(buffer, n, "Error", 5) ||
|
||||
memmem(buffer, n, "ERROR", 5)) {
|
||||
has_error = 1;
|
||||
}
|
||||
|
||||
// // 输出到终端
|
||||
// if (write(STDOUT_FILENO, buffer, n) == -1) {
|
||||
// perror("Failed to write to stdout");
|
||||
// }
|
||||
|
||||
// 写入日志
|
||||
if (write(log_fd, buffer, n) == -1) {
|
||||
perror("Failed to write to log file");
|
||||
}
|
||||
}
|
||||
|
||||
if (has_error) {
|
||||
printf("\n检测到命令执行出错,已经上报北冥论坛~ \n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
close(master_fd);
|
||||
close(log_fd);
|
||||
}
|
||||
|
||||
int execve(const char *filename, char *const argv[], char *const envp[]) {
|
||||
// if (!is_initialized) {
|
||||
// initialize();
|
||||
// }
|
||||
DEBUG_LOG("Intercepted execve for: %s", filename);
|
||||
DEBUG_LOG("argv[0] = %s", argv[0]);
|
||||
|
||||
orig_execve = (orig_execve_type)dlsym(RTLD_NEXT, "execve");
|
||||
if (orig_execve == NULL) {
|
||||
fprintf(stderr, "Error in dlsym(\"execve\"): %s\n", dlerror());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// 加载配置(仅在需要时)
|
||||
load_config_if_needed();
|
||||
|
||||
// 仅在 shell 终端调用 execve 时拦截
|
||||
if (!is_terminal_shell()) {
|
||||
DEBUG_LOG("Not a terminal shell, bypassing interception.");
|
||||
return orig_execve(filename, argv, envp);
|
||||
}
|
||||
|
||||
// 当前配置信息
|
||||
DEBUG_LOG("Current Config rule count : %d", shared_config->rule_count);
|
||||
|
||||
// 如果共享内存未成功加载,则直接执行
|
||||
if (shared_config == NULL) {
|
||||
DEBUG_LOG("Shared memory not initialized, bypassing interception.");
|
||||
return orig_execve(filename, argv, envp);
|
||||
}
|
||||
|
||||
// 如果功能被禁用,则直接执行
|
||||
if (!shared_config->enabled) {
|
||||
DEBUG_LOG("Not enabled.");
|
||||
return orig_execve(filename, argv, envp);
|
||||
}
|
||||
|
||||
write_log(filename, argv);
|
||||
|
||||
const char *basename = argv[0];
|
||||
if (strcmp(filename, COMMAND_NOT_FOUND) == 0 && argv[2]) {
|
||||
basename = argv[2];
|
||||
}
|
||||
|
||||
// 特殊处理以 shell.posix
|
||||
// 方式执行的命令,直接执行,不进行规则匹配和输出重定向
|
||||
if (argv[1] != NULL && strcmp(argv[1], "shell.posix") == 0) {
|
||||
return orig_execve(filename, argv, envp);
|
||||
}
|
||||
|
||||
for (int i = 0; i < shared_config->rule_count; i++) {
|
||||
if (strcmp(basename, shared_config->rules[i].cmd) == 0 &&
|
||||
args_match(argv, &shared_config->rules[i])) {
|
||||
DEBUG_LOG("Rule matched: %s (type: %s)",
|
||||
shared_config->rules[i].cmd,
|
||||
shared_config->rules[i].type);
|
||||
if (strcmp(shared_config->rules[i].type, "warn") == 0) {
|
||||
printf(ANSI_COLOR_YELLOW "[Warning] %s\n" ANSI_COLOR_RESET,
|
||||
shared_config->rules[i].msg);
|
||||
printf("按下 'Y' 继续执行, 或按任意键取消: ");
|
||||
char input = getchar();
|
||||
if (input != 'Y' && input != 'y') {
|
||||
printf("\nExecution cancelled.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
// return -1;
|
||||
}
|
||||
printf("\nContinuing execution...\n");
|
||||
} else if (strcmp(shared_config->rules[i].type, "error") == 0) {
|
||||
printf(ANSI_COLOR_RED "[Error] %s" ANSI_COLOR_RESET "\n",
|
||||
shared_config->rules[i].msg);
|
||||
exit(EXIT_FAILURE);
|
||||
// return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 复制 stdout 和 stderr 到日志文件
|
||||
duplicate_output_to_log();
|
||||
|
||||
return orig_execve(filename, argv, envp);
|
||||
}
|
||||
|
||||
// // 构造函数,在库被加载时执行
|
||||
// __attribute__((constructor)) static void initialize() {
|
||||
// if (is_initialized) return;
|
||||
// is_initialized = 1;
|
||||
// DEBUG_LOG("Initializing execve_intercept library.");
|
||||
// // // 获取原始的 write 函数
|
||||
// // original_write = dlsym(RTLD_NEXT, "write");
|
||||
// // if (original_write == NULL) {
|
||||
// // fprintf(stderr, "Error in dlsym(\"write\"): %s\n", dlerror());
|
||||
// // exit(EXIT_FAILURE);
|
||||
// // }
|
||||
|
||||
// // // 打开日志文件,以追加模式打开,如果不存在则创建
|
||||
// // log_fd = open(LOG_OUT_FILE, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||
// // if (log_fd == -1) {
|
||||
// // fprintf(stderr, "Error opening log file \"%s\": %s\n",
|
||||
// LOG_OUT_FILE,
|
||||
// // strerror(errno));
|
||||
// // exit(EXIT_FAILURE);
|
||||
// // }
|
||||
|
||||
// load_config_if_needed();
|
||||
// orig_execve = (orig_execve_type)dlsym(RTLD_NEXT, "execve");
|
||||
// if (orig_execve == NULL) {
|
||||
// fprintf(stderr, "Error in dlsym(\"execve\"): %s\n", dlerror());
|
||||
// exit(EXIT_FAILURE);
|
||||
// }
|
||||
// }
|
||||
|
||||
// 在库卸载时分离和删除共享内存
|
||||
__attribute__((destructor)) static void cleanup_shared_memory() {
|
||||
DEBUG_LOG("execve_intercept library unloaded.");
|
||||
// log输出路径
|
||||
DEBUG_LOG("Log file: %s", LOG_FILE);
|
||||
DEBUG_LOG("Log out file: %s", LOG_OUT_FILE);
|
||||
DEBUG_LOG("Config file: %s", CONFIG_FILE);
|
||||
DEBUG_LOG("Shared memory ID: %d", shm_id);
|
||||
if (shared_config != NULL) {
|
||||
DEBUG_LOG("Cleaning up shared memory.");
|
||||
// 解除共享内存映射
|
||||
if (shmdt(shared_config) == -1) {
|
||||
perror("shmdt failed");
|
||||
}
|
||||
shared_config = NULL;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
print_stacktrace();
|
||||
#endif
|
||||
|
||||
// if (log_fd != -1) {
|
||||
// DEBUG_LOG("Closing log file descriptor.");
|
||||
// close(log_fd);
|
||||
// }
|
||||
// 注意:这里不删除共享内存段,因为可能被其他进程使用。
|
||||
// 如果需要删除,需要一个明确的机制来判断是否是最后一个使用者。
|
||||
// 例如,可以创建一个单独的工具来管理共享内存的生命周期。
|
||||
}
|
||||
BIN
intercept.so
BIN
intercept.so
Binary file not shown.
|
|
@ -0,0 +1,144 @@
|
|||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include <json-c/json.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Global variables (defined in execve_interceptor.c)
|
||||
extern ConfigData *shared_config;
|
||||
extern int shm_id;
|
||||
extern time_t last_modified_time;
|
||||
|
||||
// Load configuration to shared memory
|
||||
int load_config_to_shm() {
|
||||
DEBUG_LOG("Loading configuration from %s to shared memory", CONFIG_FILE);
|
||||
json_object *root = json_object_from_file(CONFIG_FILE);
|
||||
if (!root) {
|
||||
DEBUG_LOG("Failed to parse config file from %s", CONFIG_FILE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ConfigData temp_config;
|
||||
temp_config.enabled = false;
|
||||
temp_config.rule_count = 0;
|
||||
|
||||
json_object *enabled_obj;
|
||||
if (json_object_object_get_ex(root, "enabled", &enabled_obj)) {
|
||||
temp_config.enabled = json_object_get_boolean(enabled_obj);
|
||||
}
|
||||
|
||||
if (!temp_config.enabled) {
|
||||
json_object_put(root);
|
||||
return 0; // Feature not enabled, don't load rules
|
||||
}
|
||||
|
||||
json_object *rules_array_obj;
|
||||
if (json_object_object_get_ex(root, "rules", &rules_array_obj) &&
|
||||
json_object_get_type(rules_array_obj) == json_type_array) {
|
||||
int rules_len = json_object_array_length(rules_array_obj);
|
||||
temp_config.rule_count = rules_len < MAX_RULES ? rules_len : MAX_RULES;
|
||||
|
||||
for (int i = 0; i < temp_config.rule_count; i++) {
|
||||
json_object *rule_obj =
|
||||
json_object_array_get_idx(rules_array_obj, i);
|
||||
json_object *cmd, *type, *msg, *args;
|
||||
|
||||
json_object_object_get_ex(rule_obj, "cmd", &cmd);
|
||||
json_object_object_get_ex(rule_obj, "type", &type);
|
||||
json_object_object_get_ex(rule_obj, "msg", &msg);
|
||||
|
||||
if (cmd)
|
||||
strncpy(temp_config.rules[i].cmd, json_object_get_string(cmd),
|
||||
sizeof(temp_config.rules[i].cmd) - 1);
|
||||
if (type)
|
||||
strncpy(temp_config.rules[i].type, json_object_get_string(type),
|
||||
sizeof(temp_config.rules[i].type) - 1);
|
||||
if (msg)
|
||||
strncpy(temp_config.rules[i].msg, json_object_get_string(msg),
|
||||
sizeof(temp_config.rules[i].msg) - 1);
|
||||
|
||||
// Parse args parameter
|
||||
temp_config.rules[i].arg_count = 0;
|
||||
if (json_object_object_get_ex(rule_obj, "args", &args) &&
|
||||
json_object_get_type(args) == json_type_array) {
|
||||
int args_len = json_object_array_length(args);
|
||||
temp_config.rules[i].arg_count =
|
||||
args_len < MAX_ARGS ? args_len
|
||||
: MAX_ARGS; // Limit to MAX_ARGS parameters
|
||||
|
||||
for (int j = 0; j < temp_config.rules[i].arg_count; j++) {
|
||||
json_object *arg_item = json_object_array_get_idx(args, j);
|
||||
if (arg_item) {
|
||||
strncpy(temp_config.rules[i].args[j],
|
||||
json_object_get_string(arg_item),
|
||||
sizeof(temp_config.rules[i].args[j]) - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_object_put(root);
|
||||
|
||||
// Copy the temporary configuration to shared memory
|
||||
memcpy(shared_config, &temp_config, sizeof(ConfigData));
|
||||
|
||||
DEBUG_LOG("Loaded %d rules to shared memory", shared_config->rule_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if the configuration file has been modified
|
||||
int config_file_modified() {
|
||||
struct stat file_stat;
|
||||
DEBUG_LOG("Checking if config file has been modified: %s", CONFIG_FILE);
|
||||
if (stat(CONFIG_FILE, &file_stat) != 0) {
|
||||
DEBUG_LOG("Cannot get stat for FILE: %s", CONFIG_FILE);
|
||||
return 0;
|
||||
}
|
||||
int isChanged = file_stat.st_mtime != last_modified_time;
|
||||
if (isChanged != 0) {
|
||||
DEBUG_LOG("Updating last_modified_time to: %ld", file_stat.st_mtime);
|
||||
last_modified_time = file_stat.st_mtime;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Load or reload configuration to shared memory if needed
|
||||
void load_config_if_needed() {
|
||||
if (shared_config == NULL) {
|
||||
// First load, create shared memory
|
||||
DEBUG_LOG("Creating shared memory for config data");
|
||||
shm_id = shmget(SHM_KEY, sizeof(ConfigData), IPC_CREAT | 0644);
|
||||
if (shm_id == -1) {
|
||||
perror("shmget failed");
|
||||
return;
|
||||
}
|
||||
shared_config = (ConfigData *)shmat(shm_id, NULL, 0);
|
||||
if (shared_config == (void *)-1) {
|
||||
perror("shmat failed");
|
||||
shared_config = NULL;
|
||||
return;
|
||||
}
|
||||
// Read config file on first load
|
||||
DEBUG_LOG("Loading config file for the first time");
|
||||
struct stat file_stat;
|
||||
if (stat(CONFIG_FILE, &file_stat) == 0) {
|
||||
last_modified_time = file_stat.st_mtime;
|
||||
load_config_to_shm();
|
||||
} else {
|
||||
DEBUG_LOG("Cannot get stat for FILE: %s", CONFIG_FILE);
|
||||
// Initialize an empty configuration
|
||||
shared_config->enabled = false;
|
||||
shared_config->rule_count = 0;
|
||||
}
|
||||
} else if (config_file_modified()) {
|
||||
DEBUG_LOG("Config file has been modified.");
|
||||
load_config_to_shm();
|
||||
} else {
|
||||
DEBUG_LOG("Config file has not been modified, skipping reload.");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include "exec_hook.h"
|
||||
|
||||
int load_config_to_shm();
|
||||
int config_file_modified();
|
||||
void load_config_if_needed();
|
||||
|
||||
#endif // CONFIG_H
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#ifdef DEBUG
|
||||
#include <execinfo.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void print_stacktrace() {
|
||||
void *buffer[100];
|
||||
int size = backtrace(buffer, 100);
|
||||
backtrace_symbols_fd(buffer, size, STDERR_FILENO);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <execinfo.h>
|
||||
|
||||
void print_stacktrace();
|
||||
#define DEBUG_LOG(fmt, ...) \
|
||||
fprintf(stderr, "[DEBUG][PID %d] %s:%d:%s(): " fmt "\n", getpid(), \
|
||||
__FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_LOG(fmt, ...) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif // DEBUG_H
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef EXEC_HOOK_H
|
||||
#define EXEC_HOOK_H
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <json-c/json.h>
|
||||
#include <pty.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include "struct.h"
|
||||
|
||||
#define CONFIG_FILE "/tmp/exec_hook/config/execve_rules.json"
|
||||
#define LOG_FILE "/tmp/exec_hook/logs/execve.log"
|
||||
#define LOG_OUT_FILE "/tmp/exec_hook/logs/execve_out.log"
|
||||
#define COMMAND_NOT_FOUND "/usr/lib/command-not-found"
|
||||
|
||||
#define ANSI_COLOR_RED "\033[31m"
|
||||
#define ANSI_COLOR_YELLOW "\033[33m"
|
||||
#define ANSI_COLOR_RESET "\033[0m"
|
||||
|
||||
#define SHM_KEY 12345
|
||||
|
||||
// Global variable, pointing to the configuration data in shared memory
|
||||
extern ConfigData *shared_config;
|
||||
extern int shm_id;
|
||||
extern time_t last_modified_time;
|
||||
// extern int is_initialized;
|
||||
|
||||
#endif // EXEC_HOOK_H
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
#include "execve_interceptor.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include "init_cleanup.h"
|
||||
#include "logging.h"
|
||||
#include "rules.h"
|
||||
#include "utils.h"
|
||||
|
||||
// Global variables (declared in exec_hook.h and defined here)
|
||||
ConfigData *shared_config = NULL;
|
||||
int shm_id = -1;
|
||||
time_t last_modified_time = 0;
|
||||
// int is_initialized = 0;
|
||||
|
||||
// Original pointer
|
||||
orig_execve_type orig_execve = NULL;
|
||||
|
||||
int execve(const char *filename, char *const argv[], char *const envp[]) {
|
||||
// if (!is_initialized) {
|
||||
// initialize();
|
||||
// }
|
||||
DEBUG_LOG("Intercepted execve for: %s", filename);
|
||||
DEBUG_LOG("argv[0] = %s", argv[0]);
|
||||
|
||||
orig_execve = (orig_execve_type)dlsym(RTLD_NEXT, "execve");
|
||||
if (orig_execve == NULL) {
|
||||
fprintf(stderr, "Error in dlsym(\"execve\"): %s\n", dlerror());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Load configuration (only if needed)
|
||||
load_config_if_needed();
|
||||
|
||||
// Intercept only when execve is called from a shell terminal
|
||||
if (!is_terminal_shell()) {
|
||||
DEBUG_LOG("Not a terminal shell, bypassing interception.");
|
||||
return orig_execve(filename, argv, envp);
|
||||
}
|
||||
|
||||
// Current configuration information
|
||||
DEBUG_LOG("Current Config rule count : %d", shared_config->rule_count);
|
||||
|
||||
// If shared memory was not successfully loaded, execute directly
|
||||
if (shared_config == NULL) {
|
||||
DEBUG_LOG("Shared memory not initialized, bypassing interception.");
|
||||
return orig_execve(filename, argv, envp);
|
||||
}
|
||||
|
||||
// If the feature is disabled, execute directly
|
||||
if (!shared_config->enabled) {
|
||||
DEBUG_LOG("Not enabled.");
|
||||
return orig_execve(filename, argv, envp);
|
||||
}
|
||||
|
||||
write_log(filename, argv);
|
||||
|
||||
const char *basename = argv[0];
|
||||
if (strcmp(filename, COMMAND_NOT_FOUND) == 0 && argv[2]) {
|
||||
basename = argv[2];
|
||||
}
|
||||
|
||||
// Special handling for commands executed via shell.posix
|
||||
// Execute directly, without rule matching and output redirection
|
||||
if (argv[1] != NULL && strcmp(argv[1], "shell.posix") == 0) {
|
||||
return orig_execve(filename, argv, envp);
|
||||
}
|
||||
|
||||
for (int i = 0; i < shared_config->rule_count; i++) {
|
||||
if (strcmp(basename, shared_config->rules[i].cmd) == 0 &&
|
||||
args_match(argv, &shared_config->rules[i])) {
|
||||
DEBUG_LOG("Rule matched: %s (type: %s)",
|
||||
shared_config->rules[i].cmd,
|
||||
shared_config->rules[i].type);
|
||||
if (strcmp(shared_config->rules[i].type, "warn") == 0) {
|
||||
printf(ANSI_COLOR_YELLOW "[Warning] %s\n" ANSI_COLOR_RESET,
|
||||
shared_config->rules[i].msg);
|
||||
printf("按下 'Y' 继续执行, 或按任意键取消: ");
|
||||
char input = getchar();
|
||||
if (input != 'Y' && input != 'y') {
|
||||
printf("\nExecution cancelled.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
// return -1;
|
||||
}
|
||||
printf("\nContinuing execution...\n");
|
||||
} else if (strcmp(shared_config->rules[i].type, "error") == 0) {
|
||||
printf(ANSI_COLOR_RED "[Error] %s" ANSI_COLOR_RESET "\n",
|
||||
shared_config->rules[i].msg);
|
||||
exit(EXIT_FAILURE);
|
||||
// return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Duplicate stdout and stderr to the log file
|
||||
duplicate_output_to_log();
|
||||
|
||||
return orig_execve(filename, argv, envp);
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef EXECVE_INTERCEPTOR_H
|
||||
#define EXECVE_INTERCEPTOR_H
|
||||
|
||||
#include "exec_hook.h"
|
||||
|
||||
// Original execve type
|
||||
typedef int (*orig_execve_type)(const char *filename, char *const argv[],
|
||||
char *const envp[]);
|
||||
|
||||
extern orig_execve_type orig_execve;
|
||||
|
||||
int execve(const char *filename, char *const argv[], char *const envp[]);
|
||||
|
||||
#endif // EXECVE_INTERCEPTOR_H
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#include "init_cleanup.h"
|
||||
#include "exec_hook.h"
|
||||
#include "debug.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/shm.h>
|
||||
|
||||
// Global variables (defined in execve_interceptor.c)
|
||||
extern ConfigData *shared_config;
|
||||
extern int shm_id;
|
||||
|
||||
// // Constructor, executed when the library is loaded
|
||||
// __attribute__((constructor)) static void initialize() {
|
||||
// if (is_initialized) return;
|
||||
// is_initialized = 1;
|
||||
// DEBUG_LOG("Initializing execve_intercept library.");
|
||||
// load_config_if_needed();
|
||||
// // orig_execve is initialized in the execve function itself
|
||||
// }
|
||||
|
||||
// Destructor, executed when the library is unloaded
|
||||
__attribute__((destructor)) static void cleanup_shared_memory() {
|
||||
DEBUG_LOG("execve_intercept library unloaded.");
|
||||
// Log output paths
|
||||
DEBUG_LOG("Log file: %s", LOG_FILE);
|
||||
DEBUG_LOG("Log out file: %s", LOG_OUT_FILE);
|
||||
DEBUG_LOG("Config file: %s", CONFIG_FILE);
|
||||
DEBUG_LOG("Shared memory ID: %d", shm_id);
|
||||
if (shared_config != NULL) {
|
||||
DEBUG_LOG("Cleaning up shared memory.");
|
||||
// Detach shared memory segment
|
||||
if (shmdt(shared_config) == -1) {
|
||||
perror("shmdt failed");
|
||||
}
|
||||
shared_config = NULL;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
print_stacktrace();
|
||||
#endif
|
||||
// Note: We don't delete the shared memory segment here, as it might be
|
||||
// used by other processes. A separate mechanism would be needed to manage
|
||||
// the lifecycle of the shared memory if deletion is required.
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef INIT_CLEANUP_H
|
||||
#define INIT_CLEANUP_H
|
||||
|
||||
// void initialize();
|
||||
__attribute__((destructor)) static void cleanup_shared_memory();
|
||||
|
||||
#endif // INIT_CLEANUP_H
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
#include "logging.h"
|
||||
#include "exec_hook.h"
|
||||
#include "debug.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
|
||||
// Write log
|
||||
void write_log(const char *filename, char *const argv[]) {
|
||||
DEBUG_LOG("Writing exec log for command: %s", filename);
|
||||
time_t now;
|
||||
time(&now);
|
||||
|
||||
FILE *log = fopen(LOG_FILE, "a");
|
||||
if (!log) return;
|
||||
|
||||
fprintf(log, "[%s] Command: %s\n", ctime(&now), filename);
|
||||
|
||||
for (int i = 0; argv[i]; i++) {
|
||||
fprintf(log, "arg[%d]: %s\n", i, argv[i]);
|
||||
}
|
||||
|
||||
fclose(log);
|
||||
}
|
||||
|
||||
// Duplicate stdout/stderr to log file, while preserving terminal colors
|
||||
void duplicate_output_to_log() {
|
||||
DEBUG_LOG("Duplicating stdout/stderr to log file: %s", LOG_OUT_FILE);
|
||||
|
||||
int log_fd = open(LOG_OUT_FILE, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||
if (log_fd == -1) {
|
||||
perror("Failed to open log file");
|
||||
return;
|
||||
}
|
||||
|
||||
int master_fd, slave_fd;
|
||||
if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) == -1) {
|
||||
perror("openpty failed");
|
||||
close(log_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("fork failed");
|
||||
close(log_fd);
|
||||
close(master_fd);
|
||||
close(slave_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0) { // Child process
|
||||
close(master_fd);
|
||||
|
||||
// Connect slave_fd to standard input/output/error
|
||||
dup2(slave_fd, STDIN_FILENO);
|
||||
dup2(slave_fd, STDOUT_FILENO);
|
||||
dup2(slave_fd, STDERR_FILENO);
|
||||
close(slave_fd);
|
||||
|
||||
// Child process does not output, only keeps the environment for execve
|
||||
return; // Will be replaced by the original execve call
|
||||
}
|
||||
|
||||
// Parent process (controller): read from master_fd and write to stdout + log
|
||||
close(slave_fd);
|
||||
|
||||
// Ignore child process exit signal
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
char buffer[1024];
|
||||
ssize_t n;
|
||||
int has_error = 0;
|
||||
|
||||
while ((n = read(master_fd, buffer, sizeof(buffer))) > 0) {
|
||||
// Check for errors
|
||||
if (memmem(buffer, n, "error", 5) || memmem(buffer, n, "Error", 5) ||
|
||||
memmem(buffer, n, "ERROR", 5)) {
|
||||
has_error = 1;
|
||||
}
|
||||
|
||||
// Write to log
|
||||
if (write(log_fd, buffer, n) == -1) {
|
||||
perror("Failed to write to log file");
|
||||
}
|
||||
}
|
||||
|
||||
if (has_error) {
|
||||
printf("\n检测到命令执行出错,已经上报北冥论坛~ \n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
close(master_fd);
|
||||
close(log_fd);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef LOGGING_H
|
||||
#define LOGGING_H
|
||||
|
||||
void write_log(const char *filename, char *const argv[]);
|
||||
void duplicate_output_to_log();
|
||||
|
||||
#endif // LOGGING_H
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#include "rules.h"
|
||||
#include "debug.h"
|
||||
#include <string.h>
|
||||
|
||||
// Check if args match
|
||||
int args_match(char *const argv[], Rule *rule) {
|
||||
DEBUG_LOG("Matching args for rule with cmd: %s", rule->cmd);
|
||||
if (rule->arg_count == 0) {
|
||||
return 1; // No args constraint, so it matches
|
||||
}
|
||||
|
||||
for (int i = 0; i < rule->arg_count; i++) {
|
||||
int found = 0;
|
||||
for (int j = 1; argv[j] != NULL; j++) { // Skip argv[0] (command itself)
|
||||
if (strcmp(argv[j], rule->args[i]) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) return 0; // If any argument doesn't match, the rule doesn't apply
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef RULES_H
|
||||
#define RULES_H
|
||||
|
||||
#include "exec_hook.h"
|
||||
|
||||
int args_match(char *const argv[], Rule *rule);
|
||||
|
||||
#endif // RULES_H
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef STRUCT_H
|
||||
#define STRUCT_H
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#define MAX_RULES 100
|
||||
#define MAX_ARGS 10
|
||||
|
||||
typedef struct {
|
||||
char cmd[256];
|
||||
char type[32];
|
||||
char msg[1024];
|
||||
char args[MAX_ARGS][256];
|
||||
int arg_count;
|
||||
} Rule;
|
||||
|
||||
typedef struct {
|
||||
bool enabled;
|
||||
Rule rules[MAX_RULES];
|
||||
int rule_count;
|
||||
} ConfigData;
|
||||
|
||||
#endif // STRUCT_H
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// Determine if the parent process is a terminal shell (bash, zsh, fish, etc.)
|
||||
int is_terminal_shell() {
|
||||
pid_t ppid = getppid();
|
||||
char path[64], proc_name[256];
|
||||
FILE *file;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/comm", ppid);
|
||||
file = fopen(path, "r");
|
||||
if (!file) return 0;
|
||||
|
||||
if (fgets(proc_name, sizeof(proc_name), file)) {
|
||||
proc_name[strcspn(proc_name, "\n")] = 0; // Remove newline
|
||||
if (strcmp(proc_name, "bash") == 0 || strcmp(proc_name, "zsh") == 0 ||
|
||||
strcmp(proc_name, "fish") == 0 || strcmp(proc_name, "sh") == 0) {
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if a string is an ANSI escape sequence
|
||||
int is_ansi_escape_sequence(const char *str) {
|
||||
// Common format for ANSI escape sequences is ESC followed by '['
|
||||
return str[0] == '\033' && str[1] == '[';
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
int is_terminal_shell();
|
||||
int is_ansi_escape_sequence(const char *str);
|
||||
|
||||
#endif // UTILS_H
|
||||
|
|
@ -35,5 +35,5 @@ echo -e "${CYAN}=============================================${RESET}"
|
|||
HOOK_EXEC_PATH=/tmp/exec_hook/intercept.so
|
||||
|
||||
rm -rf $HOOK_EXEC_PATH
|
||||
cp ./intercept.so $HOOK_EXEC_PATH
|
||||
cp ./build/intercept.so $HOOK_EXEC_PATH
|
||||
LD_PRELOAD=$HOOK_EXEC_PATH bash
|
||||
|
|
|
|||
Loading…
Reference in New Issue