I am new to ghidra and reverse engineering, but i have watched this series text and read text. And encountered following problem with ghidra:
When you have a Base class with members and virtual functions and a Derived class and the base class is located at 0x0 inside the Derived class for example, then the vTable for Derived class is colliding with the base vTable:
class Base {
int var1;
int var2;
public:
virtual void virtual_func_1(void);
virtual void virtual_func_2(void);
};
resulting in:
class Base size(12):
+---
0 | {base_vTable}
4 | var1
8 | var2
+---
and:
class Derived: public Base {
int var3;
public:
void virtual_func_3();
};
resulting in:
class Derived size(16):
+---
| +--- (base class Base)
0 | | {base_vTable}
4 | | var1
8 | | var2
| +---
12 | var3
+---
So far so good. The resulting assembly code (reinterpreted in C code) could look something like this:
void __thiscall Derived::Derived_constructor(Derived* this) {
Base::Base_constructor((Base*) this);//the base class is located at 0x0 of Derived
this->var3 = 0;
(this->base).base_vTable = &PTR_FUN_004af9f4;//PTR_FUN_004af9f4 being the address of the Derived vTable <- this is where my problem arises, as it would be of type BaseVtable (in case 1, see below) but actually is of type DerivedVtable (casted to BaseVtable)
}
void __thiscall Base::Base_constructor(Base* this) {
this->var1 = 0;
this->var2 = 0;
this->base_vTable = &PTR_FUN_004bf966;//PTR_FUN_004bf966 being the address of the Base vTable
}
Now i would create some structs:
BaseVtable {0x0: pointer virtual_func_1; 0x4: pointer virtual_func_2;};
Base {0x0: BaseVtable* base_vTable; 0x4: int var1; 0x8: int var2;}
DerivedVtable {0x0: BaseVtable base_vTable; 0x8: pointer virtual_func_3;};
//as far as i know all entries in a derived vTable must include or override all entries of the base vTable in the same order therefor this should work...
and:
Derived{0x0: Base baseClass; 0xc: int var3;}; //(Case 1)
or
Derived{0x0: DerivedVtable* derived_vTable; 0x4: int var1; 0x8: int var2; 0xc: int var3;}; //(Case 2)
In case 1 if i reverse engineer a member all derived classes will be updated because all derived classes have a copy of Base and all virtual functions from Base would match, but because 0x0 of Derived class is of type is Base.base_vTable it is of type BaseVtable and no new/overriden virtual functions of Derived class would be there.
In case 2 all virtual functions of derived class will be correct but var1/var2 which are actually from Base class wont be updated for all derived classes of Base and would have to be edited manually after RE the Base class...
Is there a way to use case 1 but interpret 0x0 of derived class (base class) as DerivedVtable*, but 0x4 (var1) /0x8 (var2) to be from Base class 0x4/0x8?
I could separate the actuall vTable from the variables but that would double the classes for all derived classes:
Base_member{0x0: int var1; 0x4: int var2;}
Base_Derived{0x0: DerivedVtable* derived_vTable; 0x4: Base_member base_member}
Derived{0x0: Base_Derived base; 0xc: int var3;}
Base_otherDerived{0x0: OhterDerivedVtable* otherDerived_vTable; 0x4: Base_member base_member}
OtherDerived{0x0: Base_otherDerived base; /*...*/}
Am i missing something?