This post is going to be brief on details, but I'll point to my github project which I hope may find greater use. I'd be particularly interested in hearing from anyone willing to replicate the work on the windows client. The following was performed on the Tibia-1079 client.
Let's take the example of creating an auto login as motivation. The technique I will describe is to locate the UI element in memory and call the button_press() and button_release() functions. This technique generalises to all elements, and the button_release() functions make for very interesting break points. By breaking on the button_release() function of map element, I was able to quickly identify the below function addresses.
Code:
int32_t (*set_target)(int32_t, int32_t, int32_t, int32_t, int32_t) = (void *)0x080e8930;
int32_t (*astar)(void) = (void *)0x080837e0;
int32_t (*local_z)(void) = (void *)0x08185a70;
// The below will walk to (14, 6) relative to the top left corner of the map (0, 0)
int32_t x = 14;
int32_t y = 6;
int32_t z = local_z();
to_global(&x, &y, &z);
set_target(x, y, z);
astar();
The Tibia GUI structure has been known for a while, however I am going to further explain how to call the GUI methods.
First we need to understand the top level structure of the top level GUIComponent class.
Code:
typedef struct GUIComponent GUIComponent;
struct GUIComponent {
void *vtable;
int32_t unknown1;
int32_t unknown2;
GUIComponent *parent;
GUIComponent *sibling;
int32_t x;
int32_t y;
int32_t width;
int32_t height;
};
This is an abstract class from which all gui components inherit, and therefore the compiler has created a vtable for this type. GUIComponent defines a large number of virtual functions, and the vtable for a child of GUIComponent contains the function pointers to the child's implementations.
Using edb I found two global pointers.
Code:
GUIComponent **root = (GUIComponent **)0x83d4e84;
GUIComponent **dialog = (GUIComponent **)0x83d4e80;
I've also partially determined the structure of the vtable
Code:
struct {
void *unknown1[9];
GUIComponent *(*get_children)(GUIComponent *w);
void *unknown2[1];
GUIComponent *(*get_sibling)(GUIComponent *w);
void *unknown3[1];
GUIComponent *(*get_parent)(GUIComponent *w);
void *unknown4[9];
int32_t (*button_press)(GUIComponent *w, int32_t x, int32_t y, int32_t button);
int32_t (*button_release)(GUIComponent *w, int32_t x, int32_t y, int32_t button);
void *unknown5[4];
int32_t (*key_press)(GUIComponent *w, int32_t code);
int32_t (*key_release)(GUIComponent *w, int32_t code);
void *unknown[0];
} *vtable;
And this is everything we need to know to call button press on the root element.
Code:
GUIComponent **root = (GUIComponent **)0x83d4e84;
(*root)->vtable.button_press(*root, x, y, 0);
(*root)->vtable.button_release(*root, x, y, 0);
If we know the x y position of the element we want to click we are set. But this is less than ideal, we would rather not need to know the screen coordinates of the element. We may even want to interact with an off screen part of the game.
So now we need to interpret the structure of the GUI tree, and to do this we need to understand GUIComposite, a GUIComponent with children.
Code:
typedef struct GUIComposite GUIComposite;
struct GUIComposite {
GUIComponent component;
GUIComponent *children;
};
GUIComposit adds a single element and no new virtual members. If there were new virtual methods, a second vtable pointer would come before children;
Code:
typedef struct GUIComposite GUIComposite;
struct GUIComposite {
GUIComponent component;
// void *vtable;
GUIComponent *children;
};
GUIComponents form linked lists of siblings. Additionally, the GUIComposite may have children. The resulting structure looks as below.
Code:
MainWindow
- TitleWindow
- GUIFrameWindow
- GUIComposite
- GUITextButton - GUITextButton - GUITextButton - GUITextButton - GUITextButton - GUITextButton
The row of buttons corresponds to the buttons down the left hand side of the logged out screen. To traverse the tree structure though, we need to determine whether or not a GUIComponent is a GUIComposite. In c++, this would be done by making a dynamic cast.
Code:
GUIComponent *component = ...;
GUIComposite *composite = dynamic_cast<GUIComposite>(component);
if (composite != NULL) {
// was a composite
}
The linux client was compiled with gcc, which created the c __dynamic_cast method
Code:
// the -1 means "no offset hint", which is always valid but potentially inefficient
void *__dynamic_cast(void *ptr, rtti::type_info src_type, rtti::type_info dst_type, -1);
If we know where to find the rtti::type_info, we can call the underlying __dynamic_cast method. Well we can actually do better than that, my rtti_extractor will extract the rtti::type_info addresses for all dynamic types.
We can also extract the class names and inheritance hierarchy, from which I've generated an svg.
Most importantly, we can determine if a GUIComponent has children, which allows us to traverse the tree. login.c is a proof of concept with uses the aforementioned machinery to traverse the GUI tree and find the relevant gui components. Then we can call the function pointers which perform the login sequence. Because we found the elements in memory, there is no need to know their x, y positions.
Code:
// Find account / password entry components.
GUIPasswordEdit *account = (GUIPasswordEdit *)find_gui_component(*current, is_password, NULL);
GUIPasswordEdit *password = (GUIPasswordEdit *)find_gui_component(*current, is_password, account);
GUIButton *ok = (GUIButton *)find_gui_component(*current, is_button, "Ok");
// We don't know which password edit is which, but we know account is above password.
// Sort on y offset.
if (account->edit.element.component.y > password->edit.element.component.y) {
GUIPasswordEdit *tmp = account;
account = password;
password = tmp;
}
gui_type((GUIComponent *)account, "account");
gui_type((GUIComponent *)password, "password");
gui_click((GUIComponent *)ok, 1, 1, 0);
For most parts of the client, this is exactly the level of control we want. Calling the button_press and button_release functions of a button will do exactly what the client would do if you clicked it. (Not exactly true, the client would normally store the last x,y click position of the cursor, but this is irrelevant).
But for more complicated elements like the map we want control at a lower level. Instead of specifying cursor positions we would rather specify map coordinates. We know that the button_release function on the MapWindow must be converting the screen coordinates to x, y to map coordinates and then using them to move the character, and to determine how it does this the button_release function makes a splendid starting point.
There are a couple of ways to find the vtable of a GUI component type. The easiest way I can think of is to use the print_tree method in my proof of concept to print out a detailed view of the GUI, including type names and importantly the address of the vtable.
The other method is to look up the address of the rtti::type_info in rtti.h. If you search for (rtti_address - 4), you will find a reference from (vtable_address - 4).
Once you have the vtable address, the button_release function pointer is at (vtable_address + 96). You can look at the vtable structure above to work out the offsets of the other functions. You can now set a break point here and start tracing how the map responds to button releases.
If you've read this far, you may be interested in trying the proof of concept. It has a second file xpending.c which defines XPending, a function the Tibia client calls on each iteration of the event loop. This makes a fantastic entry point for our code, which we will inject by using LD_PRELOAD.
To run the proof of concept on a linux system, it should be as simple as.
Code:
make
LD_PRELOAD=/path/to/tibia-hook.so ./Tibia
It will attempt to log in using the incorrect credentials "account" and "password", which you may change to your own.
If you would be interested in further reverse engineering the GUI structure or working on a windows port, contact me via my github.