When to use / when not to use
- Use: binaries dense with RTTI/exception tables, C++ targets whose decompilation output is chaotic (class hierarchies/virtual calls/exception flow cannot be read directly)
- Do not use: C code or pure assembly (follow the general path of [[re-binary-core]]); targets dominated by obfuscation (first [[re-deobfuscate]])
Tool preparation
readelf / llvm-objdump (section table and exception table)
- Linux:
apt install binutils llvm/dnf install binutils llvm/pacman -S binutils llvm - macOS:
brew install llvm(part of binutils, bundled with macOS) - Windows: WSL or llvm precompiled
- Verify:
readelf --version、llvm-objdump --version
c++filt / undname (mangling decoding)
- Linux/macOS:
c++filt(bundled with binutils); Windows:undname(VS toolchain) - Verify:
echo '_ZN3foo3barEv' | c++filt(outputfoo::bar())
Ghidra / IDA (decompilation base, scripted RTTI traversal)
- For installation and verification see [[re-ghidra]] / [[re-ida]] tool preparation
gdb (exception breakpoints, optional)
- For installation and verification see [[re-gdb]]
Operating steps
Execute in order, archive the artifacts of each step (path + sha256, see [[re-triage]]).
-
ABI identification:
readelf -s sample | grep -E '_ZN|_ZTV|_ZTI' | head # Itanium(GCC/Clang) strings sample | grep -E '^\?\?_' | head # MSVC- Itanium characteristics:
_ZN(functions),_ZTV(vtables),_ZTI(RTTI type information) - MSVC characteristics:
??_7(vftable)、??_R(RTTI) (after strip ??_ is in the symbol table rather than the string area—also check the import table __CxxFrameHandler / msvcp characteristics to assist judgment) - If identified incorrectly, everything afterward will be off—confirm before continuing
- Itanium characteristics:
-
RTTI reconstruction (Itanium):
readelf -s sample | grep _ZTI | head # _ZTI<类名> 指向 typeinfo:起始为 vptr(指向 _ZTVN10__cxxabiv1... 类型信息虚表), # 下一 pointer-sized 槽为 __type_name;64 位通常 +0/+8、32 位 +0/+4 # 解析前先定 ELF class / 指针宽度;__si_class_type_info / __vmi_class_type_info 同样按目标 ABI 对齐与字段宽度解析- Structure:
typeinfo→__class_type_infoderivation chain → the complete inheritance path of each class - Scripted: Ghidra/IDA traverses _ZTI references to reconstruct the class inheritance graph (parent-child relationship table)
- Output: class name → inheritance chain mapping (written into the session symbols_known, see [[re-analyze/analysis-contract]])
- MSVC:
??_R0<类名>TypeDescriptor symbols +_RTTICompleteObjectLocator(COL) traversal to reconstruct the inheritance graph
- Structure:
-
Vtable recovery:
readelf -s sample | grep _ZTV | head_ZTV<类名>points to the start of the vtable (array of virtual function pointers); the vtable prefix has two slots (8 bytes on 32-bit / 16 bytes on 64-bit): offset to top + typeinfo pointer (Itanium ABI)- After locating the vtable: the function address of each slot → reverse-infer the virtual method name from the call site (combined with the inheritance graph from step 2)
- Virtual call (
call *reg) cannot be named statically → use the call site context (usage of parameters/return value) to narrow down candidates
-
Exception handling table:
readelf -S sample | grep -E 'eh_frame|gcc_except' # ELF:.eh_frame(readelf 不解析 PE,PE 走下方命令) llvm-readobj --coff-unwind-info sample.exe # PE:.pdata/.xdata(或 objdump -h / pefile)- PE:
.pdataRUNTIME_FUNCTION (Begin/End/UnwindInfo) →.xdataunwind data → exception handler (__CxxFrameHandler3) - ELF:
.eh_frameFDE/CIE → unwind rules and LSDA (.gcc_except_table) → exception handling functions - Purpose: recover control flow interrupted by exception paths, locate destructor/cleanup logic (catch blocks)
- PE:
-
Template/lambda identification:
- Templates: symbols contain
<...>parameters (expanded into long strings in Itanium mangling); when instantiation explodes, cluster by call pattern - lambda: the closure type is a real anonymous class type; in ordinary call scenarios, a separate RTTI is often not emitted, so the absence of
_ZTIcan neither rule out nor infer lambda (when ODR-used by typeid etc., a corresponding RTTI may exist). For identification, prioritize Itanium's_ZZ<作用域>ENK.../operator()mangling, capture member layout, and call sites; MSVC's<lambda_...> - Output: list of functions suspected to be template instantiations/lambdas + call sites
- Templates: symbols contain
-
mangling decoding (batch):
readelf -s sample | grep -E '_ZN|_ZTV|_ZTI' | awk '{print $8}' | c++filt | head -20- MSVC:
undnameor an online equivalent tool - Write decoding results into the symbol table (for renaming by [[re-ghidra]] / [[re-ida]])
- MSVC:
Cross-domain union
- [[re-binary-core]] gateway: ownership of this skill (the re-binary-core selection tree is already mounted)
- [[re-ghidra]] / [[re-ida]]: decompilation foundation and scripting
- [[re-deobfuscate]]: interface between obfuscation and ABI analysis
- [[re-analyze/analysis-contract]]: class inheritance graph/symbol table passed according to the data contract
- [[re-analyze/rerouting]]: RTTI/exception table features trigger this skill (table A already mounted)
Common pitfalls and traps
- ABI misjudgment causes all parsing to fail: Symptom - using Itanium structures to parse an MSVC target (or vice versa) causes total misalignment; Cause - the identification step was skipped; Countermeasure - do step 1 first, and double-check the mangling features
- Template expansion causes symbol explosion: Symptom - readelf output runs to tens of thousands of lines
_Z...; Cause - template instantiation; Countermeasure - cluster by call pattern and filter out standard library symbols (libstdc++/STL prefixes) - Lambdas lacking RTTI are mistaken for having no type: Symptom - the class inheritance graph is missing nodes, and one mistakenly thinks a lambda is not a real type; Cause - the closure type is a real anonymous class type, but in ordinary call scenarios a separate typeinfo is often not emitted, so the absence of
_ZTIcan neither rule it out nor infer it; Countermeasure - identify by_ZZ<作用域>ENK.../operator()mangling, capture member layout, and call sites, and do not force a search for RTTI (when ODR-used by typeid etc., a separate RTTI may exist) - Exception table version differences: Symptom - .xdata parsing is misaligned; Cause - different MSVC exception handling versions (__CxxFrameHandler3 etc.); Countermeasure - confirm the version by the imported function (__CxxFrameHandler) before parsing
- Virtual calls cannot be statically named: Symptom -
call *regare all indirect calls; Cause - virtual dispatch; Countermeasure - narrow down candidates by combining vtable slots and call-site evidence, and do not guess