Pwn 学习笔记:从 ELF 结构到 GDB 调试的最短路径
目录
说明:本文用于 CTF / 本地靶场学习。请勿用于未授权的系统或网络环境。
学习 Pwn 的第一道门槛不是“会写脚本”,而是能回答三个问题:
1)这个二进制是什么?(ELF 结构、段/节、入口、依赖)
2)它怎么跑起来的?(动态链接、装载、libc、栈/堆、保护)
3)我怎么“看见它”在跑?(GDB:断点、寄存器、内存、栈回溯)
这篇笔记把这三块串成一条最短路径:看完后你应该能对一份普通 CTF Pwn 二进制完成“检查—运行—调试—定位关键点”的闭环。
一、ELF 到底是什么:从“文件”到“程序”的桥 #
在 Linux 下常见的可执行文件是 ELF(Executable and Linkable Format)。你可以把它简单理解为:
- 文件层:ELF Header、Program Header、Section Header,描述“里面装了什么”
- 装载层:Program Header 指导内核/加载器如何把内容映射进内存
- 符号层:符号表、重定位信息告诉链接器/加载器如何拼装函数与地址
1)最常用的三条命令(先形成肌肉记忆) #
file ./chall
checksec --file=./chall
ldd ./chall
它们分别帮你快速回答:
- file:确认架构(x86_64 / i386 / aarch64)、是否 stripped
- checksec:栈保护、PIE、NX、RELRO 等安全特性
- ldd:动态依赖,最关键的是 libc.so.6 的路径(后面复现/对齐环境很重要)
2)段(Segment)与节(Section)你要记住谁? #
入门阶段优先记住:
- Segment(段):运行时重要(内存映射按段来)
- Section(节):分析时重要(反编译/符号/重定位多按节组织)
你可以用:
readelf -h ./chall
readelf -l ./chall # Program Headers(段)
readelf -S ./chall # Section Headers(节)
一个直觉:程序能跑起来靠段,程序好分析靠节。
二、保护机制:checksec 输出你要会“翻译” #
checksec 常见字段(入门先掌握它们在做什么,以及对你意味着什么):
- NX:栈不可执行。传统“把 shellcode 塞栈里再跳过去”通常不行,但并不等于不能利用
- PIE:程序本体地址随机化。硬编码程序内地址会变得不稳定
- Canary:栈溢出检测。覆写返回地址前很可能要先绕过 canary
- RELRO:
- Partial:GOT 可写(一些思路更容易)
- Full:GOT 只读(很多“改 GOT”思路受限)
看到这些不要慌,把它当“地图”。你不是背套路,而是在决定“下一步该观察什么”。
三、动态链接与 libc:为什么同一题不同机器表现不一样? #
大量 Pwn 利用依赖 libc:
- 不同 libc:函数偏移不同、gadget 分布不同
- 有些结构体布局也会差异明显
所以要养成习惯:
1)确认本题对应 libc(题目是否给 libc.so.6 / ld-linux)
2)本地复现尽量用同一套 libc + loader
最重要的是你心里要有一句话:环境不一致,结果就不可靠。
四、GDB:把程序“切开”看里面发生了什么 #
入门 GDB 先把四件事练熟:
1)断点
2)单步
3)查看寄存器/栈/内存
4)回溯调用栈
1)推荐起手式(很实用) #
gdb -q ./chall
set disassembly-flavor intel
set pagination off
break main
run
然后常用观察:
info registers
x/32gx $rsp
bt
你会慢慢建立“画面感”:程序走到哪、参数放在哪、栈上发生了什么。
2)你真正要学会的:调用约定与栈 #
以 x86_64 为例:
- 参数通常在 rdi rsi rdx rcx r8 r9
- 返回地址在栈上
- rsp 指向栈顶
所以调试的核心不是“看源码”,而是回答:
- 输入进来后,被放到了哪里?(栈/堆/bss)
- 崩溃发生在哪?为什么?
- 返回前栈是什么样?有没有被污染?
五、一个安全的练手闭环:从输入到崩溃点(不写攻击脚本) #
学习阶段建议做一个通用训练闭环:
1)运行程序,输入一段较长字符串
2)如果崩溃,用 GDB 看崩溃现场
3)观察:
- rip 指向哪里?
- 栈上返回地址附近是什么?
- 你的输入在内存里出现在哪些位置?
能稳定复现崩溃现场,并把“输入 → 内存 → 控制流”的关系理清,就已经走在正确方向。
六、工具链小抄(够你写 80% 题) #
- checksec:保护机制
- readelf / objdump:结构与反汇编
- strings:关键字符串定位
- gdb:动态观察
- (可选)pwndbg / gef:让 gdb 更顺手,但建议先会原生 gdb
结语:下一步怎么学最省时间? #
建议顺序:
1)ELF + checksec + ldd(看到文件就能形成判断)
2)GDB 基本功(断点、寄存器、栈、内存)
3)栈/堆基本概念(先知道数据在哪里)
4)再进入具体题型(溢出、格式化字符串、UAF、堆风水、ROP…)
题型很多,但“看程序怎么跑”永远不变。