使用pty修复了重定向输出的问题
This commit is contained in:
parent
2256a6ce3b
commit
6e79d980c5
|
|
@ -1,17 +1,23 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <errno.h> // 添加 errno 相关定义
|
||||
#include <fcntl.h>
|
||||
#include <fcntl.h> // 添加 fcntl 相关定义
|
||||
#include <json-c/json.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>
|
||||
#include <pty.h>
|
||||
#include <signal.h>
|
||||
|
||||
#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);
|
||||
// }
|
||||
// 注意:这里不删除共享内存段,因为可能被其他进程使用。
|
||||
// 如果需要删除,需要一个明确的机制来判断是否是最后一个使用者。
|
||||
// 例如,可以创建一个单独的工具来管理共享内存的生命周期。
|
||||
}
|
||||
|
|
|
|||
BIN
intercept.so
BIN
intercept.so
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,27 @@
|
|||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
Loading…
Reference in New Issue