# Execve Hook Project 本项目是一个基于 `LD_PRELOAD` 机制的系统调用拦截库,主要用于监控和控制 Linux 系统中的命令执行(`execve`)。它能够拦截命令执行请求,检查配置规则,记录执行日志,甚至通过 PTY(伪终端)捕获命令的输入输出。 ## 核心逻辑概述 整个 C 代码库的逻辑可以分为以下几个核心模块: ### 1. `execve` 拦截器 (`src/execve_interceptor.c`) 这是项目的核心入口。 - **拦截机制**: 通过 `LD_PRELOAD` 预加载动态库,覆盖系统默认的 `execve` 函数。 - **主要流程**: 1. **环境清理**: 在调用前移除 `LD_PRELOAD` 环境变量,防止对子进程造成非预期的递归影响。 2. **配置加载**: 调用 `config.c` 从共享内存中加载配置。 3. **规则匹配**: 使用 `rules.c` 检查当前执行的命令是否命中特定规则。 4. **行为决策**: 根据规则决定是否需要拦截、记录日志或通过 PTY 执行。 5. **执行原函数**: 最终调用原始的 `execve` 系统调用执行目标程序。 ```mermaid sequenceDiagram participant User as User/Shell participant Hook as execve_interceptor participant Config as Config (Shm) participant Rules as Rules Engine participant PTY as PTY/IO Handler participant Sys as Original execve User->>Hook: execve(filename, argv, envp) Hook->>Hook: Remove LD_PRELOAD Hook->>Hook: dlsym(RTLD_NEXT, "execve") Hook->>Config: load_config() Config-->>Hook: ConfigData* alt Not Terminal Shell or Config Disabled Hook->>Sys: orig_execve(...) end loop Check Rules Hook->>Rules: args_match(argv, rule) Rules-->>Hook: Match Result end alt Rule Type: SKIP Hook->>Sys: orig_execve(...) else Rule Type: WARN Hook->>User: Print Warning User->>Hook: Confirm (Y/N) alt No Hook->>User: Exit end else Rule Type: ERROR Hook->>User: Print Error Hook->>User: Exit else Rule Type: LOG/DEFAULT (Match Found) Hook->>PTY: dupIO(filename, argv, envp) PTY->>PTY: forkpty() par Child Process PTY->>Sys: orig_execve(...) and Parent Process PTY->>PTY: handle_io() (Capture Output) PTY->>User: Forward Output end else No Match Hook->>Sys: orig_execve(...) end ``` ### 2. 配置管理 (`src/config.c`) - **共享内存**: 配置数据不直接从文件读取,而是从共享内存(Shared Memory)中加载。 - **机制**: 这意味着有一个外部进程(通常是配套的 Go 服务)负责解析配置文件(如 JSON/YAML)并将结构化数据写入共享内存,C 代码只需高效读取即可。 ### 3. 规则引擎 (`src/rules.c`) - **功能**: 负责解析和匹配命令执行规则。 - **匹配逻辑**: 检查命令名称(`filename`)以及参数列表(`argv`)是否符合预定义的条件。 ### 4. I/O 捕获与 PTY (`src/pty_dup.c`) - **目的**: 为了完整记录交互式命令的输入输出(Session Recording)。 - **实现**: - 使用 `forkpty` 创建一个新的伪终端会话。 - 在子进程中执行目标命令。 - 父进程负责在主终端和伪终端之间转发数据(`stdin`, `stdout`, `stderr`)。 - 单独处理 `stderr` 管道以区分标准输出和错误输出。 - 处理信号(如 `SIGINT`, `SIGCHLD`)以确保子进程正确退出。 ```mermaid flowchart TD A[dupIO Called] --> B{forkpty} B -- Error --> C[Exit] B -- Child Process --> D[Setup Signals] D --> E[Close Pipe Read End] E --> F[Dup2 Stderr -> Pipe Write End] F --> G[Return to execve_interceptor] G --> H[Call orig_execve] B -- Parent Process --> I[Close Pipe Write End] I --> J[Get Log File Paths] J --> K[handle_io Loop] subgraph IO_Loop [IO Handling Loop] L{Select/Poll} L -- Stdin Data --> M[Write to Master PTY] L -- Master PTY Data --> N[Read Output] N --> O[Write to Stdout] N --> P[Write to Log File] L -- Stderr Pipe Data --> Q[Read Stderr] Q --> R[Write to Stderr] Q --> S[Write to Log File] end K --> IO_Loop IO_Loop -- Child Exit --> T[Check Exit Status] T -- Error Code --> U[Send Params to Socket] T -- Success --> V[Exit Parent] ``` ### 5. Write 调用拦截 (`src/hook_write.c`) 这是一个独立的模块(编译为 `hook_write.so`),用于更细粒度的输出捕获。 - **拦截对象**: `write`, `writev`, `fwrite`, `puts`, `printf` 等标准输出函数。 - **功能**: 将进程写入 `stdout` 或 `stderr` 的内容同时写入到一个指定的日志文件中。 - **用途**: 即使不使用 PTY,也能捕获程序的输出内容。 ```mermaid flowchart LR A[Application] -->|Calls write/printf| B(Hook Function) B --> C{Log FD Open?} C -- Yes --> D[Write to Log File] C -- No --> E[Skip Log] D --> F[Call Original Function] E --> F F --> G[Output to Terminal] ``` ### 6. 进程间通信与错误上报 (`src/client.c`) - **方式**: Unix Domain Socket (`/etc/exec_hook/exec.sock`)。 - **作用**: 将拦截到的执行信息(文件名、参数、环境变量、日志路径等)发送给后端服务(Go Service)。 - **流程**: 在 `execve` 拦截阶段,如果需要上报,会通过此模块连接 Socket 并发送数据。 #### 错误上报机制 当被拦截的命令执行失败(退出码非 0 或被信号终止)时,系统会触发错误上报流程。 1. **执行与捕获**: `pty_dup.c` 中的 `handle_io` 循环负责实时捕获子进程的 `stdout` 和 `stderr`。 2. **日志记录**: 捕获到的 `stderr` 内容会被实时写入到特定的日志文件中(由 `GET_LOG_FILE` 宏定义路径)。 3. **触发上报**: - 父进程等待子进程退出。 - 检查子进程退出状态 (`child_status`)。 - 如果 `WIFEXITED` 且退出码非 0,或者 `WIFSIGNALED`(被信号终止),则调用 `send_exec_params`。 4. **发送数据**: `send_exec_params` 将以下信息打包发送给 Go 服务: - **Filename**: 执行的命令名称。 - **CWD**: 当前工作目录。 - **Argv**: 完整的参数列表。 - **Envp**: 完整的环境变量列表。 - **Log Path**: 记录了错误输出的日志文件绝对路径。 5. **等待响应**: 客户端等待服务端返回处理结果(如 AI 分析建议),并将其打印到终端。 ```mermaid sequenceDiagram participant Parent as Parent Process participant Child as Child Process (Cmd) participant Log as Log File (Stderr) participant Socket as Unix Socket participant Server as Go Service Parent->>Child: forkpty() & exec() loop Execution Child->>Parent: Stderr Output (Pipe) Parent->>Log: Write Stderr Content end Child-->>Parent: Exit (Status != 0) Parent->>Parent: Check Exit Code alt Exit Code != 0 Parent->>Socket: Connect Parent->>Socket: Send Filename Parent->>Socket: Send CWD Parent->>Socket: Send Argv & Envp Parent->>Socket: Send Log Path (Error Log) Socket->>Server: Forward Request Server->>Server: Analyze Error (Read Log) Server-->>Socket: Response (Suggestion) Socket-->>Parent: Receive Response Parent->>Parent: Print Suggestion end ``` #### 通信协议格式 数据通过 Socket 顺序发送,格式如下(均为二进制流): | 字段 | 类型 | 说明 | | :--- | :--- | :--- | | `filename_len` | `size_t` | 文件名长度 | | `filename` | `char[]` | 文件名内容 | | `pwd_len` | `size_t` | 当前工作目录长度 | | `pwd` | `char[]` | 当前工作目录内容 | | `argc` | `int` | 参数个数 | | `arg_len` | `size_t` | (循环) 参数长度 | | `arg_str` | `char[]` | (循环) 参数内容 | | `envc` | `int` | 环境变量个数 | | `env_len` | `size_t` | (循环) 环境变量长度 | | `env_str` | `char[]` | (循环) 环境变量内容 | | `log_path_len` | `size_t` | 日志路径长度 | | `log_path` | `char[]` | 日志路径内容 | ## 编译构建 项目使用 `Makefile` 进行管理。 - **默认构建**: `make` (生成 `intercept.so`) - **开启 Hook 功能**: `make HOOK=1` (定义 `HOOK` 宏,启用拦截逻辑) - **清理**: `make clean` ## 目录结构说明 - `src/`: 源代码目录 - `execve_interceptor.c`: `execve` 拦截主逻辑 - `hook_write.c`: `write` 系列函数拦截逻辑 - `config.c`: 共享内存配置加载 - `pty_dup.c`: 伪终端处理 - `client.c`: Socket 通信客户端 - `rules.c`: 规则匹配逻辑 - `build/`: 编译输出目录