NOTETips:
- 本教程于 2026.1 更新
由于教程是在 WSL 子系统教程之后编写的,只作为针对新手的补充,后续可以参照子系统的教程,相对来说没有子系统的教程细致,但是更适合新手
- 使用 VMWare:VMWare Linux 基础配置
- 使用 WSL:WSL Linux 基础配置
- 工具安装:基础 Pwn 环境安装
- 本教程默认你有了基本的计算机操作水平并且已经解决了任何网络问题
- 由于网页和图床代理到 Cloudflare,如果你浏览有些卡顿甚至加载失败,请自行加速并刷新
1. Linux 下 Pwn 基础环境安装
IMPORTANT确保你使用了
pyenv,conda,uv,原始的venv等虚拟环境,或确保你可以使用pipx进行包管理,如果你没有准备环境,过度的话也可使用apt安装
1-1 安装 Pwntools
Pwntools 是一个 Python 库,专门用于写利用脚本,核心功能包括构造 payload,自动处理网络连接,解析 ELF 文件,处理 ROP,远程交互,生成 ShellCode 等
那你要问我,为什么不手动呢?手动 nc 只能做一次性试验,而真实利用往往涉及地址泄露、偏移计算、分阶段 payload、反复重连和自动化判断,人工既低效又容易出错,Pwntools 把整个攻击流程写成脚本,让打包、解析、计算和交互全部自动完成,从而实现可复现、可调试、可批量运行的 exploit,这在复杂场景下几乎是必需品
我们会向 Python 环境中安装这个包,如果不用 apt 安装请务必激活当前的 Python 环境
<br>pip install pwntools
pipx install pwntools
sudo apt updatesudo apt install python3-pwntools
uv pip install pwntools
pythonfrom pwn import *
倘若没有什么报错,说明你的导入非常成功,只不过要注意,如果你是安装到了一个虚拟环境中,记得要先激活环境才能使用
1-2 安装 GDB 以及 Pwndbg / gef
TIP对于写文章的时候来说,由于常用的 GDB 插件 Pwndbg 最新的安装方式已经不是网络上存在的克隆仓库并执行
setup.sh了,参考下文的一键安装,会自带 GDB 和 Pwndbg,可以考虑跳过下面
首先需要安装 GDB
sudo apt install gdb gdbserver gdb-multiarch在此基础上我们会安装 Pwndbg / Pwngdb /peda / gef ,事实上 Pwndbg 是目前最常用的,gef 的非官方一个版本也很厉害,他们可以满足大部分需求
1-2-1 安装 Pwndbg
TIP通过一键安装的 Pwndbg 和 GDB 默认支持异架构,无需使用 multiarch 版本
,如果不支持,那你没办法
项目链接:pwndbg/pwndbg: Exploit Development and Reverse Engineering with GDB & LLDB Made Easy
参考上面的链接,新版简化了安装流程,无需从源码安装,我们直接可以
apt install xz-utilscurl -qsL 'https://install.pwndbg.re' | sh -s -- -t pwndbg-gdb就安装好了,输入 pwndbg 即可使用
如果你想自定义安装位,你可以前往 Releases 下载压缩包解压到合适的位置
apt install xz-utilschmod 777 pwndbg_2025.05.30_x86_64-portable.tar.xztar -xvf pwndbg_2025.05.30_x86_64-portable.tar.xz解压之后会有四个文件夹出现,我们可以将当前文件夹的 bin 目录添加到环境变量,也可以将文件夹下的 bin 里的可执行文件链接到系统的任意环境变量目录下,这里采用了后者
sudo ln -s /path/to/pwndbg/bin/pwndbg /usr/bin/pwndbg测试一下
pwndbg
1-2-2 集成 Pwngdb 与去重
项目地址:scwuaptx/Pwngdb: gdb for pwn
在任意目录下克隆库
git clone https://github.com/scwuaptx/Pwngdb.git克隆之后你可以看到 angelheap 和 pwngdb.py
修改 Pwndbg 启动脚本
vim /path/to/pwndbg/bin/pwndbg#!/bin/shdir="$(cd -- "$(dirname "$(dirname "$(realpath "$0")")")" >/dev/null 2>&1 ; pwd -P)"export TERMINFO_DIRS=/etc/terminfo:/lib/terminfo:/usr/share/terminfo:/run/current-system/sw/share/terminfo:$dir/share/terminfoexport PYTHONNOUSERSITE=1export PYTHONHOME="$dir"export PYTHONPATH=""export PATH="$dir/bin/:$PATH"
exec "$dir/lib/ld-linux-x86-64.so.2" "$dir/exe/gdb" --quiet --early-init-eval-command="set auto-load safe-path /" --command=$dir/exe/gdbinit.py "$@"
exec "$dir/lib/ld-linux-x86-64.so.2" "$dir/exe/gdb" --quiet --early-init-eval-command="set auto-load safe-path /" --command=$dir/exe/gdbinit.py --command=/path/to/pwngdb.py --command=/path/to/angelheap/gdbinit.py "$@"可以使用一些特殊的命令验证是否安装成功
安装此插件会导致 Pwndbg 的 canary 以及 got 命令被替换,对于喜欢 pwndbg 的命令(或者是因为颜色)的人来说,你可以去搞一下这个
其实作者留下了一个兼容的脚本,就在 Pwngdb/pwndbg 中,不过由于更新问题兼容较为麻烦,这里我们有一种简单粗暴的方法,修改 Pwngdb/pwngdb.py
vim /path/to/Pwngdb/pwngdb.py ... other ...
def tls(self): """ Get tls base """ print("\033[34m" + "tls : " + "\033[37m" + hex(gettls()))
# def canary(self): # """ Get canary value """ # print("\033[34m" + "canary : " + "\033[37m" + hex(getcanary()))
def fmtarg(self,*arg): (addr,) = normalize_argv(arg,1) getfmtarg(addr)
... other ...
def findsyscall(self): """ find the syscall gadget""" arch = getarch() start,end = codeaddr() if arch == "x86-64" : gdb.execute("find 0x050f " + hex(start) + " " + hex(end) ) elif arch == "i386": gdb.execute("find 0x80cd " + hex(start) + " " + hex(end) ) elif arch == "arm": gdb.execute("find 0xbc80df00 " + hex(start) + " " + hex(end) ) elif arch == "aarch64": gdb.execute("find 0xd4000001 " + hex(start) + " " + hex(end) ) else : print("error")
# def got(self): # """ Print the got table """ # processname = getprocname() # if processname : # cmd = "objdump -R " # if iscplus : # cmd += "--demangle " # cmd += "\"" + processname + "\"" # got = subprocess.check_output(cmd,shell=True)[:-2].decode('utf8') # print(got) # else : # print("No current process or executable file specified." )
def dyn(self): """ Print dynamic section """ processname = getprocname() if processname : dyn = subprocess.check_output("readelf -d \"" + processname + "\"",shell=True).decode('utf8') print(dyn) else : print("No current process or executable file specified." )
... other ...我们直接找到 /path/to/Pwngdb/pwngdb.py 使用直接注释法,把上图两个位置的函数注释掉就行,效果对比,注释之后是
和注释之前

1-2-3 如何为依赖 Python 环境安装包
在以前的教程中是创建一个虚拟环境来安装 Pwndbg 的依赖,而官网最新的环境则是开箱即用的,无法通过正常途径为 Python 安装第三方的包,这里主要说明如何借一个 pip 来达成目的
我们首先需要确定 Pwndbg 的 site-packages 文件夹在哪里,你应该找到你的安装目录,然后找到下面的目录
cd /path/to/Pwndbg/lib/python3.12/site-packages/确定了路径之后,我们随便借一个 pip,这里使用了 uv 的 pip 安装一个包,使用 --target 参数指定目录
uv pip install --target=/path/to/Pwndbg/lib/python3.12/site-packages decomp2dbg1-2-4 gef 安装(可以用于内核 / 异架构)
仓库链接:bata24/gef: GEF - GDB Enhanced Features - bata24’s fork
一键安装,但是安装到了并且只推荐以 root 身份安装
wget -q https://raw.githubusercontent.com/bata24/gef/dev/install-uv.sh -O- | sudo sh由于作者亲自说明是在 root 环境下测试,所以这里保持原位置,为了方便使用,可以:
sudo vim /usr/bin/gef#!/bin/shsudo /usr/bin/gdb "$@"然后赋予可执行权限
sudo chmod 777 /usr/bin/gef效果图

1-2-5 设置默认 GDB(可选)
这个基于个人习惯,如果习惯默认使用 Pwndbg 而只想输入 的话可以改一下名字
sudo mv /usr/bin/gdb /usr/bin/gdb-orisudo mv /usr/bin/pwndbg /usr/bin/gdb如果更改的话记得上面的 gef 脚本要更改对应 GDB 二进制文件为对应名称,因为上文提到的 gef 插件是使用系统的 GDB 加载脚本运行的
sudo vim /usr/bin/gef#!/bin/shsudo /usr/bin/gdb-ori "$@"如果是脚本里用可以指定
context.gdb_binary = '/path/to/gdb'1-3 安装其他小部件
记得一定要激活环境,请根据当前的环境管理方式自行决定工具的安装方式
conda activate <name>1-3-1 ROPgadget 与 ropper
用于搜寻在 ROP 攻击时使用的 gadget
apip install ROPgadget
sudo apt install ropper

1-3-2 one_gadget
用于在 libc 库中找到一个直接执行可以 getshell 的地址
sudo apt install ruby ruby-dev gcc makesudo gem install one_gadget1-3-3 seccomp-tools
用于检测程序启用的沙箱保护
sudo gem install seccomp-tools
1-3-4 patchelf 与 glibc-all-in-one
由于题目在远程部署的环境和本地一定不相同,在某些对 libc 版本要求高的题目中需要更换指定的 libc
pip install patchelfgit clone https://github.com/matrix1001/glibc-all-in-one.gitglibc-all-in-one 官方页面在:
matrix1001/glibc-all-in-one: 🎁A convenient glibc binary and debug file downloader and source code auto builder
可以参考进行进一步下载不同版本 libc 的方式
cd ./glibc-all-in-one
./update_list # 更新列表cat list # 查看列表
./download <name> # 开始下载在之后使用 patchelf 即可
patchelf --set-interpreter <ld 位置> <文件位置>patchelf --replace-needed <需替换的 libc 类型,例如 "libc.so.6"> <libc 位置> <文件位置>或者patchelf --set-rpath <'动态库文件夹位置1(必选):动态库文件夹位置1(可选):......'> <文件位置>2. Pwn 基础工具
2-1 IDA Pro
2-1-1 基础安装
WARNING这是一个商业软件,原则上你需要付费购买正版使用
运行安装程序即可,可以设置安装路径
IDA 集成了 Python 环境,不仅可以直接执行 Python 命令,也可以为各种插件提供支持,没有疑惑的话直接勾选安装吧,如果你有自己的环境的话可以参考下面的自定义教程
第一次运行的时候会要求选择许可证,选择你拥有的对应的 idapro.hexlic 文件就可以了
NOTE如果你使用的是某些神秘版本,你可能会找到一些神秘的
*.dll文件,你需要做的就是替换到安装目录的ida32.dll和ida,dll然后激活神秘的许可证文件,或者是某些神秘*.py脚本,在安装目录下执行脚本就可以了
效果如下图
2-1-2 自定义 IDAPython 环境
在安装目录找到 idapyswitch.exe
如果双击可以打开,那你可以通过数字选择你想使用的环境(最好是独立的环境)
如果没有想用的环境或者是根本是一闪而过的窗口的话,我们可以在当前目录起一个命令窗口执行这个程序,并使用 --force-path 参数来指定 Python 的地址,一般选择 python3.dll ,来源最好是官方安装包,版本推荐 3.11
.\idapyswitch.exe --force-path "F:\Tools\Disassembler\IDA Python\python3.dll"
2-1-3 安装插件
安装插件的方法各异,需要针对不同插件,这里就不赘述了,总体来说分为三种
- 扔到安装目录的
plugins文件夹 - 在 IDAPython 中安装必要的包和依赖
- 在其他环境中安装必要的包和依赖
怎么证明安装成功了呢,可以看下面的 Output 有没有报错,看看能不能使用插件功能
我用的有下面的一些,感觉足够日常使用了,这里就不贴链接了 - finger_plugin
- keypatch
- LazyIDA
- ida-pro-mcp
- patching
- decomp2dbg
2-2 Ghidra
仅作介绍:这是一款由美国国家安全局开源的反编译工具,你可以前往 Github 下载,可以安装很多的插件,但是效果不如 IDA,对于某些冷门的架构而你没有购买 IDA 的高级商业版本的情况下可能有奇效
2-3 Binary Ninja
WARNING和 IDA Pro 一样,这是一个商业软件,原则上你需要付费购买正版使用
仅作介绍:和 IDA 差不多,也是一个商业软件,使用起来也不错,据说有官方带头的插件和 MCP 生态,周围有氪金使用的用起来很爽,不过入门的话其实也差不多,本人用的也不是很多,可以考虑备一个
3. Pwn 特殊环境 - C++
WARNING并非完全体,还在更新
当我们目光看向一道 C++ 的题目,正常的 C++ 题目都会提供下面这几样库文件,使用 patchelf 更改就行
如果有些只给出了 libc.so.6 的话,你就需要手动下载其他的文件
3-1 下载对应发行版本的 libstdc++.so.6
3-1-1 从 Docker / 对应 Linux 里扒
很简单,如果题目给 Dockerfile 了,可以尝试部署题目的 Docker 环境,把用的库拷贝出来,使用 patchelf 或其他方式更改,如果没有就找 libc 或者是程序字符串寻找版本特征并从对应 Docker 或者 Linux 系统把缺的库扒出来
3-1-2 手动下载
如果你发现 patch 完之后会说你的 libstdc++.so.6 需要更高版本,那么你可能需要更换一下这个库,一般来说这个东西不吃小版本,也没必要对应 Ubuntu 的版本下载了,直接前往 Debian 官网下载对应版就行
访问官网:Debian — 软件包
点击下面有搜索,直接搜索
你要找的就是下面这样的,叫做 libstdc++6-xx-dbg 的包
如何确定你需要哪个?点进去看一眼版本
确认版本后就可以下载,如果程序需要的话,你可能还需要下载一个 libgcc_s.so.1
同时下面还有 dev 包供你选择,内含源码(可选,一般用不到)

3-2 替换程序使用的库
经测试,libm.so.6 有一定概率无法使用 patchelf 的 --set-interpreter 进行修改,我们可以通过指定环境变量 LD_PRELOAD 来强制替换该库,它的位置和 libc 与 ld 坐一桌,正常运行只需要
LD_PRELOAD=/path/to/target.so ./pwn# 示例 LD_PRELOAD=./libm.so.6 ./pwn如果你要引入多个,使用 : 隔开
LD_PRELOAD=./libc.so.6:./libstdc++.so.6:./libm.so.6:./libgcc_s.so.1 ./pwn