Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

254 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ALIS — A Lightweight Instrumentation System

全国大学生操作系统能力大赛(2026)· 功能挑战赛道 · 轻量级用户态动态追踪设计与实现

ALIS 是一个轻量级用户态动态探针注入库,面向 Linux x86_64 平台,基于 ptrace 系统调用实现纯用户态函数级动态插桩。项目采用经典的生产者-消费者架构,配合无锁环形缓冲区(lock-free ring buffer)和宿主侧多线程轮询处理技术,能够在远程进程中以极低开销执行探针。自研的动态函数地址解析器和第三方 Capstone 反汇编库等外围组件为 ALIS 提供强大支持,使其能够兼容 ASLR 和 PIE,成为一个真正可用的库而不仅是实验项目。

📖 详细设计见 设计文档 · 测试数据与指标见 测试报告

赛题背景

Linux 内核提供的 uprobe 机制通过修改目标进程指定地址的指令为 trap 指令陷入内核态执行探针,存在两方面局限:一是 trap 陷入内核对性能敏感函数影响不可忽视,二是 eBPF 编程受限于目标进程符号及结构体信息的缺失,表达能力有限。本赛题要求设计一种纯用户态跳转执行探针代码的方案,借助 ptrace 系统调用的内存修改能力,在不产生内核陷出的前提下完成函数级动态插桩,旨在深入实践进程内存布局、指令编码、上下文保存恢复、信号与异常处理、ptrace 等操作系统核心知识。

已完成功能

所有 7 项基础功能(F1–F7)均已实现并通过测试。

编号 功能 实现要点
F1 函数入口探针插入 通过 ptrace 将目标函数入口处指令替换为 jmp,跳转至 mmap 分配的 shellcode 区域,全程在用户态执行,不产生 trap 陷入内核
F2 函数参数获取 手写 x86_64 汇编在探针入口保存 rdi/rsi/rdx/rcx/r8/r9(System V ABI 前 6 个参数)至 ring buffer,通过共享内存回传宿主进程
F3 函数返回值捕获 通过篡改栈上返回地址(push ret_probe 地址),使原函数返回时自动跳入 ret_probe,捕获 rax/rdx/xmm0 后返回原调用者
F4 探针卸载与恢复 unpin() 将入口指令恢复为原始字节码,释放 mmap 区域,关闭共享内存,无内存泄漏
F5 探针动态开关 shellcode 内部通过读取共享内存中的 enable 标志位,运行时条件跳过 arg probe 或 ret probe;宿主侧通过 shared_mutex 安全切换
F6 多探针共存 支持同一进程内 ≥16 个并发探针,每个探针拥有独立的 shellcode 区域、ring buffer 和监控线程,互不干扰
F7 多线程安全 入队操作使用 lock xadd 原子指令,宿主侧使用 std::shared_mutex,ptrace 操作期间遍历并暂停所有远程线程

实现细节

探针注入流程

  1. 通过 /proc/<pid>/maps 解析目标库的加载基址和 .text 段范围
  2. 调用自研 ELF 解析器(支持 GNU hash / SYSV hash / 线性扫描三级查找)获取目标函数在库内的偏移量,加上基址得到运行时地址
  3. 使用 Capstone 反汇编目标函数入口处指令,检测 RIP-relative 指令和 endbr64,确保覆盖区域可安全搬迁
  4. 在目标进程地址空间中通过 ptrace 注入 mmap 调用,分配 RWX shellcode 区域、RW 堆区域,并通过 POSIX 共享内存建立 ring buffer
  5. 将 arg_probe 和 ret_probe shellcode 与搬迁后的原指令拼接写入 mmap 区域,在函数入口写入跳转指令
  6. 使用 ptrace SINGLESTEP 使所有线程越过被覆盖的指令区域(damaged zone)到达安全点后恢复运行

用户自定义回调

用户可通过 setProbeCallback() 设置任意回调函数,在探针触发时接收参数和返回值。回调签名:

void callback(const ProbeReturnDataHelper& data, probe_id_t id,
              bool arg_enabled, bool ret_enabled);
  • ProbeReturnDataHelper 提供 getArg(index)getRetval()getFloatRetval() 等便捷接口
  • 每个探针独立调用回调,同一进程的多个探针可设置不同回调
  • 回调在监控线程中执行,用户可自由选择输出方式(控制台、日志文件、网络等)
  • 演示程序 alis.cc 利用 MessageQueue 实现类似 GDB 的交互式体验,将探针数据异步投递到主线程输出

Ring Buffer 设计

+----------+----------+-----+----------+--------+--------+-----------+
| entry[0] | entry[1] | ... | entry[n] |  head  |  tail  | committed |
+----------+----------+-----+----------+--------+--------+-----------+
|<------------- 64B per entry ------------>|<---- 64B each ----->|

每条目 64 字节(缓存行对齐,避免 false sharing),布局如下:

[ type:4B ][ key(rsp):8B ][ arg1-arg6:48B ][ padding:4B ]
或
[ type:4B ][ key(rsp):8B ][ rax:8B ][ rdx:8B ][ xmm0:8B ][ padding:... ]

三个控制指针(各占独立缓存行,std::atomic<size_t>):

指针 写入者 含义
head 生产者(shellcode) 已分配的最大槽位索引(lock xadd 原子递增)
committed 生产者(shellcode) 所有数据字段写入完毕后递增,用于粗粒度通知消费者
tail 消费者(宿主线程) 已处理的最大槽位索引

竞态解决与 type 标志位

早期的两指针设计(head + tail)存在竞态问题:head 移动后数据可能尚未写入,消费者会读到脏数据。为此引入了 committed 指针——生产者写完所有字段后递增 committed,消费者仅读取 [tail, committed) 区间。

但在多线程环境下仍存在问题:线程 A 先获得槽位,线程 B 后获得;若 B 先完成写入并推进 committed,消费者可能读取到 A 的未完成数据。最终解决方案是type 字段作为逐条目的就绪标志

  • 生产者最后写入 type(在 key、参数/返回值全部写入之后)
  • 消费者检查 type:若为 0 则表示该条目尚在写入中,不推进 tail 并重新读取同一槽位(忙等),直到生产者写入 type 或超时放弃
  • type 的非零值同时携带语义信息(1=仅参数,2=仅返回值,3=参数+返回值,4=返回值无参数)

这实际上形成了一种基于数据就绪标志的无锁同步,在不引入内核锁的前提下保证了多生产者单消费者的正确性。

数据流

  1. 生产者(shellcode):head = lock xadd(head, 1) → 写入 key 和数据 → 最后写入 typecommitted = lock xadd(committed, 1)
  2. 消费者(宿主线程):轮询 committed,当 tail != committed 时遍历 [tail, committed) → 检查每条的 type,type==0 则不自增 tail 并重新循环读取(等待数据就绪)→ type!=0 的条目按类型匹配 arg/ret → 更新 tail → 调用用户回调

ELF 符号解析器

  • 优先使用 .gnu_hash(O(1) 均摊),回退 .hash(SYSV),最终回退线性扫描 .dynsym
  • 支持 .symtab 静态符号表回退(解析非导出函数)
  • 支持 .gnu_debuglink 自动加载独立调试文件(解析被 strip 的符号)
  • 允许返回 IFUNC 符号地址;通过 e_type 区分 PIE / 非 PIE 可执行文件

构建指南

依赖

依赖 最低版本 说明
CMake 3.16 构建系统
GCC 或 Clang 支持 C++20 编译器
Capstone 5.0+ (推荐 6.0) 反汇编库,用于指令分析和安全校验
Linux 内核 4.0+ ptrace / shm / mmap 等系统调用

安装 Capstone

# 从源码编译安装 Capstone v6
git clone https://github.com/capstone-engine/capstone.git
cd capstone
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
sudo make install

注意:某些 Linux 发行版通过包管理器安装的 Capstone 可能为旧版本(v4),与项目使用的 v6 头文件存在 ABI 不兼容,会导致 cs_disasm() 返回 0 而静默失败。建议从源码编译安装最新版本,确保头文件和库文件版本一致。项目的 CMake 配置已强制链接 /usr/local/lib 下的库文件以规避此问题。更多细节参见 lab/note.md

构建

cd alis
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)

构建产物:

产物 路径 说明
libalis_probe.a build/src/probe/ 探针静态库
alis build/src/examples/ 交互式示例程序
alis_target build/src/test/ 多线程测试目标
alis_bm_* build/src/benchmark/ 性能基准测试套件

运行指南

示例程序

# 终端 1:启动目标进程
./build/src/test/alis_target &
# 输入任意字符开始执行多线程循环

# 终端 2:以 root 权限启动探针交互程序
sudo ./build/src/examples/alis
# 按 Ctrl+C 进入交互模式,可使用以下命令:
#   pin    — 对 sum/sum2 函数插入探针
#   unpin  — 卸载探针
#   enable — 启用 arg/ret 探针
#   disable— 禁用 arg/ret 探针
#   exit   — 退出程序

性能基准测试

# 基本性能测试(getpid 100 万次循环)
cd build/src/benchmark/
sudo ./alis_bm_host ./alis_bm_target
# 输出包含:
#   Baseline  — 无探针耗时
#   Uprobe    — 内核 uprobe 耗时
#   ALIS      — 用户态探针耗时
#   Overhead   — 单次探针额外开销 (ns)

# 多线程测试
sudo ./alis_mt_host ./alis_mt_target

# 16 函数多探针测试
sudo ./alis_f16_host ./alis_f16_target

# 探针开关测试
sudo ./alis_toggle_host ./alis_toggle_target

# 信号安全测试
sudo ./alis_sig_host ./alis_sig_target

提示:所有探针操作需要 ptrace 权限,必须使用 sudo 或设置 ptrace_scope 为 0。

第三方库

本项目使用 Capstone 反汇编引擎,用于在探针注入前对目标函数入口指令进行反汇编分析,确保被覆盖的指令不包含 RIP-relative 寻址模式,从而保证指令搬迁的安全性。

人工智能使用说明

本项目开发过程中使用了 AI 辅助编程工具,使用情况如下:

  • 开发环境:主要使用 VS Code,部分使用 TraeCN。
  • Inline Suggestions(VS Code)和 CUE(TraeCN):在编码全程保持开启,用于代码补全和局部建议。尤其在编写重复性的日志输出时(如DEBUG_LOG, ERROR_LOG内的信息和格式化内容等),自动补全提供了极大便利。
  • elf_resolver 模块src/probe/elf_resolver.cc / elf_resolver.h)以及 src/benchmark/ 下的测试代码:由我们提供详细的设计需求和实现思路,交由 AI Agent 完成绝大部分编码工作,随后经过人工审查和修正。
  • log.h:主要由 AI 生成,是一些标准的日志输出宏定义。
  • 其他核心模块(包括探针汇编 shellcode、环形缓冲区设计与实现、探针注入流程中的辅助函数等):AI 直接上手参与或利用agent直接生成大段代码的占比极低(< 5%),主要由人工设计和编写。但会涉及与 AI 的讨论、问题求解和代码审查。
  • 项目文档(含本 README):由我们提供详细大纲和要点,使用 AI 辅助生成初稿,经过严格人工核对和修改后定稿。
  • Commit message:由AI翻译得到英文提交信息
  • 使用的模型:一部分(前期工作和elf_resolver)使用了Gemini-3.1-Pro-Preview,其余工作使用DeepSeek-V4-Pro

项目目录结构

alis/
├── CMakeLists.txt              # 顶层 CMake 配置
├── README.md                   # 项目说明(本文件)
├── doc/                        # 文档
│   ├── task.md                 # 赛题要求
│   ├── design.md               # 设计文档
│   ├── test_report.md          # 测试报告
│   ├── note.md / note_zh.md    # 开发笔记(踩坑记录)
│   ├── note2.md                # 函数入口指令多样性调研
│   └── note3.md                # ELF 解析器设计分析
├── src/                        # 主库源码
│   ├── CMakeLists.txt
│   ├── common/                 # 公共头文件(类型、日志、RAII 辅助类)
│   ├── probe/                  # 探针核心库(alis_probe)
│   │   ├── probe.h / probe.cc  # 探针主类(pin/unpin/toggle)
│   │   ├── arg_probe.s         # 参数探针汇编
│   │   ├── ret_probe.s         # 返回值探针汇编
│   │   ├── elf_resolver.h/cc   # ELF 符号解析器
│   │   ├── opcodes.h           # x86_64 指令 opcode 常量
│   │   └── utils.h             # 注入辅助函数(remoteMMap/skipDamagedZone 等)
│   ├── examples/               # 示例程序(交互式 alis.cc)
│   ├── test/                   # 测试目标程序
│   └── benchmark/              # 性能基准测试套件
│       ├── host.cc / target.cc # 基本性能对比(baseline/uprobe/ALIS)
│       ├── f16_*.cc            # 16 函数多探针测试
│       ├── mt_*.cc             # 多线程并发测试
│       ├── toggle_*.cc         # 探针动态开关测试
│       └── sig_*.cc            # 信号安全测试
└── lab/                        # 早期实验原型
    ├── probe/                  # 单文件探针原型
    ├── target/                 # 简单目标程序
    └── objdump/                # 反汇编输出

团队分工

成员 职责
探针注入引擎、Shellcode 汇编、环形缓冲区设计与实现、探针生命周期管理、指令安全分析、性能基准测试、多线程测试、探针开关测试、信号安全测试、项目文档
队友A RIP-relative 指令重定位器(未合入主分支)、汇报 PPT
队友B ELF 符号解析器、函数入口指令多样性调研(note2.md)、ELF 解析器设计分析(note3.md

开发时间线

时间 里程碑
2026-05-10 项目启动,初始化仓库和基础 CMake 配置
2026-05-21 完成 lab 原型(ptrace 探针注入基本流程)
2026-06-08 lab 重构为主库(src/probe/),多线程模型引入
2026-06-22 探针动态开关、环形缓冲区竞态修复(type 就绪标志)
2026-06-24 ELF 符号解析器合入主分支
2026-06-30 测试套件完成、文档定稿

原计划在初赛阶段完成全部基础功能(F1–F7)及进阶功能 A1(ARM64)和 A2(条件探针),因时间原因进阶功能未及实装。项目中部分为 A1/A2 铺设的基础设施(opcodes.h 中的 __aarch64__ 分支、ring buffer 中的条件 type 标记等)已就位。

许可证

本项目源代码遵循 BSD 3-Clause License。技术文档(含设计文档测试报告、本 README)遵循 CC-BY-SA 4.0

第三方依赖 Capstone 遵循 BSD 3-Clause 许可证。

About

A lightweight user-space dynamic instrumentation library using ptrace for function-level probing without kernel traps.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages