astrelsky / Ghidra-Cpp-Class-Analyzer

Ghidra C++ Class and Run Time Type Information Analyzer
MIT License
633 stars 46 forks source link

How to process a x86:LE:32/64:default:gcc ELF binary #35

Closed ghost closed 1 year ago

ghost commented 3 years ago

Hi there!

Can anybody please tell me how to process a x86:LE:32/64:default:gcc ELF binary?

I know that in the README.md it says:

Dynamic RTTI Handling

For GNU binaries a project archive will need to be created to provide data required for analysis. Each library containing dynamic RTTI will need to be analyzed and copied into the project archive via the TypeInfoTree prior to analyzing the program. In the future an archive wil be distributed for libstdc++.

I also found the following in the Releases page:

Issues related to empty vtable structs have been fixed. Also attached to this release are gdt archives for x86 and x86_64 libstdc++.so.6. To use them simply drag the file into Ghidra's project manager. Then before running the class analyzer, open the project archive in the ClassTypeInfo Tree. When running the C++ Class Analyzer's with "Use Archived Rtti Data" selected, the analyzer with search through the archived for the data type and use them to apply the data type and function definitions. This is particularly useful for stripped programs.

But for The Love Of God, I CANNOT do it!

I'm not even starting with what's wrong with the above quotes.

It's not that I didn't try, I did, I really did.

For, I don't know, by now maybe 6 hours straight?!?

Ended giving up in frustration, trying everything that I could think of.

However, the extension is working just fine for PE32(+) executables for MS Windows.

So in conclusion, it's working for PE but it's not working for ELF, and I really need it working for ELF.

Can anybody please help me?

Huge thanks!

PS:

These are the binaries (x86:LE:64:default:windows PE & x86:LE:64:default:gcc ELF) and this is the source code for my test program(s) (taken from The Ghidra Book: The Definitive Guide, by Chris Eagle and Kara Nance):

/* rtti.cpp */

#include <iostream>
#include <ctime>

class BaseClass {
public:
    BaseClass();
    virtual void vfunc1() = 0;
    virtual void vfunc2();
    virtual void vfunc3();
    virtual void vfunc4();
private:
    int x;
    int y;
};

class SubClass : public BaseClass {
public:
    SubClass();
    virtual void vfunc1();
    virtual void vfunc3();
    virtual void vfunc5();
private:
    int z;
};

class SubSubClass : public SubClass {
public:
    SubSubClass();
    virtual void vfunc3();
private:
    int z;
};

BaseClass::BaseClass() {
    std::cout << "Base Class constructor called" << std::endl;
}

void BaseClass::vfunc2() {
    std::cout << "Base Class vfunc2() called" << std::endl;
}

void BaseClass::vfunc3() {
    std::cout << "Base Class vfunc3() called" << std::endl;
}

void BaseClass::vfunc4() {
    std::cout << "Base Class vfunc4() called" << std::endl;
}

SubClass::SubClass() {
    std::cout << "Sub Class constructor called" << std::endl;
}

void SubClass::vfunc1() {
    std::cout << "Sub Class vfunc1() called" << std::endl;
}

void SubClass::vfunc3() {
    std::cout << "Sub Class vfunc3() called" << std::endl;
}

void SubClass::vfunc5() {
    std::cout << "Sub Class vfunc5() called" << std::endl;
}

SubSubClass::SubSubClass() {
    std::cout << "Sub Sub Class constructor called" << std::endl;
}

void SubSubClass::vfunc3() {
    std::cout << "Sub Sub Class vfunc3() called" << std::endl;
}

void call_vfunc(BaseClass *bc_ptr) {
    bc_ptr->vfunc3();
}

int main() {
    BaseClass *bc_ptr = new SubClass();
    std::cout << "typdeid(bc_ptr)  = " << typeid(bc_ptr).name() << std::endl;
    std::cout << "typdeid(*bc_ptr) = " << typeid(*bc_ptr).name() << std::endl;
    call_vfunc(bc_ptr);

    SubClass *sc_ptr = dynamic_cast<SubClass*>(bc_ptr);
    std::cout << "typdeid(sc_ptr)  = " << typeid(sc_ptr).name() << std::endl;
    std::cout << "typdeid(*sc_ptr) = " << typeid(*sc_ptr).name() << std::endl;
    call_vfunc(sc_ptr);

    BaseClass *bc_ptr_2;
    srand(time(0));
    if (rand() % 2) {
        bc_ptr_2 = dynamic_cast<SubClass*>(new SubClass());
    }
    else {
        bc_ptr_2 = dynamic_cast<SubClass*>(new SubSubClass());
    }
    std::cout << "typdeid(bc_ptr_2)  = " << typeid(bc_ptr_2).name() << std::endl;
    std::cout << "typdeid(*bc_ptr_2) = " << typeid(*bc_ptr_2).name() << std::endl;
    call_vfunc(bc_ptr_2);

    return 0;
}
astrelsky commented 3 years ago

The test programs don't require any rtti from a dynamic library. You can just import the program into ghidra and run the GCC RTTI Analyzer and GCC C++ Class Analyzer.

What I was trying to explain is that there are times where a class will inherit from rtti in a dynamic library (such as libstdc++.so.6). When this is the case that rtti data needs to be made available to the analyzer in order for classes which need it to be analyzed. This can be done with the provided project archives or by making your own.

You can test the situation where this is required by adding a class which has std::exception as a base class to your test program.

Reading the description again I really should have specified that it is only necessary if rtti not found in the program is required for analysis.

astrelsky commented 3 years ago

@paketto do you still need help?