Vector35 / Vector35/binaryninja-api

Derived class VFT definition uses multiple base VFT for multiple inheritence when debug symbols are present

Open
#4,967 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Component: DebugInfo DebugInfo: DWARF DebugInfo: PDB Effort: Medium Impact: Medium
Dominant language
C++
Stars
1.3k
Forks
298
Avg merge
5d 5h
Merged PRs (30d)
19

Description

Version and Platform (required):

  • Binary Ninja Version: 3.6.4790-dev, 10b89271
  • OS: macos
  • OS Version: 14.3
  • CPU Architecture: arm64

Bug Description:
I have two base classes named Animal and Pet, and a Cat class derived from both of them. When I use a PDB and BN imports the types from it, BN generates vft types for these classes like this:

struct __data_var_refs Animal::VTable
{
    void* (* __vecDelDtor)(class Animal* this, uint32_t);
    void (* introduce)(class Animal const* this);
    void (* makeSound)(class Animal const* this);
    char const* (* getType)(class Animal const* this);
};

struct __data_var_refs Pet::VTable
{
    void* (* __vecDelDtor)(class Pet* this, uint32_t);
    void (* play)(class Pet const* this);
};

struct __base(Animal::VTable, 0) __base(Pet::VTable, 0) __data_var_refs Cat::VTable
{
    void* (* __vecDelDtor)(class Cat* this, uint32_t);
    __inherited void (* Pet::VTable::play)(class Pet const* this);
    __inherited void (* Animal::VTable::makeSound)(class Animal const* this);
    __inherited char const* (* Animal::VTable::getType)(class Animal const* this);
};

Notice that the VFT for Cat uses two base VFT for the same offset, resulting in the second function being overwritten with the Pet::play function from Pet's VFT definition, which is incorrect; instead, it should be introduce from Animal. When I remove the __base(Pet::VTable, 0) part from the type definition, the type transforms into this:

struct __base(Animal::VTable, 0) __data_var_refs Cat::VTable
{
    void* (* __vecDelDtor)(class Cat* this, uint32_t);
    __inherited void (* Animal::VTable::introduce)(class Animal const* this);
    __inherited void (* Animal::VTable::makeSound)(class Animal const* this);
    __inherited char const* (* Animal::VTable::getType)(class Animal const* this);
};

Which seems correct to me.

Steps To Reproduce:

  1. Download and build the attached source code using
    1. MSVC
    2. x64 architecture
    3. Release mode
    4. Debug symbols enabled
    5. Optimizations disabled
  2. Load the binary into the BN
  3. Check VFT definition for Cat class.

Additional Information:
class_test_source.zip

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the attached class_test_source.zip and reproduce the issue by building it with MSVC, x64, release mode, debug symbols, and optimizations disabled, then load the binary in Binary Ninja. Inspect the generated Cat::VTable definition and the handling of its Animal and Pet base VFTs at the same offset. Done means the Cat definition preserves the expected Animal entries instead of overwriting one with Pet::play.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
reverse-engineering
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.