diff --git a/execve_intercept.c b/execve_intercept.c index e423a30..64c8ca6 100644 --- a/execve_intercept.c +++ b/execve_intercept.c @@ -1,17 +1,23 @@ #define _GNU_SOURCE #include #include +#include // 添加 errno 相关定义 #include +#include // 添加 fcntl 相关定义 #include +#include // 添加 SIGCHLD 相关定义 #include // 引入 bool 类型 #include #include #include #include +#include // 添加 select 相关定义 #include #include #include #include +#include +#include #ifdef DEBUG @@ -55,6 +61,7 @@ typedef struct { 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() { @@ -178,27 +185,28 @@ int is_ansi_escape_sequence(const char *str) { return str[0] == '\033' && str[1] == '['; } -// 保存原始的 write 函数指针 -static ssize_t (*original_write)(int fd, const void *buf, size_t count) = NULL; +// // 保存原始的 write 函数指针 +// static ssize_t (*original_write)(int fd, const void *buf, size_t count) = +// NULL; -// 保存日志文件描述符 -static int log_fd = -1; +// // 保存日志文件描述符 +// static int log_fd = -1; -// 我们自己的 write 函数 -ssize_t write(int fd, const void *buf, size_t count) { - ssize_t result = -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; -} +// 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[]); @@ -231,6 +239,7 @@ int is_terminal_shell() { // 检查配置文件是否已修改 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; @@ -248,6 +257,7 @@ int config_file_modified() { 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"); @@ -260,6 +270,7 @@ void load_config_if_needed() { 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; @@ -278,19 +289,105 @@ void load_config_if_needed() { } } +// 复制 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); } - // 加载配置(仅在需要时) - load_config_if_needed(); - // 当前配置信息 DEBUG_LOG("Current Config rule count : %d", shared_config->rule_count); @@ -345,60 +442,61 @@ int execve(const char *filename, char *const argv[], char *const envp[]) { } // 复制 stdout 和 stderr 到日志文件 - // duplicate_output_to_log(); + duplicate_output_to_log(); return orig_execve(filename, argv, envp); } -// 构造函数,在库被加载时执行 -__attribute__((constructor)) static void initialize() { - 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); - } +// // 构造函数,在库被加载时执行 +// __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); - } +// // // 打开日志文件,以追加模式打开,如果不存在则创建 +// // 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); - } -} +// 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("Cleaning up shared memory."); - if (shared_config != NULL) { - if (shmdt(shared_config) == -1) { - perror("shmdt failed"); - } - shared_config = NULL; - } - if (log_fd != -1) { - DEBUG_LOG("Closing log file descriptor."); - close(log_fd); - } - // 注意:这里不删除共享内存段,因为可能被其他进程使用。 - // 如果需要删除,需要一个明确的机制来判断是否是最后一个使用者。 - // 例如,可以创建一个单独的工具来管理共享内存的生命周期。 -} - -__attribute__((destructor)) static void debug_log() { 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; + } + // if (log_fd != -1) { + // DEBUG_LOG("Closing log file descriptor."); + // close(log_fd); + // } + // 注意:这里不删除共享内存段,因为可能被其他进程使用。 + // 如果需要删除,需要一个明确的机制来判断是否是最后一个使用者。 + // 例如,可以创建一个单独的工具来管理共享内存的生命周期。 } diff --git a/intercept.so b/intercept.so index 2c48af2..813ed79 100755 Binary files a/intercept.so and b/intercept.so differ diff --git a/tests/a.out b/tests/a.out new file mode 100755 index 0000000..727cc87 Binary files /dev/null and b/tests/a.out differ diff --git a/tests/dup2.c b/tests/dup2.c new file mode 100644 index 0000000..0fd110b --- /dev/null +++ b/tests/dup2.c @@ -0,0 +1,27 @@ +#include +#include +#include + +int main() { + // 打开输出文件 + int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); + + // 保存原始的标准输出 + int stdout_copy = dup(STDOUT_FILENO); + + // 复制文件描述符 + dup2(fd, STDOUT_FILENO); + + // 关闭原始的文件描述符 + close(fd); + + // 恢复标准输出 + dup2(stdout_copy, STDOUT_FILENO); + + char *const argv[] = {"ls", "--color=always", "--l", NULL}; + extern char **environ; + + execve("/usr/bin/ls", argv, environ); + + return 0; +} diff --git a/tests/output.txt b/tests/output.txt new file mode 100644 index 0000000..e69de29