HexRaysPyTools icon indicating copy to clipboard operation
HexRaysPyTools copied to clipboard

Creating __cppobj structures

Open vient opened this issue 5 years ago • 10 comments

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.

vient avatar Jul 11 '19 16:07 vient

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

igogo-x86 avatar Jul 14 '19 07:07 igogo-x86

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.

igogo-x86 avatar Jul 20 '19 19:07 igogo-x86

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.

vient avatar Jul 22 '19 09:07 vient

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

igogo-x86 avatar Jul 22 '19 10:07 igogo-x86

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.

vient avatar Jul 22 '19 11:07 vient

Yeah, that will do. The tiny little thing left is too implement it

igogo-x86 avatar Jul 23 '19 09:07 igogo-x86

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.

vient avatar Jul 23 '19 11:07 vient

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.

igogo-x86 avatar Jul 23 '19 12:07 igogo-x86

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

Trass3r avatar Oct 01 '20 09:10 Trass3r

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.

vient avatar Oct 15 '21 16:10 vient