Go to file
Pan Qiancheng 5418ce4b38 feat: 清理不再需要的hook_write,新增交叉编译脚本 2025-12-09 20:08:15 +08:00
.vscode 连接service进行报错信息上传,分离输出stderr和stdout 2025-04-12 19:35:11 +08:00
data/MNIST/raw 连接service进行报错信息上传,分离输出stderr和stdout 2025-04-12 19:35:11 +08:00
src feat: 清理不再需要的hook_write,新增交叉编译脚本 2025-12-09 20:08:15 +08:00
tests feat: makefault_arm64 2025-12-07 23:57:40 +08:00
.gitignore fix: 修复LD_PRELOAD bash -c执行时无法回显的bug, 修复窗口大小改变时无法同步子进程的bug 2025-12-05 17:13:20 +08:00
Makefile feat: 清理不再需要的hook_write,新增交叉编译脚本 2025-12-09 20:08:15 +08:00
README.md fix: 修复LD_PRELOAD bash -c执行时无法回显的bug, 修复窗口大小改变时无法同步子进程的bug 2025-12-05 17:13:20 +08:00
build_all.sh feat: 清理不再需要的hook_write,新增交叉编译脚本 2025-12-09 20:08:15 +08:00
go_install.sh 编译 2025-04-25 16:29:11 +08:00
test_bash.sh Merge branch 'master' of https://gitea.51mars.com/pqc/execve_hook 2025-04-28 10:44:13 +08:00
test_log.stderr.log fix: 修复socket路径不一致,提供测试 2025-12-07 20:37:23 +08:00

README.md

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 系统调用执行目标程序。
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 等标准输出函数。
  • 功能: 将进程写入 stdoutstderr 的内容同时写入到一个指定的日志文件中。
  • 用途: 即使不使用 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 或被信号终止)时,系统会触发错误上报流程。

  1. 执行与捕获: pty_dup.c 中的 handle_io 循环负责实时捕获子进程的 stdoutstderr
  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 分析建议),并将其打印到终端。
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/: 编译输出目录