我们的 C 程序编译通过了,但一运行就 Segmentation fault (core dumped)。没有堆栈回溯、没有行号、只有冷冷的一行错误——我们盯着屏幕,不知道是哪个指针越了界、哪次 strcpy 写穿了缓冲区。我们想用 GDB 加载 core dump,发现二进制是 stripped 的,符号表被剥离了;想查安全特征,看看编译时到底有没有开 Stack Canary,又不知道从哪里下手。
这时候需要的是一个能读懂 ELF 文件的搭档。ELF(Executable and Linkable Format)是 Linux 系统上所有可执行文件、共享库和内核模块的通用格式。我们需要的答案——架构、符号、安全特征、库依赖、反汇编——全都写在 ELF 的结构里,只是散落在几十个命令的输出中,等着人去拼凑。
本文从 ELF 基础知识出发,介绍一套专门为此设计的 Claude Code 分析技能——elf-analyzer(ELF 结构分析)、binary-reverse(逆向工程)和 linux-pwn(漏洞利用)。这三个技能将专业的二进制分析知识编码为 AI 可执行的指令,不用记住几十个命令行参数,也能像资深工程师一样拆解 ELF 文件的秘密。
打开终端,运行一条最简单的命令:
file /bin/ls会看到这样的输出:
/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),dynamically linked (uses shared libs), for GNU/Linux 2.6.32, not strippedELF —— Executable and Linkable Format(可执行和可链接格式)—— 是 Linux 世界的通用语言。我们运行的每一个命令、加载的每一个共享库(.so 文件)、内核加载的每一个模块(.ko 文件),甚至操作系统内核本身,都遵循 ELF 规范。

入门文章 - Linux 二进制文件格式 ELF 入门为我们打开了理解 ELF 的大门。从 ELF Header 中解读架构(32 位还是 64 位?)、端序(小端还是大端?)、文件类型(可执行还是共享库?),到程序头和节头的区别,再到静态链接与动态链接的分野——这些基础知识是二进制分析的地基。
正如那篇 elf_101 文章指出的,理解 ELF 格式的动机来自四个方向:
这四个场景有一个共同点:它们都需要我们阅读并理解 ELF 文件的内部结构。而这正是我们技能设计的起点。
亲手分析过一个 ELF 文件的人,一定经历过这三道坎。
elf_101 文章帮我们理解了 ELF 的基础结构,但动手分析时很快就会发现——命令实在太多了。
分析一个 ELF 文件至少需要七八个命令:
file binary # 初步识别readelf -h binary # ELF 头部readelf -l binary # 程序头(运行时视图)readelf -S binary # 节头(链接视图)readelf -d binary # 动态链接信息readelf -s binary # 符号表objdump -d binary # 反汇编strings binary # 字符串提取ldd binary # 库依赖每个命令还有各种参数变体,不同发行版上命令名称可能不同。readelf 还是 eu-readelf?objdump 还是 llvm-objdump?checksec 安装了吗?每切换到一个新环境,就要重新梳理一遍工具链。
readelf -S /usr/bin/ps 输出 29 个 section header,objdump -d 可能输出几万行反汇编。真正的关键信息——比如 "这个二进制有 Full RELRO,GOT 不可写"——隐藏在大量元数据中。从这些输出中手动提取关键决策信息,是一个费时费力的过程。
最重要的不是"看到什么",而是"这意味着什么"。看看这些场景:
GNU_STACK 的 Flags 是 RW(不是 RWE)—— 意味着 NX 启用,栈不可执行,需要 ROPBIND_NOW 和 GNU_RELRO 同时存在 —— 意味着 Full RELRO,GOT 覆写路径封堵Type: EXEC —— 意味着非 PIE,地址固定,不需要信息泄露就能构建 ROP 链not stripped —— 意味着符号表完整,可以直接用函数名定位这些"如果-那么"的决策链,构成了资深工程师与新手的本质差异。但谁来帮我们记住并在合适的时机提醒这些决策呢?
基于对 GitHub 上 6 个核心技能仓库和 4800+ 个 SKILL.md 文件的深入调研,我们构建了三款 Claude Code 技能。它们分别覆盖 ELF 分析链路的不同阶段,可以独立使用,也可以接力串联。
┌──────────────────┐ │ elf-analyzer │ ← ELF 格式分析 (起点) │ (8 文件, 2541行) │ └────────┬─────────┘ │ triage 结果 ┌──────────────┼──────────────┐ ▼ │ ▼ ┌───────────────────┐ │ ┌──────────────────┐ │ binary-reverse │◄─────┘ │ linux-pwn │ ← 漏洞利用 │ (6 文件, 2038行) │ │ (7 文件, 2037行) │ │ 通用逆向工程 │ │ 二进制漏洞利用 │ └───────────────────┘ └──────────────────┘一句话定位:自动完成从文件识别到安全态势评估的全流程。
当我们把 /usr/bin/ps 交给它,它会自动完成 elf_101 文章中描述的所有检查步骤——file → checksec → readelf -h/-l/-d/-s → ldd → strings——并输出一份结构化的分析报告。
它的知识库(7 个 reference 文件)覆盖了 ELF 的所有核心维度:
elf-triage.md | |
elf-headers.md | |
elf-dynamic.md | |
elf-security.md | |
static-analysis.md | |
dynamic-analysis.md | |
unpacking.md |
使用示例:
在 Claude Code 中打开终端,直接对话:
"分析 /usr/bin/ps 的安全特征"
Claude 会自动探测工具链、执行 triage、对比 elf-security.md 中的规则,然后输出:
## ELF 分析结果: /usr/bin/ps### 基本信息- 架构: x86-64 | 端序: little-endian | 链接: dynamic- Stripped: yes | 加壳: no### 安全态势| RELRO | Canary | NX | PIE | seccomp | FORTIFY ||-------|--------|----|----|---------|---------|| Full| Yes | Yes| Yes| No | Yes |### 关键发现- Full RELRO: GOT 完全只读,无法 GOT 覆写,需转向 ret2libc 或其他攻击路径- NX enabled: 需要 ROP/ret2libc- PIE enabled: 需要信息泄露获取代码基址- 反调试检测: ptrace 调用未发现### 进一步建议- 如需漏洞利用 → 切换到 linux-pwn 技能- 如需深入理解代码逻辑 → 切换到 binary-reverse 技能一句话定位:跨越反汇编、混淆和编程语言的逆向工程工具箱。
Triage 回答了"这个二进制有什么特征",binary-reverse 回答了"它的代码在做什么"。它覆盖了 elf_101 文章中提到的"更进一步"的部分——当 strings 和 file 不够用的时候。
知识库(5 个 reference 文件):
disasm-patterns.md | push rbp; mov rbp, rspstp x29, x30, [sp, #-32]! 是什么意思?495 行的模式库 |
crypto-patterns.md | 63 7C 77 7B 是什么?362 行的密码学指纹库 |
anti-analysis.md | ptrace(PTRACE_TRACEME) 怎么绕过?455 行的反分析技术全解 |
language-specific-re.md | Option/Result 在反汇编中长什么样? |
advanced-tools.md |
使用示例:
"这个二进制里有一段 XOR 循环,帮我分析它在做什么"
Claude 会加载 disasm-patterns.md 和 crypto-patterns.md,将反汇编片段与模式库对照:
检测到的模式:1. 函数序言: 标准 x86-64 (push rbp; mov rbp, rsp; sub rsp, 0x40)2. XOR 循环: xor [rsi+rcx], 0x55 → 单字节 XOR 解码,key = 0x553. 后续调用: call printf@plt → 解码结果被打印4. 未检测到 AES/RC4/Base64 常量结论: 这是一个简单的 XOR 字符串去混淆函数,key = 0x55。或者面对一个 Go 编译的二进制:
"这个二进制是 Go 写的,stripped 了,帮我恢复函数名"
Claude 会加载 language-specific-re.md,自动调用 GoReSym,基于 pclntab 恢复函数符号。
一句话定位:从漏洞发现到完整 exploit 的技术全栈。
当 elf-analyzer 完成了安全态势评估,linux-pwn 接手回答"这能利用吗?怎么利用?"
知识库(6 个 reference 文件):
exploit-primitives.md | gets()printf(user_input) 能怎么利用?漏洞评分算法 |
heap-exploitation.md | |
rop-advanced.md | pop rdx 怎么办?ret2csu 万能 gadget 怎么用?SROP 一次调用如何控制所有寄存器? |
format-string-deep.md | %n__printf_chk 如何绕过? |
kernel-exploitation.md | prepare_kernel_cred + commit_creds 链怎么构造?KASLR 怎么绕过? |
kernel-modules.md |
使用示例:
用户: "这个二进制 checksec 显示 NX enabled, PIE disabled, Partial RELRO, Canary disabled。而且代码里用了 gets(),怎么利用?"
Claude 会加载 exploit-primitives.md → 自动路由到 rop-advanced.md:
安全态势: NX=Yes, PIE=No, RELRO=Partial, Canary=No利用策略:1. gets() 无条件栈溢出 → 覆盖返回地址2. NX enabled → 不能直接栈 shellcode → 需要 ROP/ret2libc3. PIE disabled → 所有代码地址固定 → 不需要泄露基址4. Partial RELRO → GOT 可写 → ret2dl_resolve 也可行5. Canary disabled → 无需泄露 canary,直接溢出即可推荐路径 (最简单): ROP 链: pop rdi; ret → "/bin/sh" 字符串地址 → system@plt (所有地址固定,objdump 直接查)进阶路径 (不依赖 libc 版本): ret2dl_resolve: 伪造 Elf64_Sym("system") → _dl_runtime_resolve用户: "如果这个二进制是 PIE enabled 呢?"
区别: PIE enabled → 代码基址随机 → 需要先泄露一个代码段地址额外步骤: 在 ROP 链之前,先通过 puts@plt 泄露任意 GOT 条目中的libc 地址或栈上的返回地址,计算基址。其余 ROP 链结构不变——所有 gadget 偏移加上泄露的基址即可。每个技能都内建了 token 预算管理机制,遵循四条铁律:
strings、objdump -d、strace 日志)写入 case 目录,用 rg 检索这意味着即使是分析一个复杂的、加壳的、stripped 的 ELF 样本,技能也不会消耗无谓的上下文。
让我们用一个具体例子串联三个技能的协作流程。假设我们拿到一个陌生的 ELF 文件 suspicious.elf。
"分析 suspicious.elf"
elf-analyzer 自动执行: file suspicious.elf → ELF 64-bit, x86-64, dynamically linked, stripped checksec → RELRO: Full, Canary: Yes, NX: Yes, PIE: Yes readelf -l → 2 个 LOAD segments, GNU_STACK=RW, GNU_RELRO present readelf -d → BIND_NOW present → Full RELRO confirmed strings → 发现 URL "hxxp://evil-c2.example.com/api/beacon" → 发现路径 "/tmp/.hidden/init.so" → 发现 "UPX!" 字符串 ldd → libc.so.6, libpthread.so.0关键发现: - UPX 加壳!→ 切换到脱壳流程 - Full RELRO → GOT 不可写 - NX+Canary+PIE 全开 → 利用难度较高"先脱壳,然后分析它的 C2 通信逻辑"
upx -d suspicious.elf -o unpacked.elf → 成功脱壳re-analyze unpacked.elf → not stripped (符号表恢复)切换到 binary-reverse: - 识别 C2 URL 的引用函数 - 发现 XOR 解码循环 (key = 0x2F) - 追踪到 socket → connect → send 调用链 - 确认 C2 通信协议: HTTP POST, User-Agent 硬编码"这个 C2 通信函数里有 recv() 写入 256 字节栈缓冲区,能利用吗?"
切换到 linux-pwn: 安全态势回顾: Full RELRO, Canary, NX, PIE 全开 漏洞: recv(sock, stack_buf[128], 256, 0) → 128 字节溢出 路由决策: - Canary → 需要先泄露 canary - NX → 需要 ROP/ret2libc - PIE → 需要泄露代码基址 - Full RELRO → GOT 不可写,走 ret2libc(通过 PLT 直接调用 system,无需覆写 GOT) 推荐路径: 1. 先利用 recv 溢出 1 字节 → 泄露 canary 2. 再通过 recv 泄露返回地址 → 计算 PIE 基址 3. 最后构造 ROP 链: pop rdi; ret → "/bin/sh" → system@plt最初的设计是一个单一的 elf-analyzer 技能,包含了从 triage 到 kernel exploit 的所有知识。但随着知识库膨胀到 19 个文件(6495 行),我们意识到每次分析都携带不相关的知识是对 token 的大量浪费。
拆分为三个技能后:
每个技能保持在 2300 行以内,初始 SKILL.md 在 100-150 行,大幅降低了首次加载的 token 消耗。
这些技能的价值不在于"替代我们的知识",而在于"补充我们的盲区":
readelf -d 的输出怎么映射到 Full/Partial RELRO 判定——技能会做但我们仍然做最终的决策。技能是工具,不是替代。
技能的 reference 文件设计为活文档——每次分析中发现的新模式(新的密码学常量、新的反分析技术、新版本的 glibc 变化)都可以追加到对应的 reference 中。这使技能具备从实践中自我进化的能力。
本文中的例子涉及两类操作:静态分析(在任何平台均可)和动态分析(需要 Linux 环境)。
macOS 本地验证(静态分析):
# 安装 GNU binutils(提供 greadelf/gobjdump/gnm/gstrings)brew install binutils radare2 upx# 设置别名(或加到 ~/.zshrc)export PATH="/opt/homebrew/opt/binutils/bin:$PATH"alias readelf=greadelfalias objdump=gobjdumpalias nm=gnm以上安装后,文章中 file、readelf -h/-l/-S/-d/-s、objdump -d、nm、strings、hexdump 等静态分析命令均可直接在 macOS 终端中验证。checksec 如无法通过 pwntools 安装(Python 3.14+ 兼容性问题),可使用等效命令替代(见 elf-triage.md 第 2 步的手动检查方法)。
Docker 环境(动态分析 + 运行样本):
# 启动隔离容器,挂载当前目录docker run -it --rm --name elf-lab \ -v "$(pwd):/work" \ ubuntu:22.04 bash# 一次性安装所需工具apt update && apt install -y \ binutils file strace ltrace gdb \ python3 python3-pip radare2 upx-ucl \ pax-utils elfutils netcat-openbsdpip3 install pwntools --break-system-packages文章中涉及 strace、ltrace、GDB 调试、upx -d 脱壳、运行二进制样本等操作,建议在容器中进行——天然隔离,用完即弃。
测试用 ELF 文件:如没有现成的 Linux ELF 文件,可用一行汇编快速创建一个:
echo '.globl _start; _start: mov \$1,%rax; mov \$1,%rdi; lea msg(%rip),%rsi; mov \$13,%rdx; syscall; mov \$60,%rax; xor %rdi,%rdi; syscall; .data; msg: .ascii "Hello, ELF!\\n"' > /tmp/minimal.sclang --target=x86_64-linux-gnu -nostdlib -static -fuse-ld=lld -Wl,-e_start -o /tmp/test-elf /tmp/minimal.sfile /tmp/test-elf# → ELF 64-bit LSB executable, x86-64, statically linked, not stripped# 克隆到本地后,创建符号链接到 Claude Code skills 目录ln -s "$(pwd)/skills/elf-analyzer" ~/.claude/skills/elf-analyzerln -s "$(pwd)/skills/binary-reverse" ~/.claude/skills/binary-reverseln -s "$(pwd)/skills/linux-pwn" ~/.claude/skills/linux-pwncd ~/.claude/skillsclaude然后在对话中:
Claude 会自动激活对应的技能。
SKILL.md 调度器references/├── elf-triage.md (460行) Triage 完整流程 + 辅助工具├── elf-headers.md (276行) ELF 结构深度解析├── elf-dynamic.md (323行) GOT/PLT/LD_PRELOAD/rpath├── elf-security.md (324行) 安全缓解机制全解├── static-analysis.md (323行) objdump/radare2/Ghidra├── dynamic-analysis.md (380行) GDB/strace/Frida└── unpacking.md (304行) UPX + 通用脱壳策略SKILL.md 调度器references/├── disasm-patterns.md (495行) x86-64/ARM64 模式库├── crypto-patterns.md (362行) AES/RC4/SHA/TEA 检测├── anti-analysis.md (455行) 反调试/反VM/反混淆├── language-specific-re.md (259行) Go/Rust/C++/Swift 专项└── advanced-tools.md (367行) angr/Qiling/Unicorn/BinDiffSKILL.md 调度器references/├── exploit-primitives.md (318行) 漏洞原语识别 + 评分├── heap-exploitation.md (337行) House of系列/tcache/FSOP├── rop-advanced.md (356行) ret2csu/SROP/栈枢轴├── format-string-deep.md (280行) 格式字符串深入利用├── kernel-exploitation.md (352行) 内核提权 + 保护绕过└── kernel-modules.md (302行) .ko/eBPF/io_uring 分析本文参考了 Linux 二进制文件格式 ELF 101 一文中对 ELF 格式的基础讲解,并基于对 GitHub 上 6 个核心技能仓库(malware-analysis-static、CyberSecurity-Skill、ctf-skills、Claude-Red、antigravity-awesome-skills、awesome-llm-reverse-engineering)的深入分析构建。