SuperAPI AI Directory

re-cpp-abi

General & other

View repository

Updated 10-15 21:35

About this skill

Modern C++ reverse engineering (RTTI / exceptions / vtables)

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(output foo::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]]).

  1. 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
  2. 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_info derivation 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
  3. 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
  4. 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:.pdata RUNTIME_FUNCTION (Begin/End/UnwindInfo) → .xdata unwind data → exception handler (__CxxFrameHandler3)
    • ELF:.eh_frame FDE/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)
  5. 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 _ZTI can 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
  6. mangling decoding (batch):

    readelf -s sample | grep -E '_ZN|_ZTV|_ZTI' | awk '{print $8}' | c++filt | head -20
    
    • MSVC: undname or an online equivalent tool
    • Write decoding results into the symbol table (for renaming by [[re-ghidra]] / [[re-ida]])

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 _ZTI can 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 *reg are all indirect calls; Cause - virtual dispatch; Countermeasure - narrow down candidates by combining vtable slots and call-site evidence, and do not guess