executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
re-nimNim 编译产物逆向:运行时识别、NimString 结构、异常与 GC 路径。 触发词:Nim逆向、nim、NimString、NimMain、nim 产物。
Independent GitHub snapshot · Automated classification · Original documentation governs
NimMain / GC 符号、NimString 结构特征),需要还原字符串逻辑、异常路径、GC/所有权关系apt install nim / dnf install nim / pacman -S nim;macOS: brew install nim;Windows: choosenim/官方安装器nim -v;用途: 同版本编译对照产物,验证字符串布局/GC 符号形态(版本差异见 [[layout]])xxd);Python 3 自带 struct按顺序执行,每步产物存档(路径 + 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
NimMain(入口链)、GC 符号(refc 的 nimGC_* / orc 的 nimIncRefCyclic 等)、NimStringV2/NimStringDesc 结构类型NimMain 与字符串函数兜底main → NimMain → NimMainInner → NimMainModule(模块初始化)→ 业务 mainNimMainModule 内的初始化段先跑被依赖模块——在初始化段断点/下钻时按调用序对照 import 链字符串与序列结构(先判 GC 模式):
NimStringV2 = {len: int, p: ptr NimStrPayload};NimStrPayload = {cap: int, data: 内联字符数组}——字符串是"len + 堆上 payload 指针",cap 高位带字面量标记位(见 [[layout]])NimStringDesc = {len, reserved, data[]}——字符内联在结构体里rawNewString(1.x 与 2.x 均为此 importc 名)分配调用点 → 结构布局 → 字符串操作函数(eqStrings 等)eqStrings 调用点即字符串相等判断len + payload 指针(payload 带 cap),refc 同为 len/reserved + 内联——按同一判别表处理栈/对象内: len(int64) | p(ptr) ───────────────┐
堆上 payload: cap(int64, bit62=字面量标记) | data[cap+1] ←┘
字符串本体只有 16 字节句柄,内容在独立堆块——分析对象结构时按句柄跳转,别在对象里找字符数据内存中定位字符串(动态/转储场景):
# 在内存转储中按 v2 布局找字符串:cap 是 8 字节对齐的容量值,data 后跟可打印 ASCII
# 候选: 8 字节对齐的 cap + len 匹配的 data → 反推 NimStringV2 起点
字符串内容在堆上 payload 里,栈上只有 len+指针;先按 cap 前缀特征定位 payload,再往回找引用它的 NimStringV2([[examples]] 有完整示例)。
异常与 raises 路径:
raiseExceptionEx/raiseExceptionAux--exceptions:setjmp,Nim 2.0 及以前)用 setjmp/longjmp:nimSetjmp 符号可见raiseExceptionEx 调用点 → 异常对象分配与消息 → catch 分支(异常表驱动)GC 与引用计数(按 GC 模式分叉):
nimGC_*/nimGCunref/nimIncRef/nimDecRef 调用点nimIncRefCyclic/nimDecRefIsLastCyclicDyn 等;显式 inc/dec 少,释放由编译期插入C 混合编译边界:
{.compile:} / {.importc:} 混合时 Nim 与 C 符号并存:Nim 侧符号带模块前缀(hello__u8 形态)与运行时(NimMain/NimString),C 侧符号无stripped 产物兜底:
strings -n 6 sample | grep -E '@m.*\.nim\.c' | head # C 生成缓存文件名(含模块名,release 也嵌入)
strings -n 6 sample | grep -iE 'fatal\.nim|Exception' | head
NimMain 等被 strip 后按特征串(@m<模块>.nim.c、std 运行时源文件名)与运行时行为识别([[re-triage]] 初勘兜底);业务源码名不嵌入二进制,别指望它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 字段是否出现{.compile:} 混合;对策——按符号来源分组,Nim 侧进本技能路径raiseExceptionEx 与异常表定位 catch,别当"无异常处理"len + p 两个字段;对策——动态/转储场景先按 cap 前缀特征定位 payload 块,再回找引用它的句柄([[examples]] 内存定位示例)readelf -h 的 e_entry 是绝对地址(如 0x401xxx),在内存里直接对得上;原因——非 PIE 构建(老默认);对策——入口链定位用符号(NimMain 三连)不依赖 PIE 与否,但换算地址时按 e_type 区分