From 2256a6ce3bb5f8d6a899fc3ce0ce023eafc3af63 Mon Sep 17 00:00:00 2001 From: "QCQCQC@Ubuntu" <1220204124@zust.edu.cn> Date: Mon, 7 Apr 2025 14:53:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9write=E4=B9=9F=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E4=BA=86hook=EF=BC=8C=E5=B9=B6=E4=B8=94=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E4=BA=86=E5=88=9D=E5=A7=8B=E5=8C=96=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- execve_intercept.c | 127 +++++++++++++++++++++------------------------ intercept.so | Bin 26768 -> 26672 bytes 2 files changed, 58 insertions(+), 69 deletions(-) diff --git a/execve_intercept.c b/execve_intercept.c index 1fb13a4..e423a30 100644 --- a/execve_intercept.c +++ b/execve_intercept.c @@ -178,75 +178,34 @@ int is_ansi_escape_sequence(const char *str) { return str[0] == '\033' && str[1] == '['; } -// 复制标准输出和错误输出到日志文件 -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; - } +// 保存原始的 write 函数指针 +static ssize_t (*original_write)(int fd, const void *buf, size_t count) = NULL; - int pipe_fds[2]; - if (pipe(pipe_fds) == -1) { - perror("Failed to create pipe"); - close(log_fd); - return; - } +// 保存日志文件描述符 +static int log_fd = -1; - pid_t pid = fork(); - if (pid == -1) { - perror("Failed to fork"); - close(log_fd); - close(pipe_fds[0]); - close(pipe_fds[1]); - return; - } +// 我们自己的 write 函数 +ssize_t write(int fd, const void *buf, size_t count) { + ssize_t result = -1; - if (pid == 0) { // 子进程:读取 pipe,并写入日志 - close(pipe_fds[1]); // 关闭写端 - char buffer[1024]; - ssize_t n; - int has_error = 0; - - while ((n = read(pipe_fds[0], buffer, sizeof(buffer))) > 0) { - // 检查buffer中是否包含错误信息 - if (strstr(buffer, "error") || strstr(buffer, "Error") || - strstr(buffer, "ERROR")) { - has_error = 1; - } - - // 输出到终端时保留颜色 - if (isatty(STDOUT_FILENO)) { - 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"); - } + 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 的返回值 } - - if (has_error) { - printf("\n检测到命令执行出错,已经上报北冥论坛~ \n"); - fflush(stdout); // 确保提示文字被输出 - } - - close(pipe_fds[0]); - close(log_fd); - _exit(0); - } else { // 父进程:写入 pipe - close(pipe_fds[0]); // 关闭读端 - dup2(pipe_fds[1], STDOUT_FILENO); - dup2(pipe_fds[1], STDERR_FILENO); - close(pipe_fds[1]); - close(log_fd); } + 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(); @@ -326,8 +285,6 @@ int execve(const char *filename, char *const argv[], char *const envp[]) { // 仅在 shell 终端调用 execve 时拦截 if (!is_terminal_shell()) { DEBUG_LOG("Not a terminal shell, bypassing interception."); - orig_execve_type orig_execve = - (orig_execve_type)dlsym(RTLD_NEXT, "execve"); return orig_execve(filename, argv, envp); } @@ -340,16 +297,12 @@ int execve(const char *filename, char *const argv[], char *const envp[]) { // 如果共享内存未成功加载,则直接执行 if (shared_config == NULL) { DEBUG_LOG("Shared memory not initialized, bypassing interception."); - orig_execve_type orig_execve = - (orig_execve_type)dlsym(RTLD_NEXT, "execve"); return orig_execve(filename, argv, envp); } // 如果功能被禁用,则直接执行 if (!shared_config->enabled) { DEBUG_LOG("Not enabled."); - orig_execve_type orig_execve = - (orig_execve_type)dlsym(RTLD_NEXT, "execve"); return orig_execve(filename, argv, envp); } @@ -363,8 +316,6 @@ int execve(const char *filename, char *const argv[], char *const envp[]) { // 特殊处理以 shell.posix // 方式执行的命令,直接执行,不进行规则匹配和输出重定向 if (argv[1] != NULL && strcmp(argv[1], "shell.posix") == 0) { - orig_execve_type orig_execve = - (orig_execve_type)dlsym(RTLD_NEXT, "execve"); return orig_execve(filename, argv, envp); } @@ -394,12 +345,37 @@ int execve(const char *filename, char *const argv[], char *const envp[]) { } // 复制 stdout 和 stderr 到日志文件 - duplicate_output_to_log(); + // duplicate_output_to_log(); - orig_execve_type orig_execve = (orig_execve_type)dlsym(RTLD_NEXT, "execve"); 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); + } + + // 打开日志文件,以追加模式打开,如果不存在则创建 + 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("Cleaning up shared memory."); @@ -409,7 +385,20 @@ __attribute__((destructor)) static void cleanup_shared_memory() { } 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); +} diff --git a/intercept.so b/intercept.so index 2d551b7387f4069467bf32b742b66d7a1b26598f..2c48af2822ff07c6288e4fa3613e0c1481628bad 100755 GIT binary patch literal 26672 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 np1uE+gVN_h)?@)Ut9s_XSeQf8J&&nzsY&-N7j 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 sz)$&C`J(R|D(|>7kJ8)3