Nim 逆向
SkillDev toolsNim 编译产物逆向:运行时识别、NimString 结构、异常与 GC 路径。 触发词:Nim逆向、nim、NimString、NimMain、nim 产物。
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Nim 逆向 skill
What this skill tells your AI
The instructions your AI receives, as published by dslsdzc/rev-skills in .claude/skills/re-nim/SKILL.md and read by ahel’s review.
何时使用 / 何时不用
- 用:Nim 产物(
NimMain/ GC 符号、NimString 结构特征),需要还原字符串逻辑、异常路径、GC/所有权关系 - 用:Nim/C 混合产物中区分 Nim 侧代码(按符号来源分组后 Nim 侧进本技能路径)
- 不用:纯 C/C++ 产物(走 [[re-cpp-abi]] / [[re-binary-core]] 通用路径)
- 不用:只需函数逻辑(直接反编译技能)
工具准备
readelf / llvm-nm(符号与节分析)
- 安装与验证见 [[re-cpp-abi]] 工具准备
- Nim 产物以 ELF 为主(Linux 默认 C 后端);macOS 用 llvm-nm
Ghidra / IDA(反编译底座)
- 安装与验证见 [[re-ghidra]] / [[re-ida]];Nim 符号(NimMain/NimStringV2 等)导入后直接可读
nim 编译器(可选,对照编译)
- Linux:
apt install nim/dnf install nim/pacman -S nim;macOS:brew install nim;Windows: choosenim/官方安装器 - 验证:
nim -v;用途: 同版本编译对照产物,验证字符串布局/GC 符号形态(版本差异见 [[layout]])
xxd + Python struct(字节级核对)
- 系统自带(
xxd);Python 3 自带struct - 用途: 符号/布局输出异常时按偏移直接解析 NimString 与对象结构(示例见 [[examples]])
操作步骤
按顺序执行,每步产物存档(路径 + sha256,见 [[re-triage]])。
-
产物识别:
readelf -s sample | grep -iE 'NimMain|nimGC|NimString' | head # ELF;Mach-O 用 llvm-nm readelf -s sample | grep -iE 'nimIncRef|nimDecRef|rawNewString|eqStrings' | head- Nim 特征:
NimMain(入口链)、GC 符号(refc 的nimGC_*/ orc 的nimIncRefCyclic等)、NimStringV2/NimStringDesc结构类型 - 注意:release 构建下 GC 符号常被内联/消除(见坑 1),靠
NimMain与字符串函数兜底 - 入口链:C
main→NimMain→NimMainInner→NimMainModule(模块初始化)→ 业务 main - 模块初始化顺序按依赖拓扑(import 关系):
NimMainModule内的初始化段先跑被依赖模块——在初始化段断点/下钻时按调用序对照 import 链
- Nim 特征:
-
字符串与序列结构(先判 GC 模式):
- orc/arc(2.x 默认 orc):
NimStringV2 = {len: int, p: ptr NimStrPayload};NimStrPayload = {cap: int, data: 内联字符数组}——字符串是"len + 堆上 payload 指针",cap 高位带字面量标记位(见 [[layout]]) - refc(旧默认):
NimStringDesc = {len, reserved, data[]}——字符内联在结构体里 - 定位:
rawNewString(1.x 与 2.x 均为此 importc 名)分配调用点 → 结构布局 → 字符串操作函数(eqStrings等) - 分析:字符串比较点是关键逻辑(校验/协议/命令分发)——
eqStrings调用点即字符串相等判断 - 序列(seq)与 string 同构:v2 布局同为
len + payload 指针(payload 带 cap),refc 同为len/reserved + 内联——按同一判别表处理 - 内存视图(v2 布局,64 位):
字符串本体只有 16 字节句柄,内容在独立堆块——分析对象结构时按句柄跳转,别在对象里找字符数据栈/对象内: len(int64) | p(ptr) ───────────────┐ 堆上 payload: cap(int64, bit62=字面量标记) | data[cap+1] ←┘
- orc/arc(2.x 默认 orc):
-
内存中定位字符串(动态/转储场景):
# 在内存转储中按 v2 布局找字符串:cap 是 8 字节对齐的容量值,data 后跟可打印 ASCII # 候选: 8 字节对齐的 cap + len 匹配的 data → 反推 NimStringV2 起点字符串内容在堆上 payload 里,栈上只有 len+指针;先按
cap前缀特征定位 payload,再往回找引用它的 NimStringV2([[examples]] 有完整示例)。 -
异常与 raises 路径:
- Nim 2.2.x(Linux x86-64 默认)异常走 goto 式异常表,无 setjmp 符号;raise 路径经
raiseExceptionEx/raiseExceptionAux - 老默认(
--exceptions:setjmp,Nim 2.0 及以前)用 setjmp/longjmp:nimSetjmp符号可见 - 定位:
raiseExceptionEx调用点 → 异常对象分配与消息 → catch 分支(异常表驱动) - 分析:异常路径揭示输入校验与失败处理,比正常路径更早暴露边界条件
- Nim 2.2.x(Linux x86-64 默认)异常走 goto 式异常表,无 setjmp 符号;raise 路径经
-
GC 与引用计数(按 GC 模式分叉):
- refc:引用计数 + 周期收集——
nimGC_*/nimGCunref/nimIncRef/nimDecRef调用点 - orc/arc:ARC 语义——
nimIncRefCyclic/nimDecRefIsLastCyclicDyn等;显式 inc/dec 少,释放由编译期插入 - 分析:GC 调用点帮助识别对象生命周期与所有权(配合字符串结构);release 下内联后改用分配/释放边界推断
- refc:引用计数 + 周期收集——
-
C 混合编译边界:
{.compile:}/{.importc:}混合时 Nim 与 C 符号并存:Nim 侧符号带模块前缀(hello__u8形态)与运行时(NimMain/NimString),C 侧符号无- 边界处是逻辑入口:Nim 业务逻辑在 NimMain 调用链内侧,C 库调用经导入表
-
stripped 产物兜底:
strings -n 6 sample | grep -E '@m.*\.nim\.c' | head # C 生成缓存文件名(含模块名,release 也嵌入) strings -n 6 sample | grep -iE 'fatal\.nim|Exception' | headNimMain等被 strip 后按特征串(@m<模块>.nim.c、std 运行时源文件名)与运行时行为识别([[re-triage]] 初勘兜底);业务源码名不嵌入二进制,别指望它
跨域联合
- [[re-binary-core]] 网关:本技能归属(选择树「Nim 产物」分支)
- [[re-analyze/analysis-contract]]:符号表按数据契约传递
- [[re-cpp-abi]]:C 混合侧与无 RTTI 判别参考
- [[re-triage]]:初勘兜底(strip 产物按特征串识别)
常见坑与陷阱
- GC 版本差异(refc/orc):现象——找不到引用计数调用;原因——orc 无显式 inc/dec(ARC 语义)且 release 下 GC 符号内联;对策——先按 Nim 版本与 GC 模式确认再分析,debug/refc 构建符号更全
- NimString 布局随 GC/版本变化:现象——按
len/reserved/data手写解析器读 2.x 产物全错;原因——2.x orc 默认是NimStringV2{len, p}(payload 带 cap),len/reserved/data内联是 refc 的 NimStringDesc(1.x 默认);对策——先确认产物 GC 模式(见 [[layout]] 判别表)再选布局:debug 构建看 GC 符号(refc 有nimGCunref,orc 有nimIncRefCyclic),release 构建看分配函数命名与 cap 字段是否出现 - 导出符号被 strip:现象——无 NimMain/NimString 符号;原因——strip 处理;对策——按特征字符串/运行时行为识别(步骤 6 兜底)
- C 混合编译:现象——Nim/C 符号混杂;原因——
{.compile:}混合;对策——按符号来源分组,Nim 侧进本技能路径 - 字符串比较点误判:现象——关键校验被当普通比较;原因——eqStrings 包装;对策——追踪 Nim 字符串函数调用点定位比较逻辑
- 异常实现代际误判:现象——按老思路找 nimSetjmp 找不到;原因——2.2+ Linux amd64 默认 goto 式异常(无 setjmp);对策——无 nimSetjmp 时沿
raiseExceptionEx与异常表定位 catch,别当"无异常处理" - release 内联导致符号稀疏:现象——debug 能看到的 nimGC_*/eqStrings 在 release 里消失;原因——-d:release 内联;对策——release 产物按行为特征(分配/释放边界、字符串函数调用模式)分析,符号表只是线索不是依据
- 字符串内容在堆上,栈上只有句柄:现象——在栈上按内容搜索字符串找不到;原因——v2 布局的字符数据在独立 payload 堆块里,栈上只有
len + p两个字段;对策——动态/转储场景先按 cap 前缀特征定位 payload 块,再回找引用它的句柄([[examples]] 内存定位示例) - 32 位产物结构尺寸减半:现象——64 位布局表套 32 位产物偏移全错;原因——NI 在 32 位平台是 4 字节(NimStringV2 为 8 字节而非 16,NimStrPayload cap 为 4 字节);对策——解析前先确认产物位数,按 4/8 字节 NI 选结构尺寸
- 非 PIE 老构建入口即固定地址:现象——
readelf -h的 e_entry 是绝对地址(如 0x401xxx),在内存里直接对得上;原因——非 PIE 构建(老默认);对策——入口链定位用符号(NimMain 三连)不依赖 PIE 与否,但换算地址时按 e_type 区分
Signals
- GitHub stars
- 57
- Forks
- 8
- Last commit
- Sep 2026
ahel review
K1binfo
installs-packages
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Catalog kind
- skill
- Gateway key
re-nim- Source
- github.com/dslsdzc/rev-skills