HexRaysPyTools
HexRaysPyTools copied to clipboard
Creating __cppobj structures
This is a question rather than an issue.
Have you looked into creating structures with __cppobj
attribute and naming virtual tables __vftable
as described here? This is a new functionality in IDA 7.2 and I'm interested in improvements it may bring comparing with your current approach. Main difference is that now you can create proper class hierarchy although it is not so simple comparing to simply creating structure with vtable pointer.
The only real difference I noticed in produced code is that there is no type conversion anymore when passing the class object to parent class method (for example, passing this
to base class constructor). While it's nice, it surely is not enough of a reason to implement __cppobj
support. I'll comment with other use cases if I find them.
Wow, this is really nice feature. I've play with it around and really allows to make hierarchy of classes where appropriate virtual tables and virtual functions are selected in decompiler view. Definitely going to implement this
Now virtual table name and type will have those postfixes. Also if reconstructed class has only single virtual table it will try to infer class name (useful with RTTI information)
The problem is multiple inheritance - we can't make 2+ __vtable
fields and only one virtual table can have right type name. I hope they'll do something with it in future versions.
Isn't it supposed to be 2 base __cppobj
classes from which you derive your child __cppobj
class? As a result you will have 0 vtables in child.
Yeah, but imagine this case:
class A
{
public:
virtual void AA() { printf("A::AA"); };
};
class B
{
public:
virtual void BB() { printf("B::BB"); };
};
class C : public A, public B
{
public:
void AA() override { printf("C::AA"); }
void BB() override { printf("C::BB"); };
};
int main()
{
auto c = new C();
c->AA();
c->BB();
delete c;
}
Objects of class C will have 2 virtual tables and we can't name both of them C_vtbl
Yeah, that is the limitation of Hex-Rays/your approach. It seems you need to create separate base classes for each child so you can place C::A::__vftable
directly in A
class (which you can name C__A
?). So if you have class C : public A, public B
and class D : public A, public B
final structures will be
struct __cppobj C__A {
C__A_vtbl __vftable;
}
struct /*VFT*/ C__A_vtbl {...}
struct __cppobj C__B {
C__B_vtbl __vftable;
}
struct /*VFT*/ C__B_vtbl {...}
struct __cppobj D__A {
D__A_vtbl __vftable;
}
struct /*VFT*/ D__A_vtbl {...}
struct __cppobj D__B {
D__B_vtbl __vftable;
}
struct /*VFT*/ D__B_vtbl {...}
struct __cppobj C : C__A, C__B {}
struct __cppobj D : D__A, D__B {}
What do you think about it? You can try to specifically find constructors of base objects to support this idea.
Yeah, that will do. The tiny little thing left is too implement it
Maybe a better way would be not to try to do it all by yourself but try to detect nesting class constructors (two different vtable writes at one offset in structure) and if you don't know what class does this constructor creates then fail with error "Define base class first". That way, user defines base classes as well as their constructors, with that available information generating derived class structure with base classes copies seems kinda easier. The process would be more controllable by user which may be convenient since the logic here is not simple.
Also, if you really implement something non-trivial with __cppobj
classes, it would be cool to have a switch between C/C++ structure creation.
Actually it is possible even with the current version. There's this button pack
and it can be used to create parent class for every virtual table. And it's good idea to give an error if 2 or more virtual tables exist in current structure.
What is missing is Inherit
button so as to generate proper type definition at finalization. But it can be done manually by copying:
class C {
A field_0;
B field_30;
}
to
class C : A, B {
}
What is also needed to be enabled is changing virtual table name since it matters much more with new version. There's no way to do in structure builder now.
Objects of class C will have 2 virtual tables and we can't name both of them
C_vtbl
They do have a multiple inheritance example and it looks like you don't need to: https://www.hex-rays.com/products/ida/support/idadoc/1691.shtml
The 'derived' class will use 2 VFTs:
offset 0: derived_vtbl
offset 8: der2_vtbl
IDA and Decompiler can use both VFTs and produce nice code for virtual calls.
https://godbolt.org/z/7aTh7K
Yeah, I've finally used it and you can have multiple inheritance, you only need to explicitly put offsets of all non-primary vtables in their names. Using example above,
class A
{
public:
virtual void AA() { printf("A::AA"); };
};
class B
{
public:
virtual void BB() { printf("B::BB"); };
};
class C : public A, public B
{
public:
void AA() override { printf("C::AA"); }
void BB() override { printf("C::BB"); };
};
will give
struct A;
struct A_vtbl {
void (*AA)(A* __hidden this);
};
struct __cppobj A {
A_vtbl* __vftable;
};
struct B;
struct B_vtbl {
void (*BB)(B* __hidden this);
};
struct __cppobj B {
B_vtbl* __vftable;
};
struct __cppobj C : A, B {};
struct C_vtbl {
void (*AA)(C* __hidden this);
};
struct C_0008_vtbl {
void (*BB)(C* __hidden this);
};
Vtable elements can have any names which is pretty convenient, they shouldn't match their parents names.