SuperAPI AI Directory

re-zig

Security & auditing

View repository

Updated 10-15 21:35

About this skill

Zig Reverse Engineering

When to Use / When Not to Use

  • Use: Zig artifacts (no C++ RTTI/exception tables, panic function chain characteristics,_start → main startup form), need to recover error handling paths, C ABI boundaries, behavior after comptime expansion
  • Use: distinguish Zig-side code in mixed Zig/C artifacts (no RTTI side + Zig symbol patterns)
  • Don't use: C/C++ artifacts (go to [[re-cpp-abi]]; having RTTI/exception tables means it is not a Zig-only artifact)
  • Don't use: only need function logic (use decompilation skill directly)

Tool Preparation

readelf / llvm-nm (symbol and section analysis)

  • For installation and verification see [[re-cpp-abi]] Tool Preparation
  • Purpose: readelf -S check exception table sections;readelf -s/llvm-nm check symbols and visibility (Zig business functions are mostly LOCAL symbols)

llvm-objdump / objdump (disassembly)

  • For installation and verification see [[re-cpp-abi]] Tool Preparation
  • Purpose: locate panic call sites, error code comparisons for catch/orelse (cmpw + branches)

Ghidra / IDA (decompilation base)

  • For installation and verification see [[re-ghidra]] / [[re-ida]]
  • Zig artifacts have no type information (when there is no DWARF); combine with behavior analysis (see steps 3/4)

strings (string pool/error names)

  • Bundled with the system; verification: strings --version
  • Purpose: @errorName locate error name strings, panic messages, format strings

zig compiler (optional, for comparison compilation)

  • Official tarball / brew install zig / Windows official installer; verification: zig version
  • Purpose: compile comparison artifacts with the same version, verify panic chain/error union layout (versions differ greatly, see [[layout]])

Operation Steps

Execute in order, archive each step's artifacts (path + sha256, see [[re-triage]]).

  1. Artifact Identification:

    readelf -s sample | grep -iE 'panicking|panicExtra|defaultPanic|zig' | head
    readelf -S sample | grep gcc_except_table   # 应无输出(Zig 产物常带 .eh_frame,不能作判别)
    readelf -s sample | grep __gxx_personality_v0   # 应无匹配
    readelf -s sample | grep -wE '_start|main'      # 启动形态
    
    • Zig characteristics: panic function chain (debug.panicExtra/debug.panicking etc., version-dependent),std symbol patterns (std.debug.print etc.),no C++ RTTI/exception tables(compare with the RTTI/exception-intensive characteristics of [[re-cpp-abi]])
    • Combined criteria: no .gcc_except_table + no __gxx_personality_v0 + no _ZTV*(RTTI vtable) → no C++ exception mechanism;.eh_frame having both is not sufficient as a sole criterion
    • Mixed compilation with C: Zig symbols coexist with C symbols (see step 5 boundaries)
  2. Symbol Visibility and Startup Path:

    readelf -s sample | grep -E 'GLOBAL|LOCAL' | grep -cE 'FUNC'
    readelf -s sample | grep -wE '_start|main' | head
    
    • Startup path:_start(GLOBAL) → runtime initialization (std.start) → main(LOCAL); Zig's main is an ordinary function, the entry is wrapped via std.start (exit handling is in the wrapper layer)
    • Symbol visibility: Zig by default only exports _start and functions explicitly marked with export ; business functions are LOCAL symbols (symbol tables still present in debug/ReleaseSafe, can be stripped in ReleaseFast)——nm being visible does not mean exported; the hook/injection surface is calculated by the export table
    • comptime expansion output: compile-time computations have been inlined/expanded—there is no corresponding source structure; analyze by behavior (see pitfall 1)
  3. panic/error handling paths:

    readelf -s sample | grep -iE 'panic' | head        # panic 链符号(版本相关命名)
    
    • panic chain:@panic/assertion failure → panic function (print + abort)—locating the panic call site finds input validation/invariants; the panic handler itself is "print + exit", and the call site is the actual business validation
    • error union:!T type; its layout is divided into two kinds depending on payload size (small payload: 8-byte slot, error code in the high bits; large payload: error code first, payload inlined according to alignment—[[layout]] has a measured table); the call site checks the orelse/catch branch (compiled as an error code comparison + branch)
    • Analysis: error paths are a reverse-engineering focus (validation logic, failure branches)—the error code comparison point is the branch condition, and error names can be recovered via the @errorName string pool
    • Error name recovery:@errorName(e) 's string is in __zig_tag_name_* symbols/string pool—strings error names correspond directly to code paths, making them first-hand clues to error semantics
  4. Locating constants and strings (entry point for behavior analysis):

    strings -n 5 sample | head -30        # 格式串/错误名/panic 消息
    readelf -S sample | grep -E 'rodata|data'   # 常量区
    
    • std.debug.print 's format string is in the read-only data section, and cross-references can lead back to the call site (error output path)
    • Constants evaluated at comptime are inlined directly as immediates, with no constant table—to find "magic numbers", trace the parameters back from the call site
  5. comptime and generic expansion:

    • Constants/inline functions computed at comptime leave no runtime trace; generic instantiation produces duplicate code (specialized by call-site parameters)
    • Recovery strategy: analyze by behavior (where a constant appears → which call argument it traces back to), not by source mapping
    • std.debug.print and other std functions are heavily inlined (Release mode),readelf -s may retain only startup and the panic chain
  6. C ABI boundary:

    • @extern / @cImport: Zig calls the C library (the import table is clearly inspectable—readelf -d 's NEEDED and imported symbols)
    • export fn: the Zig side exports to C/host calls (GLOBAL symbols, visible in the export table)
    • Mixed artifact: distinguish by symbol origin (Zig symbols vs C symbols—linker grouping/section ownership); the boundary is the logic entry point (the main Zig logic is inside the boundary)
    • Calling convention:only on extern / export or an explicit callconv(.c) boundary is it the C ABI—the compiler's own definition is "c is an alias for this target's C calling convention;functions marked extern or export obtain this convention by default", whereasordinary Zig fn uses the Zig default convention(original definition: used when it is neither export nor inline ;makes no guarantees about stack alignment, registers, etc., and can only be used within the same Zig compilation unit). So callconv(.c) isan explicit requirement for the C convention, not the default for ordinary functions
    • Pure Zig internal functions cannot unconditionally be given the C ABI: Register parameters, aggregate type argument passing, and return value rules may all differ from the target platform's C ABI; when recovering prototypes, infer from the actual usage near the call site; SysV on x86-64 only applies to .c 's mapping on that target, and does not represent Zig's internal convention itself
    • The parameter layout at C library call sites is read according to the ABI (unlike C++ thiscall in [[re-cpp-abi]], there is no hidden parameter/vtable indirection layer)——this item only applies to confirmed C ABI boundaries
  7. stripped/ReleaseFast fallback:

    strings -n 6 sample | grep -iE 'panic|error' | head     # panic 消息/错误名(@errorName 字符串池)
    
    • Under ReleaseFast, both the symbol table and the panic chain may be stripped; recover by behavioral characteristics (error code comparison patterns, string pools), see [[re-triage]] for the initial triage fallback

Cross-domain union

  • [[re-binary-core]] gateway: this skill's ownership (selection tree "Zig artifacts" branch)
  • [[re-cpp-abi]]: boundary distinction (no RTTI/exceptions → not C++)
  • [[re-imports]]: C library boundaries (NEEDED/imported symbols) and export table
  • [[re-analyze/analysis-contract]]: symbol table passed according to the data contract
  • [[re-triage]]: initial triage fallback

Common pitfalls and traps

  • Symbol bloat caused by comptime expansion: Symptom——artifact symbols do not correspond to source code; Cause——compile-time expansion/inlining/generic specialization; Countermeasure——analyze by behavior rather than mapping to source
  • Panic paths are misleading: Symptom——a large amount of panic handling code is mistaken for the main logic; Cause——error paths and normal paths are intertwined; Countermeasure——first separate out the panic call sites (validation), then analyze the normal paths
  • Error union layout version differences: Symptom——error code reads are misaligned; Cause——error union layout changes across versions (small payload 8-byte slot/high bits of the error code, large payload inlined by alignment); Countermeasure——confirm the layout according to the target version ([[layout]] measured table), the error code is always u16
  • Difficult to distinguish boundaries when mixed-compiled with C: Symptom——Zig/C symbols are mixed; Cause——mixed compilation; Countermeasure——group by symbol origin and section ownership, and enter the Zig logic at the boundary
  • No exception table ≠ no protection: Symptom——misjudging that there is no error handling; Cause——Zig error handling goes through error union rather than the exception table; Countermeasure——check the error union call sites (catch/orelse branch's error code comparison)
  • Business functions are LOCAL symbols: Symptom——nm there are a great many functions in the list, but nm -g(global) only has _start; Cause——Zig does not export business symbols by default; Countermeasure——hook/injection is counted by the export table; analysis can still locate them by LOCAL symbols
  • Panic chain naming changes across versions: Symptom——searching for symbols by std.debug.panic finds nothing; Cause——0.14+ refactored to debug.panicExtra/defaultPanic chain (older versions std.debug.panic named directly); Countermeasure——locate by the panic call site (@panic compiled artifact: print+abort sequence), without relying on the specific symbol name