execve_hook/README.md

234 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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/`: 编译输出目录