仅拦截终端调用,仅在第一次启动和变化时加载配置

This commit is contained in:
Pan Qiancheng 2025-03-26 10:18:18 +08:00
parent 1da5206101
commit d914136ce4
3 changed files with 176 additions and 34 deletions

View File

@ -1,11 +1,12 @@
#define _GNU_SOURCE
#include <dlfcn.h>
#include <json-c/json.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <json-c/json.h>
#include <unistd.h>
#define CONFIG_FILE "./config/execve_rules.json"
#define LOG_FILE "./logs/execve.log"
@ -24,10 +25,8 @@ typedef struct {
int arg_count;
} Rule;
typedef int (*orig_execve_type)(const char *filename, char *const argv[], char *const envp[]);
// 加载配置规则
Rule* load_rules(int *rule_count) {
Rule *load_rules(int *rule_count) {
json_object *root = json_object_from_file(CONFIG_FILE);
if (!root) {
fprintf(stderr, "Failed to parse JSON from %s\n", CONFIG_FILE);
@ -57,13 +56,16 @@ Rule* load_rules(int *rule_count) {
// 解析 args 参数
rules[i].arg_count = 0;
if (json_object_object_get_ex(rule, "args", &args) && json_object_get_type(args) == json_type_array) {
if (json_object_object_get_ex(rule, "args", &args) &&
json_object_get_type(args) == json_type_array) {
int args_len = json_object_array_length(args);
rules[i].arg_count = args_len < 10 ? args_len : 10; // 限制最多 10 个参数
rules[i].arg_count =
args_len < 10 ? args_len : 10; // 限制最多 10 个参数
for (int j = 0; j < rules[i].arg_count; j++) {
json_object *arg_item = json_object_array_get_idx(args, j);
strncpy(rules[i].args[j], json_object_get_string(arg_item), 255);
strncpy(rules[i].args[j], json_object_get_string(arg_item),
255);
}
}
}
@ -80,13 +82,13 @@ int args_match(char *const argv[], Rule *rule) {
for (int i = 0; i < rule->arg_count; i++) {
int found = 0;
for (int j = 1; argv[j] != NULL; j++) { // 跳过 argv[0] (命令本身)
for (int j = 1; argv[j] != NULL; j++) { // 跳过 argv[0] (命令本身)
if (strcmp(argv[j], rule->args[i]) == 0) {
found = 1;
break;
}
}
if (!found) return 0; // 只要有一个参数没有匹配,则不符合规则
if (!found) return 0; // 只要有一个参数没有匹配,则不符合规则
}
return 1;
}
@ -108,54 +110,139 @@ void write_log(const char *filename, char *const argv[]) {
fclose(log);
}
typedef int (*orig_execve_type)(const char *filename, char *const argv[],
char *const envp[]);
static Rule *rules = NULL;
static int rule_count = 0;
static time_t last_modified_time = 0;
// 判断父进程是否为终端 shell (bash, zsh, fish 等)
int is_terminal_shell() {
pid_t ppid = getppid();
char path[64], proc_name[256];
FILE *file;
snprintf(path, sizeof(path), "/proc/%d/comm", ppid);
file = fopen(path, "r");
if (!file) return 0;
if (fgets(proc_name, sizeof(proc_name), file)) {
proc_name[strcspn(proc_name, "\n")] = 0; // 去除换行符
if (strcmp(proc_name, "bash") == 0 || strcmp(proc_name, "zsh") == 0 ||
strcmp(proc_name, "fish") == 0 || strcmp(proc_name, "sh") == 0) {
fclose(file);
return 1;
}
}
fclose(file);
return 0;
}
// 检查配置文件是否已修改
int config_file_modified() {
struct stat file_stat;
if (stat(CONFIG_FILE, &file_stat) != 0) {
return 0;
}
return file_stat.st_mtime != last_modified_time;
}
// 加载或重新加载规则
void load_rules_if_needed() {
if (!rules || config_file_modified()) {
json_object *root = json_object_from_file(CONFIG_FILE);
if (!root) {
fprintf(stderr, "Failed to parse JSON from %s\n", CONFIG_FILE);
return;
}
if (rules) {
free(rules);
}
rule_count = json_object_array_length(root);
rules = malloc(sizeof(Rule) * rule_count);
if (!rules) {
json_object_put(root);
rule_count = 0;
return;
}
for (int i = 0; i < rule_count; i++) {
json_object *rule = json_object_array_get_idx(root, i);
json_object *cmd, *type, *msg, *args;
json_object_object_get_ex(rule, "cmd", &cmd);
json_object_object_get_ex(rule, "type", &type);
json_object_object_get_ex(rule, "msg", &msg);
strncpy(rules[i].cmd, json_object_get_string(cmd), 255);
strncpy(rules[i].type, json_object_get_string(type), 31);
strncpy(rules[i].msg, json_object_get_string(msg), 1023);
// 解析 args 参数
rules[i].arg_count = 0;
if (json_object_object_get_ex(rule, "args", &args) &&
json_object_get_type(args) == json_type_array) {
int args_len = json_object_array_length(args);
rules[i].arg_count = args_len < 10 ? args_len : 10;
for (int j = 0; j < rules[i].arg_count; j++) {
json_object *arg_item = json_object_array_get_idx(args, j);
strncpy(rules[i].args[j], json_object_get_string(arg_item),
255);
}
}
}
json_object_put(root);
struct stat file_stat;
if (stat(CONFIG_FILE, &file_stat) == 0) {
last_modified_time = file_stat.st_mtime;
}
}
}
int execve(const char *filename, char *const argv[], char *const envp[]) {
int rule_count;
Rule *rules = load_rules(&rule_count);
if (!rules) {
return -1;
// 仅在 shell 终端调用 execve 时拦截
if (!is_terminal_shell()) {
orig_execve_type orig_execve =
(orig_execve_type)dlsym(RTLD_NEXT, "execve");
return orig_execve(filename, argv, envp);
}
const char *basename = argv[0]; // 直接使用 argv[0]
// 加载规则(仅在需要时)
load_rules_if_needed();
// 处理 command-not-found 情况
const char *basename = argv[0];
if (strcmp(filename, COMMAND_NOT_FOUND) == 0 && argv[2]) {
basename = argv[2]; // 解析出真正要执行的命令
basename = argv[2];
}
// 检查规则匹配
for (int i = 0; i < rule_count; i++) {
if (strcmp(basename, rules[i].cmd) == 0 && args_match(argv, &rules[i])) {
if (strcmp(basename, rules[i].cmd) == 0 &&
args_match(argv, &rules[i])) {
if (strcmp(rules[i].type, "warn") == 0) {
printf(ANSI_COLOR_YELLOW "[Warning] %s\n" ANSI_COLOR_RESET, rules[i].msg);
printf(ANSI_COLOR_YELLOW "[Warning] %s\n" ANSI_COLOR_RESET,
rules[i].msg);
printf("按下 'Y' 继续执行, 或按任意键取消: ");
char input = getchar();
if (input != 'Y' && input != 'y') {
printf("\nExecution cancelled.\n");
free(rules);
return -1;
}
printf("\nContinuing execution...\n");
} else if (strcmp(rules[i].type, "error") == 0) {
printf(ANSI_COLOR_RED "[Error] %s" ANSI_COLOR_RESET "\n", rules[i].msg);
free(rules);
printf(ANSI_COLOR_RED "[Error] %s" ANSI_COLOR_RESET "\n",
rules[i].msg);
return -1;
}
break;
}
}
// 记录日志
write_log(filename, argv);
// 调用原始 execve
orig_execve_type orig_execve = (orig_execve_type)dlsym(RTLD_NEXT, "execve");
if (!orig_execve) {
fprintf(stderr, "Error: %s\n", dlerror());
free(rules);
return -1;
}
free(rules);
return orig_execve(filename, argv, envp);
}

Binary file not shown.

View File

@ -133,3 +133,58 @@ arg[1]: -b
arg[0]: /usr/lib/command-not-found
arg[1]: --
arg[2]: pip
[Wed Mar 26 10:09:14 2025
] Command: /usr/bin/lesspipe
arg[0]: lesspipe
[Wed Mar 26 10:09:14 2025
] Command: /usr/bin/basename
arg[0]: basename
arg[1]: /usr/bin/lesspipe
[Wed Mar 26 10:09:14 2025
] Command: /usr/bin/dirname
arg[0]: dirname
arg[1]: /usr/bin/lesspipe
[Wed Mar 26 10:09:14 2025
] Command: /usr/bin/dircolors
arg[0]: dircolors
arg[1]: -b
[Wed Mar 26 10:09:15 2025
] Command: /usr/bin/ls
arg[0]: ls
arg[1]: --color=auto
[Wed Mar 26 10:09:17 2025
] Command: /usr/bin/ls
arg[0]: ls
arg[1]: --color=auto
arg[2]: -alF
[Wed Mar 26 10:09:22 2025
] Command: /usr/lib/command-not-found
arg[0]: /usr/lib/command-not-found
arg[1]: --
arg[2]: pip
[Wed Mar 26 10:09:27 2025
] Command: /usr/lib/command-not-found
arg[0]: /usr/lib/command-not-found
arg[1]: --
arg[2]: pip
[Wed Mar 26 10:16:58 2025
] Command: /usr/bin/lesspipe
arg[0]: lesspipe
[Wed Mar 26 10:16:58 2025
] Command: /usr/bin/dircolors
arg[0]: dircolors
arg[1]: -b
[Wed Mar 26 10:16:59 2025
] Command: /usr/bin/ls
arg[0]: ls
arg[1]: --color=auto
[Wed Mar 26 10:17:04 2025
] Command: /usr/lib/command-not-found
arg[0]: /usr/lib/command-not-found
arg[1]: --
arg[2]: python
[Wed Mar 26 10:17:10 2025
] Command: /usr/lib/command-not-found
arg[0]: /usr/lib/command-not-found
arg[1]: --
arg[2]: pip