Closed XdotCore closed 2 weeks ago
Can you share the contents of MainMenuScreen's class structure? Does it contain fields for both inherited vftables? Were your structures manually generated or with the RTTI script or something else? Are you able to share the binary?
It was generated by the RTTI script, and no, I cannot share the binary as it is a paid game.
Here is a screenshot of the class structure It contains the vftable and fields for the first inherited class, then the vftable and fields for the second inherited class, then the fields for itself, with its new virtual functions in the first vftable, if that helps.
What is at offset 4? It is cut off. Is it data or a vbtable? I'm sorry for slow responses. I'm out most of this week but checking in once per day. If you are able to share the contents of the RTTI structures for the MainMenuScreen class that would help too. You mentioned the function was called with a cast to IEventListener. Do you think that is correctly cast? Can you share image of the calling function?
At offset 4 it is a data.
The inheritance structure is CSListLink<>
-> GUI2PageHandler
-> FlowPageHandler2
-> MainMenuScreen
and IEventListener
-> MainMenuScreen
defined as MainMenuScreen : FlowPageHandler2, IEventListener
.
The structure of the MainMenuScreen
written out is
struct MainMenuScreen : FlowPageHandler2, IEventLister {
void** vftable_for_FlowPageHandler2;
CSListLink<class_GUI2PageHandler>_data CSListLink<class_GUI2PageHandler>_data;
GUI2PageHandler_data GUI2PageHandler_data;
void** vftable_for_IEventListener;
IEventListener_data IEventListener_data;
MainMenuScreen_data MainMenuScreen_data;
}
Note: FlowPageHandler2
does not have any data or vftable of its own
GUI2PageHandler
's is
struct MainMenuScreen : CSListLink<class_GUI2PageHandler> {
void** vftable;
CSListLink<class_GUI2PageHandler>_data CSListLink<class_GUI2PageHandler>_data;
GUI2PageHandler_data GUI2PageHandler_data;
}
and IEventListener
's is
struct IEventListener {
void** vftable;
IEventListener_data IEventListenerData;
}
As you can notice, MainMenuScreen
is GUI2PageHandler
's struct + IEventListener
's struct + MainMenuScreen_data
.
The function RecieveEvent
is inherited from IEventListener
and the virtual ptr is placed and overriden in vftable_for_IEventListener
. I do not know yet where the function is being called but since the base classes function requires a IEventListener*
, the function will be called with one, and the overriden function will accept one. Due to this and the IEventListener
struct being offset within the MainMenuScreen
struct, the MainMenuScreen*
"this" argument is also offset to where the IEventListener
struct is positioned.
I have confirmed this behavior using hooking to the RecieveEvent
struct with mirrored versions of the structs in my project, and analyzing which vftable is at 0x0 with or without static casting.
I have made a repository to hopefully explain what is going on better by example: https://github.com/XdotCore/TestMultipleInheritance.
Thanks for the example
Here is how to update your function to fix the -1 offset issue:
You need to turn off the thiscall convention for the ReceiveEvent method and just replace it with stdcall. Then from the GUI, you can create the adjusted pointer be right clicking on the parameter in the decompiler (might have to add an extra param after changing the calling convention and also make sure it is a pointer), and select "Adjust Pointer Offset". Then in the dialog, enter "IEventListener" for data-type, "0x1c" for offset, and anything you want for the name.
Thanks so much, I got it working!
I used the "Adjust Pointer Offset" option to generate the type. I set the data-type to MainMenuScreen with offset 0x1c. Because I am keeping it as thiscall, it errored because you can't retype the this of a thiscall, but it still made the type which is what I needed. Then I edited the function by turning on "Use Custom Storage" and set the "this" type to the generated type, and now it works!
Thanks so much, I got it working!
I used the "Adjust Pointer Offset" option to generate the type. I set the data-type to MainMenuScreen with offset 0x1c. Because I am keeping it as thiscall, it errored because you can't retype the this of a thiscall, but it still made the type which is what I needed. Then I edited the function by turning on "Use Custom Storage" and set the "this" type to the generated type, and now it works!
Glad you got it working!
I have this class called
MainMenuScreen
, and it inherits fromMenuFlowPageHandler2
andIEventListener
. The vftable for theIEvent Listener
is placed after the vftable and inherited fields fromMenuFlowPageHandler2
, and has a method calledRecieveEvent
that is overridden inMainMenuScreen
.MainMenuScreen
'sRecieveEvent
has the pointer offset to the vftable ofIEventListener
(most likely because it was called with it cast to anIEventListener
) and causes the access of members to be off in the decompilation. I was tipped off to this mainly because of the negative indexer to access a field before theIEventListener
vftable.Is there a way to tell the ghidra decompiler that the pointer is of
MainMenuScreen
class but offset to theIEventListener
vftable?Example: