8.7 KiB
8.7 KiB
name, type, description, capabilities
| name | type | description | capabilities | |
|---|---|---|---|---|
| re-nim | atomic | Nim 编译产物逆向:运行时识别、NimString 结构、异常与 GC 路径。 触发词:Nim逆向、nim、NimString、NimMain、nim 产物。 |
|
Nim 逆向
何时使用 / 何时不用
- 用: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(反编译底座)
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 区分