我第一次在 Ubuntu 上装 pwntools 的时候,其实挺狼狈的。教程里写的就三行命令,我照着敲完,import 直接报错,查了整整一个下午,最后发现是 Python 环境、binutils、还有软件源这些“前置小事”在轮番坑人。所以这次我直接把一条龙流程整理出来,把每一步的为什么也讲清楚。这一篇的目标很单纯:照着走完,你的终端里就有一个能正常跑 pwntools 的环境,能写 exp、能连远程、能本地调试,不让你在无关的地方浪费时间。
1. 项目概述:pwntools 是什么,Ubuntu 环境怎么选
1.1 pwntools 的定位与适用人群
pwntools 是 CTF 竞赛里 Pwn 方向最常用的 Python 工具库,也被大量二进制安全研究者用在漏洞利用开发里。它解决的核心问题是:写 exploit 时那些重复劳动——生成 shellcode、解析 ELF 文件、自动计算溢出偏移、处理 socket 连接、构造格式化字符串、反汇编和汇编指令。如果没有它,你得自己去解析 ELF 头、手算 ROP 地址、自己拼远程交互逻辑,一场比赛下来光是重复代码就能写几百行。
它本质上是一个 Python 包,底层封装了 Capstone 反汇编引擎、Binutils 工具链的一些能力,同时自带针对 pwntools 生态的大量便捷接口。举个例子,你在 pwntools 里执行ELF('/bin/ls')就能直接读取程序的信息,拿到符号表、PLT、GOT 地址,这在手工readelf的时代是不可想象的。适合读这篇文章的人,主要就是三类:
- CTF 新手,刚接触二进制利用,需要一套不折腾的环境来练手。
- 安全研究员,做漏洞 demo 验证时想快速生成 PoC。
- 用过 pwntools 旧版本,但换了 Ubuntu 新系统后重新部署环境的老人。
1.2 Ubuntu 环境的选择:双系统、虚拟机还是 WSL
很多人在第一步就卡住:我到底把 Ubuntu 装在哪里?结合我自己的实际体验,以及见过的大量样本,简单聊聊三种方案的取舍。
双系统最大的优势是性能和硬件直通,如果你需要和 USB 设备、特定内核模块交互,双系统最省心。但代价是切换麻烦,系统升级出问题还会影响另一个系统,对新手来说运维成本偏高。虚拟机(VMware 或 VirtualBox)是我个人最推荐的方案,做快照方便,环境玩坏了秒回滚,虚拟机里的 Ubuntu 网络也是 NAT 隔离,不会影响宿主机。对 pwntools 这种纯用户态工具链来说,虚拟机的性能损耗几乎可以忽略。
WSL 则是最近一两年很流行的选择,特别是 WSL2,跑 Ubuntu 22.04/24.04 写 pwntools 脚本完全没问题,和 Windows 宿主机共享文件也方便。不过它对底层调试有一些小限制,比如某些内核模块、驱动相关的实验不太合适,而且如果你习惯用 pwntools 调用 gdb 弹出图形化调试界面,WSL 里需要额外配置 WSLg 或 X server,否则窗口出不来。
所以我的建议很直接:如果你是刚入门,先上虚拟机;如果是平时写脚本做题目为主,WSL 也够用;只有当你明确知道“我需要直接操作硬件”的时候,再考虑双系统。
2. 安装前必做:软件源与 Python 环境自检
2.1 让 apt 和 pip 用上合适的软件源
这一步看起来和 pwntools 无关,但恰恰是很多人安装失败的第一原因。Ubuntu 默认自带的软件源在国外,国内网络环境下执行apt update可能慢到让你以为机器卡死了,更别提后面还要安装 binutils、libcapstone-dev 这些依赖包。所以装任何开发环境的第一件事,就是先把 apt 源换成国内镜像源。
在 Ubuntu 20.04 和 22.04 上,软件源配置在/etc/apt/sources.list。我习惯用清华源或者阿里源,操作方式是用 sed 直接替换,把原来的 archive.ubuntu.com 镜像地址换掉:
sudo sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list sudo sed -i 's/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list sudo apt update但有一点要特别注意,Ubuntu 24.04 开始默认改用 deb822 格式,软件源配置放在了/etc/apt/sources.list.d/ubuntu.sources,你在网上搜到的大部分 sed 命令对它无效。24.04 上我建议直接编辑这个文件,把里面的http://archive.ubuntu.com/ubuntu/替换成https://mirrors.tuna.tsinghua.edu.cn/对应的镜像地址,再执行sudo apt update。如果你不想手动改文件,也可以打开“软件与更新”图形工具,在下拉列表里选择镜像站点,效果一样。
pip 源同理。pip 默认从官方 PyPI 下载包,国内网络下载大包经常超时。我一般直接写一个配置文件~/.config/pip/pip.conf:
[global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple trusted-host = pypi.tuna.tsinghua.edu.cn配好之后,后面所有 pip 安装都会快很多。这一步的耐心支出是性价比最高的,后面装 pwntools 的时候就能体会到。
2.2 Python 版本与虚拟环境取舍
pwntools 要求 Python 3.6 以上,实际上 Ubuntu 20.04 自带的 Python 3.8、22.04 的 Python 3.10、24.04 的 Python 3.12 都能用。但最大的坑不是版本太低,而是你把 pwntools 直接装进了系统 Python。
我见过太多人执行sudo pip install pwntools,然后过段时间系统升级或者装了别的工具,某个依赖被顶掉,pwntools 就废了。更痛苦的是,当你同时玩多个 CTF 项目,不同项目可能需要不同版本的 pwntools 或配套工具,全部塞在系统环境里就是一场灾难。所以我的原则很明确:永远把 pwntools 装在虚拟环境里。
Ubuntu 自带的python3-venv就能创建虚拟环境。在动手之前先检查一下当前状态:
python3 --version pip3 --version如果pip3找不到,或者提示要安装 python3-pip,就先用 apt 安装基础部分:
sudo apt update sudo apt install -y python3 python3-pip python3-venv这里顺带说一句,如果系统提示找不到python3-venv包,不同 Ubuntu 版本包名可能不一样,比如你可能需要安装python3.10-venv或python3.12-venv,用apt search python3.*-venv搜一下就能确定。
3. 核心安装全流程:依赖、安装、验证一条龙
3.1 先解决系统依赖包
很多人装 pwntools 失败,其实卡在系统依赖上。pwntools 的 Python 包本身依赖 PyElfTools、Cython、Capstone 等组件,这些组件在编译时可能需要系统底层库。我下面给的这组依赖是我在不同 Ubuntu 版本上都验证过的组合:
sudo apt install -y git binutils file libssl-dev libffi-dev libcapstone-dev build-essential gdb patchelf逐个解释一下为什么需要它们:
binutils:提供 as、objdump、readelf 等工具。pwntools 里的asm()和ELF()分析都调用了 binutils 的能力,缺了它你会在运行时报错,提示找不到工具链。libcapstone-dev:Capstone 反汇编引擎的开发头文件。pwntools 内部用了 Capstone 来实现反汇编功能,提前装好这个包,Python 层面安装 capstone 依赖时走系统库,能省去不少编译麻烦。libssl-dev和libffi-dev:一些依赖包在编译时会用到 OpenSSL 和 libffi 的开发库,尤其是老版本 Python 环境下,缺了它们会出现各种诡异的编译错误。gdb:pwntools 的gdb.debug()功能需要它。它的作用是把程序跑起来挂在 gdb 下面,方便你观察寄存器、内存和堆。patchelf:用于修改 ELF 文件的 interpreter 和 rpath。做远程题需要切换 libc 版本的时候,它比手动修改 ELF 头高效得多。
如果你只是想体验一下 pwntools 最基础的功能,binutils和libcapstone-dev是必须的,其余的按需安装。但既然都走这一趟了,我建议一次性装齐,免得后续想用某个功能时再回头补。
3.2 创建虚拟环境并安装 pwntools
系统依赖准备好后,就开始创建虚拟环境。我的习惯是放在~/pwnenv这个路径,简单好记:
cd ~ python3 -m venv pwnenv source pwnenv/bin/activate激活成功后,终端前面会出现一个(pwnenv)前缀,这说明你已经进入虚拟环境了。此时敲python和pip都是虚拟环境里的版本,注意这里不需要加 sudo。
接下来先升级虚拟环境里的基础工具,这一步是为了避免老版本 pip 或 setuptools 导致 pwntools 安装失败:
python -m pip install -U pip setuptools wheel然后正式安装 pwntools:
pip install pwntools如果 pip 源配置好了,这一步应该很顺畅。我这边实测在 Ubuntu 22.04、Python 3.10 环境下,整个过程两三分钟就完成了。安装完成后可以用 pip 确认版本:
pip show pwntools看到Name: pwntools和Version: 4.x.x就说明装好了。顺便说一句,pwntools 目前主流版本是 4.x,如果你在教程里看到老的from pwn import *用法,基本都是还在生效的接口。
3.3 安装验证:一个小 Demo 确认工具可用
装完了不能光看版本号,得真正跑一下才能确认整个工具链是通的。我习惯用下面这几行来验证关键能力:
python - <<'EOF' from pwn import * context.arch = 'amd64' e = ELF('/bin/ls') print('ELF loaded:', e.path) print('asm nop ->', asm('nop').hex()) EOF如果一切正常,你会看到类似输出:
ELF loaded: /bin/ls asm nop -> 90ELF('/bin/ls')能成功加载说明文件解析、binutils 调用正常;asm('nop')返回90说明汇编和 Capstone 也正常。这两条过了,pwntools 的绝大多数功能就都能用了。
到这里,一个最基础可用的 pwntools 环境已经搭建完成。但对于真正拿它做题或者做研究的人来说,还可以进一步把配套工具链和日常使用习惯配置好。接下来这部分是实战经验,很多人会忽略。
4. 配套工具链与 Shell 配置
4.1 pwntools 的黄金搭档:ROPgadget、one_gadget、pwndbg
只有 pwntools 孤零零一个工具是远远不够的。我日常做二进制利用时,还离不开下面这几个工具,它们和 pwntools 配合得好,效率会翻倍。
ROPgadget是一个搜索 ROP 指令片的工具,它不依赖 pwntools 也能用,但经常在 pwntools 脚本里以子进程方式被调用。安装很简单:
pip install ROPgadget装好后,你可以在 pwntools 脚本里通过ROPgadget命令间接调用,也可以直接在终端用ROPgadget --binary ./vuln --only "pop|ret"来找可用 gadget。说实话,它的搜索能力比 pwntools 内置的 ROP 类在某些场景下更精细。
one_gadget是另一个高频工具,用来在动态链接库里搜索一条就能 getshell 的 gadget。对于很多现代 libc 题目,一条 one_gadget 约束条件可能已经够了,能省掉冗长的 ROP 链构造。它是 Ruby 写的,需要先装环境再装工具:
sudo apt install -y ruby-full sudo gem install one_gadget安装完成后,执行one_gadget /lib/x86_64-linux-gnu/libc.so.6就能列出所有可用地址。
pwndbg则是 gdb 的增强插件,和 pwntools 配合使用可以非常直观地调试程序栈、堆、寄存器。pwndbg 的安装方式官方推荐一条脚本,但我建议你先别急着装,等 pwntools 和 one_gadget 都稳定了再装,避免一次装太多环境变量互相干扰。
4.2 虚拟环境自动激活配置:省掉反复 source
虚拟环境好是好,但有个缺点:每次打开新终端,都得手动执行一遍source ~/pwnenv/bin/activate。对这个烦琐步骤,我不建议直接改.bashrc让它自动激活,因为以后你很可能会有多个虚拟环境,自动激活会导致混乱。我推荐用一个别名,比如在~/.bashrc里加一行:
alias pwnenv='source ~/pwnenv/bin/activate'重新加载终端或者执行source ~/.bashrc后,只要敲pwnenv就能激活,简洁又不容易误伤其他项目。
如果你的项目多了,想按目录自动切换环境,可以考虑direnv工具:在项目目录里写一个.envrc,内容为source ~/pwnenv/bin/activate,进入目录就自动激活,离开目录自动退出。这个方法体验很好,适合以后维护多个 CTF 仓库时用。
4.3 SSH 与 WSL 下的使用技巧
很多人在虚拟机里操作终端时,复制粘贴特别难受。我的建议是在 Ubuntu 虚拟机里开启 SSH 服务,然后用本机 VSCode 的 Remote-SSH 插件连进去,代码编辑和命令输入都丝滑很多。在 Ubuntu 22.04 及以上版本,执行:
sudo apt install -y openssh-server sudo systemctl enable ssh --now然后在本机用ssh ubuntu@虚拟机IP就能连上。这个技巧对我个人来说极大提升了做题节奏,因为每次在终端里粘贴长 payload 的时候,没 SSH 真的是噩梦。
如果你用的是 WSL2,并且希望 pwntools 拉到 gdb 窗口做可视化调试,需要注意 WSL2 默认带 WSLg 支持,大部分图形窗口可以直接显示。但是如果遇到窗口打不开的情况,大概率是 WSLg 没有运行,这时候要么重启 WSL,要么在 WSL 侧安装一个 X server(比如 VcXsrv),并把DISPLAY环境变量配置好。相比之下,虚拟机在这块几乎没有额外的配置成本。
5. 常见问题与排查技巧实录
5.1 安装过程中的高频报错与解决方案
我在各种群里见过太多次类似的求助,这里把最常见的几个问题整理成表格,方便你直接对照解决。
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
apt update连接不上或极慢 | 软件源在国外或 DNS 异常 | 换国内镜像源,见 2.1 节 |
pip install pwntools超时中断 | pip 默认源访问慢 | 配置 pip 国内镜像源后重试 |
| 安装 capstone 时编译失败 | 缺少编译依赖 | 先安装build-essential libcapstone-dev |
提示python3-venv找不到 | 当前 Ubuntu 版本 venv 包名不同 | 用apt search python3.*-venv搜索对应版本包安装 |
| pip 提示权限不足 | 在系统 Python 下用了 pip | 进入虚拟环境后重新安装,不要 sudo pip |
运行 pwntools 报Could not find binutils | binutils 未安装 | sudo apt install -y binutils |
| 出现 GLIBC_xxx not found | 系统 libc 版本过低,或用了新版本的二进制 | 升级 Ubuntu 版本,或使用 Docker 固定环境 |
这里面最容易被忽视的其实是libcapstone-dev。因为 capstone 是一个 C 语言写的库,pip 安装时需要从源码编译,编译过程中要调用它的头文件。很多人前面一路顺风,最后在 capstone 编译环节挂掉,其实就是这一条没装。我见过有人用apt install capstone装系统包,但 pwntools 走的是 Python 绑定,和系统 capstone 并不是同一个扩展名,所以正确做法还是装libcapstone-dev来补编译环境。
5.2 使用阶段最容易踩的坑
安装完不代表就万事大吉,真正使用 pwntools 时,下面这几个坑是我自己踩过无数次的,属于高频区。
激活了虚拟环境,但which python显示的还是/usr/bin/python。这个问题多半是你用某个 IDE 或编辑器内置终端时,环境变量没继承。解决办法是确保当前 shell 里能看到(pwnenv)前缀,然后执行which python确认路径在~/pwnenv/bin/python。
from pwn import *报ModuleNotFoundError。首先检查虚拟环境是否激活,然后执行pip show pwntools看包是否真的存在。如果你之前用 sudo pip 装过其他版本的 pwntools,还可能是系统包干扰,建议在干净虚拟环境里重装。
调用ELF()时程序解不开,报Could not find binutils。这个就是系统里缺 binutils,或者 PATH 里没有它的路径。Ubuntu 默认装在/usr/bin,正常不会丢,但如果你修改过 PATH 导致找不到,执行which as和which objdump检查一下。
写了 exploit 之后在本地跑不起来,sendlineafter一直卡住。这种问题多半不是 pwntools 的问题,而是程序交互逻辑的问题,比如没等到预期的字符串、程序在等待输入时缓冲未刷新。我会在 pwntools 脚本里先context.log_level = 'debug',把收发数据打出来,一眼就能看到卡在哪个环节。
远程题目需要切换 libc 版本时,直接用patchelf --replace-needed libc.so.6 ./libc-2.31.so ./vuln来替换加载路径,比你去手动改 ELF 头靠谱得多。shellcode 生成后记得确认架构,比如 64 位程序就不能用默认的 i386 shellcode。
5.3 问题排查速查表
为了方便你以后快速定位,我把排查步骤总结成一张速查表。遇到问题时,从上往下走一遍:
| 步骤 | 命令/操作 | 预期结果 |
|---|---|---|
| 检查虚拟环境 | which python; which pip | 路径指向~/pwnenv/bin/ |
| 检查 pwntools 版本 | pip show pwntools | 看到版本号,无报错 |
| 检查 binutils | which as; which objdump | 都能返回路径 |
| 检查 gdb | which gdb | 能返回路径 |
| 最小功能验证 | 执行 3.3 节 Demo | 正常输出 ELF 路径和 nop 汇编结果 |
| 开着 debug 跑脚本 | context.log_level = 'debug' | 能看到详细的收发与调试日志 |
很多看起来神秘兮兮的问题,其实都逃不过这六步的层层筛选。记住:先把环境确认清楚,再怀疑你的 exp 代码。
最后的一点个人体会
装好 pwntools 之后,下一步我建议你写一个小 demo,用process()把一个本地程序跑起来,尝试构造一个最简单的栈溢出 exp,完整走一遍“利用”的闭环。这里的价值不在于多么炫技,而在于你会开始真正理解 pwntools 每个接口背后对应的现实需求。我每次重装环境后都会用这趟流程给自己做一次环境体检,大概不到十分钟就能确定一切正常。希望这份折腾经验也能帮你少走点弯路。