reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# Zig 二进制布局:启动路径 / 错误联合 / panic 链 / 符号可见性
|
||||
|
||||
Zig 产物与 C 产物的最大差异在错误处理(error union 而非异常表)与符号可见性(业务函数 LOCAL)。先确认 Zig 版本再读布局——panic 链命名与错误联合细节随版本演进。
|
||||
|
||||
## 启动路径(ELF,x86-64 实测 0.17.0-dev)
|
||||
|
||||
```
|
||||
_start (GLOBAL) ← 链接器入口(用户代码不可见)
|
||||
└─ std.start 运行时初始化(栈指针/安全栈、exit 处理)
|
||||
└─ main (LOCAL) ← 用户 main 函数(Zig 的 main 是普通函数)
|
||||
退出经 std.start 包裹层(return 后处理)
|
||||
```
|
||||
|
||||
- `_start` 是唯一默认导出的业务无关 GLOBAL 函数;`export fn` 才产生其他导出
|
||||
- 反汇编入口链:`_start` 短桩(16 字节级)→ std.start 初始化函数(大,含安全特性安装)
|
||||
|
||||
## 符号可见性(与 C 的重要差异)
|
||||
|
||||
| 构建模式 | 业务函数符号 | 说明 |
|
||||
|---|---|---|
|
||||
| Debug / ReleaseSafe | LOCAL(符号表保留) | `nm` 可见但非导出;`_start` 为 GLOBAL |
|
||||
| ReleaseFast / ReleaseSmall | 可被 strip | 按行为特征恢复 |
|
||||
| `export fn` | GLOBAL | 显式导出(C ABI 边界,宿主调用点) |
|
||||
|
||||
- 判别命令:`readelf -s sample | grep FUNC | awk '$7=="GLOBAL"'` 数全局函数
|
||||
- hook/注入面按导出表(GLOBAL + 动态符号)算,LOCAL 符号只是分析线索
|
||||
|
||||
## 错误联合(error union)布局
|
||||
|
||||
`E!T`(错误联合)编译为"错误码 + 载荷":错误码恒为 u16(0 = 无错),布局按载荷大小分两种。
|
||||
|
||||
### 载荷 ≤ 8 字节:整体一个槽位,错误码在载荷之后(高位侧)
|
||||
|
||||
| 载荷类型 | 槽位大小(实测 0.17) | 错误码位置 |
|
||||
|---|---|---|
|
||||
| u8 / bool | 4 字节 | 载荷之后(u8 在 offset 1) |
|
||||
| u32 | 8 字节 | offset +4(u16) |
|
||||
| u64 | 8 字节 | 高位 u16 |
|
||||
|
||||
### 载荷 > 8 字节:错误码在前 2 字节,载荷按自身对齐内联
|
||||
|
||||
| 载荷类型 | 槽位大小(实测 0.17) |
|
||||
|---|---|
|
||||
| u128(对齐 16) | 32 字节 |
|
||||
| struct { u64, u64 }(对齐 8) | 24 字节 |
|
||||
|
||||
- 反汇编识别:调用点后 `cmpw $0, 槽位+offset` + 分支 = `catch`/`orelse` 的错误码检查;错误返回路径 `call lang.returnError`(0.17 命名)后装入错误表地址
|
||||
- `@errorName(e)` 在字符串池留错误名(`__zig_tag_name_*` 符号/字符串),可还原错误语义
|
||||
|
||||
## panic 链
|
||||
|
||||
| 版本 | 链形态 |
|
||||
|---|---|
|
||||
| 0.14+(实测 0.17.0-dev) | `@panic`/断言 → `debug.panicExtra`/`debug.FullPanic.defaultPanic` → `debug.panicking` 状态 + 打印 + abort;溢出检查 `debug.FullPanic(...).integerOverflow` |
|
||||
| 更早版本 | `std.debug.panic`(直接命名) |
|
||||
|
||||
- 定位方式不依赖符号名:找"打印 + abort"序列(`__zig_probe_stack` 附近的栈检查调用也常见)
|
||||
- `debug.panicking` 是 TLS 状态(panic 期间再 panic 短路)
|
||||
|
||||
## C ABI 边界
|
||||
|
||||
- **C ABI 只在 `extern` / `export` 或显式 `callconv(.c)` 的边界上假定**——编译器定义:`c` 是本目标 C 调用的别名,`extern`/`export` 函数默认获得它;**普通 Zig `fn` 用 Zig 默认约定**(既非 `export` 也非 `inline` 时使用,不对寄存器/栈对齐作保证),逆向内部函数时不能套 C ABI 的参数与返回值规则
|
||||
- `.c` 在各 target 上有各自的映射(x86-64 Linux 为 SysV AMD64、Windows 为 x64 约定、AArch64 为 AAPCS64)——这是**边界上**的映射,不是 Zig 内部约定本身
|
||||
- 导入:`@extern`/`@cImport` → 动态符号表(`readelf -d` NEEDED + UND 符号)
|
||||
- 导出:`export fn` → GLOBAL(导出表)
|
||||
- 混合产物判别:Zig 侧无 RTTI/异常表符号(`_ZTV*`/`__gxx_personality_v0` 缺失),C++ 侧有
|
||||
|
||||
## 版本差异要点
|
||||
|
||||
- panic 链命名:0.14 前后差异大(`debug.panicExtra` vs `std.debug.panic`)
|
||||
- 错误联合布局:u16 错误码不变;槽位尺寸/对齐随版本微调(以实测为准,[[examples]] 有 0.17 实测)
|
||||
- `main` 符号形态稳定(LOCAL 普通函数);`_start` 稳定(GLOBAL)
|
||||
- ReleaseFast 可能去除 panic 链(`-Drelease-fast` 下安全检查关闭)
|
||||
|
||||
## 使用注意
|
||||
|
||||
- 静态分析无需沙箱;跨架构 Zig 产物(ARM/RISC-V)布局规则同(错误码 u16 不变),槽位尺寸按指针宽度
|
||||
- 与 [[re-analyze/analysis-contract]](符号表传递)、[[re-cpp-abi]](C++ 判别)配合使用
|
||||
Reference in New Issue
Block a user