From 6e79d980c524d6a6a97d968fd1dc7a93aa59aca8 Mon Sep 17 00:00:00 2001 From: "QCQCQC@Ubuntu" <1220204124@zust.edu.cn> Date: Mon, 7 Apr 2025 19:03:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8pty=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=BA=86=E9=87=8D=E5=AE=9A=E5=90=91=E8=BE=93=E5=87=BA=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- execve_intercept.c | 222 ++++++++++++++++++++++++++++++++------------- intercept.so | Bin 26672 -> 26856 bytes tests/a.out | Bin 0 -> 16248 bytes tests/dup2.c | 27 ++++++ tests/output.txt | 0 5 files changed, 187 insertions(+), 62 deletions(-) create mode 100755 tests/a.out create mode 100644 tests/dup2.c create mode 100644 tests/output.txt 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 2c48af2822ff07c6288e4fa3613e0c1481628bad..813ed79d385795454c1fb39cc8421bc5e2c9d49f 100755 GIT binary patch literal 26856 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 literal 26672 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 +#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