|
|
||
|---|---|---|
| .vscode | ||
| data/MNIST/raw | ||
| src | ||
| tests | ||
| .gitignore | ||
| Makefile | ||
| README.md | ||
| go_install.sh | ||
| test_bash.sh | ||
| test_log.stderr.log | ||
README.md
Execve Hook Project
本项目是一个基于 LD_PRELOAD 机制的系统调用拦截库,主要用于监控和控制 Linux 系统中的命令执行(execve)。它能够拦截命令执行请求,检查配置规则,记录执行日志,甚至通过 PTY(伪终端)捕获命令的输入输出。
核心逻辑概述
整个 C 代码库的逻辑可以分为以下几个核心模块:
1. execve 拦截器 (src/execve_interceptor.c)
这是项目的核心入口。
- 拦截机制: 通过
LD_PRELOAD预加载动态库,覆盖系统默认的execve函数。 - 主要流程:
- 环境清理: 在调用前移除
LD_PRELOAD环境变量,防止对子进程造成非预期的递归影响。 - 配置加载: 调用
config.c从共享内存中加载配置。 - 规则匹配: 使用
rules.c检查当前执行的命令是否命中特定规则。 - 行为决策: 根据规则决定是否需要拦截、记录日志或通过 PTY 执行。
- 执行原函数: 最终调用原始的
execve系统调用执行目标程序。
- 环境清理: 在调用前移除
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)以确保子进程正确退出。
- 使用
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,也能捕获程序的输出内容。
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 或被信号终止)时,系统会触发错误上报流程。
- 执行与捕获:
pty_dup.c中的handle_io循环负责实时捕获子进程的stdout和stderr。 - 日志记录: 捕获到的
stderr内容会被实时写入到特定的日志文件中(由GET_LOG_FILE宏定义路径)。 - 触发上报:
- 父进程等待子进程退出。
- 检查子进程退出状态 (
child_status)。 - 如果
WIFEXITED且退出码非 0,或者WIFSIGNALED(被信号终止),则调用send_exec_params。
- 发送数据:
send_exec_params将以下信息打包发送给 Go 服务:- Filename: 执行的命令名称。
- CWD: 当前工作目录。
- Argv: 完整的参数列表。
- Envp: 完整的环境变量列表。
- Log Path: 记录了错误输出的日志文件绝对路径。
- 等待响应: 客户端等待服务端返回处理结果(如 AI 分析建议),并将其打印到终端。
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/: 编译输出目录