对write也进行了hook,并且提供了初始化函数

This commit is contained in:
Pan Qiancheng 2025-04-07 14:53:14 +08:00
parent e6568b3d81
commit 2256a6ce3b
2 changed files with 58 additions and 69 deletions

View File

@ -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");
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 (write(log_fd, buffer, n) == -1) {
perror("Failed to write to log file");
}
}
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);
}

Binary file not shown.