在二进制交付流程里,checksec 几乎是加固检查的默认起点,但很多团队把“跑出了报告”和“检查通过”混为一谈。实际落地时,真正的问题往往不是工具不会用,而是报告结果无法被可靠解读:加固缺失、检查失败、例外放行三种状态混在一起,验收结论自然站不住脚。这篇文章给出的不是单条命令,而是一套可复用的验收资源包——包含最小测试程序、报告留存脚本、异常分类表和例外审批模板,让每次检查都能排障、能复测、能追踪例外。
先厘清三个容易混淆的状态
在动手搭资源包之前,有必要先把结果语义定清楚。checksec 的每个检查项会输出一个值(比如 Full RELRO)和一个驱动颜色的状态(green、yellow、red 或 unset),机器可读格式里则是对应的字符串。参考官方文档对颜色的定义,可以建立如下分类基准:

| 状态 | 含义 | 验收动作 |
|---|---|---|
green | 缓解措施存在且有效 | 正常通过 |
yellow | 部分保护,或 checksec 无法确定状态(Unknown) | 需人工复核,不能直接算通过 |
red | 缓解缺失/禁用,或读取二进制时出错 | 视为加固缺失或检查失败 |
italic / N/A | 该检查不适用于此类二进制 | 记录为不适用,不参与通过判定 |
这里的关键认知是:red 既可能是加固缺失,也可能是检查失败。比如二进制文件权限不足、ELF 格式异常、工具版本不匹配,都可能让 checksec 报错并输出红色。如果不加区分地当作“加固缺失”来处理,排障方向就错了。因此,验收资源包的第一件事,就是把“未知结果”和“失败结果”从“确定缺失”里拆出来。
搭建最小可复现的测试环境
建议准备一个固定版本的测试二进制集合,而不是每次拿生产文件临时检查。这样做的目的是:当 checksec 输出异常时,你能用已知良好样本快速判断是工具问题还是目标文件问题。
最小测试程序可以包含三个文件:
hardened.c:启用常见加固选项编译的示例程序,作为“预期全绿”的基准。plain.c:不加任何加固选项编译的示例程序,作为“预期多红”的对照。Makefile:固化编译参数,确保每次重建结果一致。
编译时建议显式指定参数,而不是依赖编译器默认值。例如:
HARDEN_FLAGS = -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now -pie -fPIE
PLAIN_FLAGS = -no-pie -fno-stack-protector -Wl,-z,norelro
hardened: hardened.c
gcc $(HARDEN_FLAGS) -o $@ $<
plain: plain.c
gcc $(PLAIN_FLAGS) -o $@ $<注意,-D_FORTIFY_SOURCE 需要在优化级别(-O1 及以上)下才生效,-Wl,-z,relro,-z,now 对应 Full RELRO。这些细节正是验收时容易踩坑的地方——不是 checksec 报错,而是编译参数本身没生效。
报告留存脚本:把输出变成可追溯证据
仅仅在终端看一眼彩色输出是不够的。验收资源包必须包含一个能自动留存机器可读报告的脚本,建议输出 JSON 或 YAML 格式,并附带工具版本、目标文件哈希和检查时间。
下面是一个最小脚本示例,使用 checksec 的 --output=json 参数(具体参数名以你所用版本的官方文档为准):
#!/usr/bin/env bash
set -euo pipefail
TARGET="$1"
OUT_DIR="./reports/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$OUT_DIR"
# 记录工具版本与目标文件信息
checksec --version > "$OUT_DIR/version.txt" 2>&1 || true
sha256sum "$TARGET" > "$OUT_DIR/hash.txt"
# 输出 JSON 报告
checksec --output=json --no-banner --no-headers file "$TARGET"
> "$OUT_DIR/report.json" 2> "$OUT_DIR/stderr.log"
# 记录退出码
echo "exit_code=$?" > "$OUT_DIR/exit_code.txt"脚本里特意把标准错误单独留存,因为 checksec 的警告信息(比如无法读取某个段)往往只出现在 stderr,而这些信息恰恰是区分“加固缺失”和“检查失败”的重要线索。退出码同样关键——退出码为 0 只代表命令执行完成,不代表所有检查项都是 green。CI 里如果只判断退出码,很容易漏掉黄色或红色的检查项。
异常分类表:把“未知”变成可处理项
有了报告留存,下一步是建立异常分类表。这张表的目的是让任何人在看到一份报告时,都能按统一规则归类,而不是凭感觉判断。建议按以下维度分类:
| 分类 | 判定依据 | 处理动作 |
|---|---|---|
| 加固缺失 | 状态为 red,且 stderr 无错误信息,文件哈希与构建产物一致 | 回退到构建配置,调整编译参数后重建 |
| 检查失败 | 状态为 red 或 yellow,但 stderr 有读取错误、权限警告,或工具版本与预期不符 | 先排查环境问题,修复后重新检查 |
| 未知结果 | 状态为 yellow 且无明确错误信息,或 checksec 输出 Unknown | 使用 readelf、objdump 等工具人工核验底层属性 |
| 不适用 | 状态为 italic / N/A | 记录原因,不参与通过率统计 |
| 例外放行 | 检查结果为缺失,但业务方确认可接受风险 | 走例外审批流程,留存审批记录 |
一个常见的坑是:yellow 状态下的 Partial RELRO 到底算不算通过?严格来说,它表示部分保护存在,但 checksec 无法确认是否达到完整效果。对于安全要求较高的交付,建议将 Partial RELRO 视为待复核项,而不是直接放行。
例外审批模板:让放行有据可查
例外放行是最容易被滥用的环节。如果没有审批模板,口头一句“这个风险我们接受”就足以让一次检查形同虚设。建议例外审批单至少包含以下字段:
- 目标文件路径与版本哈希
- 涉及的具体检查项及当前值
- 缺失加固的潜在影响(结合 OWASP C-Based Toolchain Hardening 等参考)
- 业务方接受风险的理由
- 审批人、审批日期与有效期
- 后续缓解措施(如运行时防护、网络隔离)
一个可复用的模板示例:
# 例外审批单
- 文件:/opt/app/bin/service
- SHA256:`...`
- 检查项:RELRO
- 当前值:Partial RELRO
- 潜在影响:GOT 覆盖风险,可能导致控制流劫持
- 接受理由:该服务仅监听内网端口,且部署了 SELinux 限制
- 缓解措施:计划于 Q3 升级构建工具链后修复
- 有效期:2025-12-31
- 审批人:安全负责人 / 业务负责人例外审批单应该与报告一起归档,而不是单独存放。这样在后续审计时,可以快速追溯到“为什么这个文件没有 Full RELRO”。
复核与回滚:调整构建配置后的闭环
当检查发现加固缺失时,正确的做法是调整构建配置、重建同一目标,然后重新跑 checksec 对比报告。这个流程需要固化,否则容易出现“改了配置但没生效”的情况。
建议在验收资源包里加入一个对比脚本,自动 diff 两次报告的 JSON 输出:
diff <(jq -S . reports/old/report.json) <(jq -S . reports/new/report.json)如果 diff 结果为空,说明重建没有改变加固状态,需要回头检查编译参数是否真的传入了。如果 diff 显示部分字段从 red 变为 green,则说明修复生效。
同时,构建配置的变更必须可回滚。建议使用版本控制管理 Makefile 或构建脚本,每次调整后打 tag,方便快速回到上一个可用配置。如果使用 CI 流水线,这一步可以自动完成——构建失败或检查不通过时,自动回退到上一个通过的构建产物。
兼容性、脱敏与最小权限
最后补充三个容易被忽略的实践细节。
兼容性测试:checksec 的不同实现(Go 版、Python 版、Bash 版)对同一二进制的输出可能存在细微差异。验收资源包应固定使用同一版本的工具,并在报告中记录版本号。如果团队内有人使用不同实现,建议统一到官方推荐的版本。
报告脱敏:JSON 报告中可能包含文件路径、构建信息等敏感数据。如果报告需要分享给外部或存入公共仓库,建议在留存前做脱敏处理,例如替换绝对路径为相对路径,或删除无关的元数据字段。
最小权限:checksec 检查单个文件通常不需要 root 权限,但检查进程(proc 命令)可能需要读取 /proc 下的某些信息。在 CI 中运行检查时,应使用最小权限账号,避免以 root 身份执行不必要的扫描。
把资源包嵌入交付流程
这套资源包的最终形态,应该是一个包含以下内容的目录结构:
checksec-acceptance/
├── samples/ # 最小测试程序
│ ├── hardened.c
│ ├── plain.c
│ └── Makefile
├── scripts/
│ ├── run_check.sh # 报告留存脚本
│ └── diff_reports.sh
├── templates/
│ ├── exception.md # 例外审批模板
│ └── classification.md # 异常分类表
└── README.md # 使用说明使用时,开发者只需将目标文件放入指定目录,运行 run_check.sh,即可得到一份带版本、哈希、退出码和 stderr 的完整报告。安全工程师在审核时,依据异常分类表快速归类,必要时生成例外审批单。整个流程从“看一眼输出”变成“留一套证据”,每一次检查结果都可追溯、可复测、可审计。
需要再次强调的是,checksec 的检查结果只代表编译时启用的缓解措施,不等同于漏洞扫描结论,更不能证明程序绝对安全。它回答的是“这个二进制有没有启用这些防护”,而不是“这个程序有没有漏洞”。把这一点写进资源包的 README,能避免团队对工具产生错误预期。
- it2021
- it2021.com
- OWASP
- tag
- 文件哈希
- 漏洞扫描





1F
这个分类表挺实用,以后验收报告不用凭感觉判断了