Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creating __cppobj structures #33

Open
vient opened this issue Jul 11, 2019 · 10 comments
Open

Creating __cppobj structures #33

vient opened this issue Jul 11, 2019 · 10 comments

Comments

@vient
Copy link

vient commented Jul 11, 2019

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.

@igogo-x86
Copy link
Owner

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
Copy link
Owner

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.

@vient
Copy link
Author

vient commented Jul 22, 2019

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.

@igogo-x86
Copy link
Owner

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

@vient
Copy link
Author

vient commented Jul 22, 2019

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.

@igogo-x86
Copy link
Owner

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

@vient
Copy link
Author

vient commented Jul 23, 2019

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.

@igogo-x86
Copy link
Owner

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.

@Trass3r
Copy link

Trass3r commented Oct 1, 2020

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

@vient
Copy link
Author

vient commented Oct 15, 2021

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants