Die GUI! Wofür interssierten wir uns überhaupt für die GUI? Die GUI mag zwar ein Randaspekt sein, aber 3 Gründe sind genug, dass wir eine Analyse vornehmen:
- Wir wollen später eine eigene Lobby schreiben, und dabei wenn möglich diese GUI Elemente wie Buttons verwenden
- Viele Werte, wie z.B. der nachher besprochene Mapname, tauchen zunächst nur in der GUI auf.
- Die GUI ist die Brücke zwischen Benutzer und Programm. Wenn wir wissen wollen, welche Funktion aufgerufen wird wenn ein bestimmter Button gedrückt wird, müssen wir wissen wie die Eventhandler für diesen Button aussehen. Und danach landen wir genau in der gesuchten Funktion (Beispiel: Neues Spiel Button -> Direkt beim Game-Konstruktor)
Anhand der GUI kann man schön sehen, wie OOP verwendet wurde. Alle Menüs erben von einem “Grundmenü”. Die Menüs implementierren die Funktionen wie zeichnen und handeln von Benutzereingaben, so dass eine schöne Kapselung entsteht. Und zum Wechseln des Menus wird einfach der Zeiger des aktuellen Menus aktualisiert, schon wird das nächste Menu angezeigt.
Beginnen wir mit etwas Code, der das Menu wechselt:
v4 = (int)this; // v4 ist das Register EBX
this->CurrentMenuIndex = a2; // Im Argument 2 wird übergeben, welche ID das neue Menu hat
v5 = v4->lpActualScreen; // Zeiger auf das aktuelle Menu Element
if ( v5 ) if (lpActualScreen != null)
{
v7 = (int)((char *)&v5->dword4 + *(_DWORD *)(v5->dword4 + 4)); // dynamsichen Funktionszeiger holen
(**(void (__thiscall ***)(_DWORD, _DWORD))v7)(v7, 1); // und aufrufen
} |
Auch wenn der von IDA Pro erzeugte Code schrecklich aussieht, so kann man daraus einiges rauslesen. Fangen wir mit der ersten komischen Zeile an. Wieso speichert das Programm den this-Pointer in einer lokalen Variable (bzw genauer gesagt einem Register?). Da der This-Pointer in ECX übergeben wird, aber weitere Funktionen mit einem vielleicht anderen This-Pointer aufgerufen werden, wird der This-Pointer in EBX gespeichert. Das ist für die Funktionsscope nun der gesicherte This-Pointer. Nun wird das aktuelle Menu überprüft (lpActualScreen != null). Wenn ein Menu gesetzt ist, so wird in v7 ein Funktionszeiger geladen, der sich aus Werten des aktuellen Menus zusammensetzt. Im Anschluss wird die Funktion aufgerufen, und dies passiert dynamisch. Per IDA Pro kann man solche Calls nicht zurückverfolgen, per Debugger allerdings schon. In diesem Falle wird eine “Uninitialize”-Methode aufgerufen, die ein paar Aufräumarbeiten erledigt. Da jedes Menu andere Aufräumarbeiten zu leisten hat, ist auch die Funktion jeweils anders.
Weiterlesen
4 people like this post.