☰
WSL2 下安装 angr 符号执行框架:环境配置与避坑指南
2026/10/16 11:00:44 网站建设 项目流程

1. 为什么要在 WSL 里折腾 angr

angr 是一个基于符号执行与约束求解的二进制分析框架,做逆向工程、漏洞挖掘、自动化利用生成的人基本都绕不开它。但 angr 的依赖链出了名的长——它底层依赖 z3 求解器、capstone 反汇编引擎、unicorn 模拟执行引擎、pyvex 中间表示库,还有一大堆 Python 科学计算栈。在原生 Windows 上装 angr,光是编译这些 C 扩展就能让人崩溃,很多包根本没有预编译的 Windows wheel,pip 装到一半就开始报编译错误。

WSL 的出现算是给了一个折中方案:你依然用 Windows 的桌面环境、编辑器和终端,但底层跑的是一个完整的 Linux 内核兼容层,apt、gcc、make 这些工具链一应俱全。angr 官方文档里推荐的安装方式就是 Linux 环境下的 pip 安装,WSL 恰好能满足这个前提。我第一次在 WSL 里装 angr 的时候,从零到跑通第一个符号执行脚本大概花了四十分钟,其中大部分时间是在等依赖下载和编译。后来帮别人装过几次,踩了不少坑,也总结出一套比较稳的流程。

这篇文章适合三类人看:第一类是在 Windows 上做逆向分析、被 angr 安装折磨过的安全从业者;第二类是刚接触符号执行、想找个稳定环境跑通 demo 的学生;第三类是已经装了 WSL 但不确定怎么把 angr 环境配干净的开发者。我会从 WSL 发行版选择开始,一路讲到 angr 安装、验证、常见报错排查,把每一步背后的原因也讲清楚,让你不只是照抄命令,而是知道为什么这么做。

提示:本文所有操作均在 WSL2 环境下完成,WSL1 虽然也能装,但在 angr 的某些底层调用上会出现兼容性问题,建议直接用 WSL2。

2. 环境准备与发行版选择

2.1 WSL2 与 WSL1 的关键差异

很多人装 WSL 的时候没注意版本,默认可能装的是 WSL1。WSL1 本质上是一个系统调用翻译层,它把 Linux 的系统调用实时翻译成 Windows 的 NT 调用,文件系统性能差、对某些底层 API 支持不完整。WSL2 则是跑在一个轻量级虚拟机里的真实 Linux 内核,系统调用是原生的,对 angr 这种需要大量底层操作的框架来说,WSL2 是唯一靠谱的选择。

检查当前 WSL 版本的方法很简单,在 PowerShell 里执行:

wsl -l -v

输出里会列出每个发行版的名称、状态和版本号。如果 VERSION 列显示的是 1,那就需要升级。升级命令是:

wsl --set-version Ubuntu-22.04 2

把Ubuntu-22.04换成你实际的发行版名称。升级过程可能需要几分钟,因为要把整个文件系统转换成虚拟磁盘格式。

2.2 发行版选哪个更省心

angr 官方测试覆盖最多的发行版是 Ubuntu 20.04 和 22.04。我建议直接用 Ubuntu 22.04 LTS,原因有三个:第一,它的 Python 版本是 3.10,angr 对 3.8 到 3.11 的支持都比较成熟;第二,apt 源里的编译工具链版本足够新,不需要额外加 PPA;第三,社区资料多,遇到问题搜一下基本都有现成答案。

如果你已经装了 Debian 或者其他发行版,也不是不能用,但可能会遇到 glibc 版本不匹配的问题。angr 的某些预编译 wheel 是针对特定 glibc 版本构建的,Debian 稳定版的 glibc 往往偏旧,导致 pip 找不到匹配的 wheel,只能从源码编译,时间成本会高很多。

安装新发行版的命令:

wsl --install -d Ubuntu-22.04

装完之后第一次启动会要求设置用户名和密码,这个密码是 sudo 用的,记牢。

2.3 系统更新与基础依赖

进入 WSL 终端后,第一件事是更新包索引并升级已有包:

sudo apt update && sudo apt upgrade -y

这一步别省。很多人在一个几个月没更新的系统上装 angr,结果因为 apt 源里的依赖版本太旧,pip 装到一半报错,回头还得重新来。

接着装编译工具链和 Python 开发头文件:

sudo apt install -y build-essential python3-dev python3-pip python3-venv git

这里解释一下每个包的作用。build-essential包含了 gcc、g++、make 等编译工具,angr 的某些依赖在没有预编译 wheel 的情况下需要本地编译。python3-dev提供 Python 的头文件,编译 C 扩展时必须要有。python3-venv用来创建虚拟环境,这是后面隔离依赖的关键。git不用多说,有些包需要从源码仓库拉取。

注意:不要用sudo pip install往系统 Python 里装 angr。系统 Python 是 apt 管理的,你手动装的包可能会和 apt 的依赖解析冲突,严重的时候会导致 apt 本身坏掉。用虚拟环境是唯一正确的做法。

3. 虚拟环境与 angr 安装实操

3.1 创建独立的虚拟环境

虚拟环境的价值在于隔离。angr 依赖的某些库版本可能和你系统里其他项目的依赖冲突,比如 z3-solver 的版本、pyvex 的版本,一旦冲突,排查起来非常痛苦。虚拟环境让每个项目的依赖互不干扰。

创建并激活虚拟环境的命令:

python3 -m venv ~/angr-env source ~/angr-env/bin/activate

第一行在用户主目录下创建一个名为angr-env的虚拟环境目录,第二行激活它。激活后你的命令行提示符前面会出现(angr-env)字样,表示当前处于虚拟环境中。

我习惯把虚拟环境放在主目录下,而不是项目目录里,原因是 WSL 访问 Windows 文件系统(/mnt/c/...)的性能很差,如果虚拟环境放在 Windows 盘上,每次激活和运行都会明显变慢。放在 Linux 原生文件系统里,速度是正常的。

3.2 升级 pip 并安装 angr

虚拟环境创建后,里面的 pip 版本可能比较旧,先升级:

pip install --upgrade pip setuptools wheel

然后安装 angr:

pip install angr

这条命令会拉取 angr 及其所有依赖。依赖列表很长,包括:

依赖包作用
z3-solverSMT 约束求解器,符号执行的核心
pyvexVEX 中间表示的 Python 绑定
capstone反汇编引擎,把机器码转成汇编
unicornCPU 模拟执行引擎
claripyangr 的抽象约束求解接口
cle二进制加载器,解析 ELF/PE 等格式
archinfo架构信息库,定义寄存器、字节序等
pyelftoolsELF 文件解析
networkx图算法,用于控制流图分析

安装过程可能需要几分钟,取决于网络速度。如果卡在某个包上很久,大概率是在从源码编译。比如pyvex在某些平台上没有预编译 wheel,pip 会下载源码包然后本地编译,这时候build-essential和python3-dev就派上用场了。

3.3 验证安装是否成功

装完之后别急着写脚本,先验证一下核心组件能不能正常导入:

python3 -c "import angr; print(angr.__version__)"

如果输出了版本号,说明基本安装成功。但 angr 的某些功能依赖额外的可选组件,比如angr-management(图形界面)和angr-targets(预定义分析目标)。这些不是必须的,但如果你要做可视化分析,可以一并装上:

pip install angr-management

再做一个更完整的验证,测试符号执行引擎能否正常工作:

import angr import claripy # 创建一个简单的项目,加载一个不存在的路径会报错,这里只是测试导入 print("angr version:", angr.__version__) print("claripy available:", claripy.__name__) # 测试 z3 求解器 x = claripy.BVS('x', 32) s = claripy.Solver() s.add(x > 10) s.add(x < 20) print("solution:", s.eval(x, 1))

这段代码创建了一个 32 位的符号变量 x,约束它大于 10 小于 20,然后求解。如果输出类似[11]或[15]这样的结果,说明符号执行的核心链路是通的。

实操心得:验证的时候不要只测import angr,因为 angr 的导入是懒加载的,有些子模块在导入时不会立即加载。真正跑一个符号执行的小例子,才能确认 z3、claripy、pyvex 这些底层组件都正常。

4. 跑通第一个符号执行案例

4.1 准备一个测试二进制文件

angr 需要一个二进制文件作为分析目标。最方便的方式是自己编译一个简单的 C 程序。在 WSL 里创建一个测试目录:

mkdir -p ~/angr-test && cd ~/angr-test

写一个带条件分支的小程序:

#include <stdio.h> #include <string.h> int check_password(char *input) { if (strlen(input) != 8) { return 0; } if (input[0] == 'a' && input[1] == 'n' && input[2] == 'g' && input[3] == 'r') { if (input[4] == '2' && input[5] == '0' && input[6] == '2' && input[7] == '4') { return 1; } } return 0; } int main(int argc, char **argv) { if (argc != 2) { printf("Usage: %s <password>\n", argv[0]); return 1; } if (check_password(argv[1])) { printf("Correct!\n"); } else { printf("Wrong!\n"); } return 0; }

编译成静态链接的二进制,方便 angr 分析:

gcc -static -o test_binary test.c

用-static是为了避免动态链接库的加载问题,angr 分析静态二进制时不需要处理外部符号,入门阶段更简单。

4.2 编写符号执行脚本

创建一个 Python 脚本solve.py:

import angr import claripy import sys def main(): # 加载二进制文件 proj = angr.Project('./test_binary', auto_load_libs=False) # 创建符号变量,长度 8 字节,每个字节 8 位 # 这里用 8 个独立的 8 位符号变量,方便加约束 password_chars = [claripy.BVS(f'c{i}', 8) for i in range(8)] password = claripy.Concat(*password_chars) # 创建初始状态,把符号变量作为 argv[1] 传入 # argv[1] 是命令行第一个参数 state = proj.factory.entry_state(args=['./test_binary', password]) # 对每个字符加约束,限制为可打印字符,缩小求解空间 for c in password_chars: state.solver.add(c >= 0x20) state.solver.add(c <= 0x7e) # 创建模拟管理器 simgr = proj.factory.simulation_manager(state) # 探索:找到输出 "Correct!" 的路径,避开输出 "Wrong!" 的路径 # 这里用字符串地址作为 find 和 avoid 的目标 # 实际地址需要根据二进制确定,下面用符号化的方式找 simgr.explore(find=lambda s: b'Correct!' in s.posix.dumps(1), avoid=lambda s: b'Wrong!' in s.posix.dumps(1)) if simgr.found: found = simgr.found[0] # 求解 password 的值 solution = found.solver.eval(password, cast_to=bytes) print(f"Found password: {solution.decode()}") else: print("No solution found") if __name__ == '__main__': main()

这段脚本的核心逻辑是:把密码的 8 个字符都设为符号变量,让 angr 去探索所有可能的执行路径,找到那条输出 "Correct!" 的路径,然后反向求解出满足条件的字符值。

4.3 运行与结果解读

执行脚本:

python3 solve.py

如果一切正常,你会看到输出:

Found password: angr2024

这正是我们在 C 代码里硬编码的密码。angr 通过符号执行,自动探索了所有分支,找到了正确的输入。

这个过程背后发生了什么?angr 把二进制加载进来,构建控制流图,然后从入口点开始模拟执行。遇到条件分支时,它不选择某一条路,而是把两条路都记录下来,分别维护各自的约束条件。当某条路径到达输出 "Correct!" 的位置时,angr 收集这条路径上所有的约束,交给 z3 求解器,z3 反推出满足所有约束的输入值。

注意:如果脚本跑得很慢或者卡住,可能是auto_load_libs=False没设置。默认情况下 angr 会尝试加载所有动态链接库,这会大幅增加分析时间。对于静态编译的二进制,关掉这个选项能快很多。

5. 常见报错与排查技巧

5.1 安装阶段的典型问题

问题一:pip 安装时卡在Building wheel for pyvex

这是最常见的情况。pyvex 在某些 Python 版本和平台上没有预编译 wheel,pip 会从源码编译。编译需要build-essential和python3-dev,如果没装就会报错。解决方法:

sudo apt install -y build-essential python3-dev pip install --upgrade pip pip install angr

如果还是卡住,可以尝试指定 pyvex 的版本,有些版本有预编译 wheel:

pip install pyvex==9.2.100 pip install angr

问题二:ERROR: Could not find a version that satisfies the requirement

这通常是网络问题或者 pip 源的问题。WSL 默认使用 PyPI 官方源,国内访问可能不稳定。可以临时切换到国内镜像源:

pip install angr -i https://pypi.tuna.tsinghua.edu.cn/simple

但要注意,镜像源同步可能有延迟,某些最新版本的包可能还没同步过来。如果镜像源找不到,换回官方源再试。

问题三:ImportError: libz3.so: cannot open shared object file

z3-solver 的共享库没找到。这通常发生在虚拟环境没有正确激活,或者 z3 安装不完整的情况下。解决方法是重新安装 z3-solver:

pip uninstall z3-solver -y pip install z3-solver

如果还不行,检查虚拟环境是否激活,which python3应该指向虚拟环境目录下的 python3。

5.2 运行阶段的典型问题

问题四:符号执行速度极慢

angr 的符号执行是指数级复杂度的,路径爆炸是常态。几个优化方向:

  • 用auto_load_libs=False关闭动态库加载
  • 对输入加约束,缩小求解空间(比如限制为可打印字符)
  • 使用simgr.explore的find和avoid参数,尽早剪枝
  • 考虑用veritesting技术,angr 内置了这个选项:proj.factory.entry_state(veritesting=True)

问题五:SimulationManager没有 found 结果

如果simgr.found为空,说明没有找到满足条件的路径。可能的原因:

  • find和avoid的条件写错了,比如字符串匹配的地址不对
  • 二进制文件路径不对,angr 加载了错误的文件
  • 输入约束太严格,导致没有可行解

排查方法是先不加find和avoid,让 angr 自由探索,看看能到达哪些状态:

simgr.run() print(simgr.deadended)

5.3 常见问题速查表

报错信息可能原因解决方法
Building wheel for pyvex卡住缺少编译工具链安装 build-essential 和 python3-dev
Could not find a version网络或源问题切换 pip 镜像源
libz3.so not foundz3 安装不完整重装 z3-solver
符号执行极慢路径爆炸加约束、用 veritesting、关闭 auto_load_libs
simgr.found为空find/avoid 条件错误先自由探索,检查状态
ImportError: angr虚拟环境未激活执行 source activate

避坑技巧:每次开始新项目前,先确认虚拟环境已激活。我见过太多人因为忘了激活虚拟环境,在系统 Python 里装了一堆包,结果和 apt 管理的包冲突,最后不得不重装系统。养成习惯,打开终端第一件事就是source ~/angr-env/bin/activate。

6. 性能调优与进阶配置

6.1 WSL2 的资源分配

WSL2 默认会占用最多 50% 的系统内存和所有 CPU 核心。对于 angr 这种内存消耗大的应用,默认配置可能不够用,也可能占用过多导致 Windows 主机卡顿。可以在用户主目录下创建.wslconfig文件来调整:

[wsl2] memory=8GB processors=4 swap=2GB

这个文件放在 Windows 用户目录下,比如C:\Users\你的用户名\.wslconfig。修改后需要重启 WSL:

wsl --shutdown

然后重新打开 WSL 终端。内存给多少取决于你的主机配置,angr 分析大型二进制时可能需要 4GB 以上,建议至少给 8GB。

6.2 文件系统性能优化

WSL2 访问 Windows 文件系统(/mnt/c/)的性能很差,因为要经过 9P 协议转换。angr 在分析过程中会频繁读写临时文件,如果项目放在 Windows 盘上,速度会明显下降。

最佳实践是把所有 angr 相关的文件都放在 Linux 原生文件系统里,也就是~/目录下。如果你需要和 Windows 共享文件,可以用cp命令手动拷贝,或者用 VS Code 的 Remote-WSL 插件直接在 WSL 里编辑。

6.3 使用 angr 的 Docker 镜像

如果你不想折腾环境,angr 官方提供了 Docker 镜像。在 WSL2 里装 Docker 之后,可以直接拉取:

docker pull angr/angr docker run -it angr/angr

Docker 镜像的好处是环境完全隔离,不会污染你的 WSL 系统。缺点是文件共享稍微麻烦一点,需要用-v参数挂载目录。对于长期使用 angr 的人来说,我建议还是自己配一个虚拟环境,灵活度更高,调试也更方便。

7. 我踩过的坑与个人建议

第一次装 angr 的时候,我在 WSL1 上折腾了两个小时,各种报错,最后发现是 WSL1 的系统调用翻译层对某些底层操作支持不好。换成 WSL2 之后,同样的步骤二十分钟就跑通了。所以如果你还在用 WSL1,别犹豫,直接升级。

第二个坑是 Python 版本。我一开始用 Ubuntu 20.04 自带的 Python 3.8,装 angr 没问题,但后来想用某个新特性,发现 3.8 不支持。Ubuntu 22.04 自带 Python 3.10,兼容性和新特性支持都更好。如果你对 Python 版本有要求,可以在虚拟环境里指定版本,但需要额外装python3.10-venv之类的包。

第三个坑是内存。有一次分析一个稍微大一点的二进制,angr 跑了十分钟,然后 WSL 直接被 OOM killer 杀掉了。后来在.wslconfig里把内存调到 12GB,问题解决。angr 的符号执行确实吃内存,尤其是路径爆炸的时候,每个状态都要保存一份内存快照。如果你的主机内存小于 16GB,建议分析小型的、静态链接的二进制,或者用veritesting减少状态数量。

最后一个建议是关于学习路径的。angr 的文档不算特别友好,很多 API 需要看源码才能理解。我的建议是先跑通官方文档里的例子,然后自己写几个简单的 C 程序,编译成二进制,用 angr 去分析。从最简单的条件分支开始,逐步增加复杂度,比如循环、函数调用、间接跳转。每遇到一个问题,就查文档、看源码、在社区里搜。这个过程比单纯看教程要扎实得多。

angr 在 WSL 里的安装和使用,本质上就是把 Linux 下的流程搬到 Windows 的子系统里。核心步骤就三步:装 WSL2、建虚拟环境、pip install angr。剩下的都是细节调优和问题排查。把环境配好之后,你就可以专注于符号执行本身,去分析那些真正有意思的二进制了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询