Archive for the ‘ XNA und Spieleprogrammierung ’ Category

HearthstoneCalc – Injecting

Der Werdegang von HearthstoneCalc hat einen eigenen Post verdient, in welchem dann weitere Ansätze (Monte Carlo Tree Search) vorgestellt werden. In diesem Blogeintrag geht es allerdings um das Injecten und Daten abrufen aus Hearthstone. Denn ohne Daten aus dem Spiel müssen wir HearthstoneCalc garnicht mehr weiterentwickeln.

Uns wird es zum Glück leicht gemacht. Hearthstone ist mit der Engine Unity3D geschrieben. Für Blizzard hat das den großen Vorteil, das Spiel einfach auf mobile Geräte (Tables und Smartphones, Android und iOS) portieren zu können. Für uns hat es den großen Vorteil, dass Unity .NET (genauer gesagt: Mono) verwendet. Und da die Binaries noch nicht einmal obfuscated vorliegen, haben wir ein relativ einfaches Spiel.

Der für Unity relevante Code wird in der Assembly-CSharp.dll gespeichert. Die Anzahl der Klassen überschreitet alles, was ich bisher gesehen habe. Dementsprechend fällt es am Anfang schwer, uns zurecht zu finden. Auch muss man sich zunächst an die Unity-Eigenarten gewöhnen: Jedes Objekt ist eine Entity, die wiederrum mehrere Unterentities haben kann. Oftmals haben Klassen Unterklassen, enthalten viel zu viel Renderlogik oder scheinen nie erzeugt zu werden.

GameStateDiagDaher gilt zu klären: Welche Daten müssen wir alle Abrufen? Die Daten aus dem Spiel müssen in die Strukturen von HearthstoneCalc gebracht werden. Die wichtigste und größte Struktur ist hierbei die GameState-Klasse (Links). Sie enthält das Spielfeld mit den Monstern, die Karten des Spielers und die Infos über die jeweiligen Helden. Natürlich haben die Helden weitere Unterklassen, wie z.B. die ausgerüstete Waffe oder die gesetzen Geheimnisse. Auch Monster sind inzwischen komplexer geworden, denn sie können verschiedene Buffs besitzen und auf andere umliegende Minions Einfluss ausüben.

Die Klassen aus Hearthstone können wir nicht einfach übernehmen, denn sie enthalten viel zu viele Informationen. Diese Klassen sind nicht dafür ausgelegt, Millionen möglicher Boardzustände zu speichern. Zu diesem Problem werde ich nächsten Post über HearthstoneCalc noch mehr sagen.

Der große Vorteil von Mono ist natürlich, dass wir realtiv einfach .NET Code ausführen können. .NET Code in einem Programm ausführen? Klingt doch sehr nach CLR-Hosting!? Leider ist das nicht so einfach, denn die Unity-Klassen mögen es nicht, wenn sie außerhalb von mscorlib.dll Thread aufgrufen werden. Versucht man eine Unity-Funktion direkt aufzurufen, so erhält man folgende Fehlermeldung zu gesicht:

Weiterlesen

6 people like this post.

HearthstoneCalc - Einstieg und Abstaktion

In diesem Post werde ich etwas über mein aktuelles Freizeitprojekt “HearthstoneCalc” berichten. Ich werde fortsetzende Posts über das Tool danach richten, wie interessiert Ihr Leser an den Artikeln seid. Schauen wir mal ;)

Ich interessiere mich schon länger für Spiele KIs und bin der Überzeugung, dass Computer bei der Planung von Spielzügen weit effektiver sind als ein menschliches Gehirn. Dabei geht es nicht um die Kreativität die ein menschlicher Spieler an den Tag legt, sondern die reine rationale Beurteilung der Spielsituation. Schachcomputer sind inzwischen ungeschlagen, weil sie einfach Unmengen an Situationen vorraussehen und bewerten können. Seid einiger Zeit fasziniert mich das Spiel “Hearthstone“, ein Rundenbasiertes Kartenspiel ähnlich Magic oder Yu-Gi-Oh. Man kann Diener oder Zauber spielen, die eigenen Heldenfähigkeiten nutzen oder mit Waffen die Kontrolle über das Board behalten. Der Spielverlauf wird später nochmal genauer erklärt, jetzt geht es erstmal um das Programm “HearthstoneCalc”.

Ziel des Programms ist es, alle möglichen Züge zu eruieren und den besten auszuwählen. Dabei sollte das Programm den aktuellen Spielstatus auslesen, die Berechnungen durchführen und den Spielzug selbstständig ausführen. Im Gegensatz zu Spielen mit perfekten Informationen wie Schach oder Vier-Gewinnt haben wir bei Hearthstone drei große Probleme:

  1. Die gegnerischen Karten sind unbekannt. Zwar werden von den Spielern meist ähnliche und als effektiv geltende Decks gespielt, allerdings varieren auch hier die Karten. Die Karten des Gegners die er auf der Hand hat sind verdeckt und bieten daher keine perfekten Informationen über zukünftige Spielzüge
  2. Hearthstone braucht keinen Skill, sondern nur Glück“. An diesem Zitat ist leider viel wahres dran. Viele Zauber wirken auf alle Einheiten verteilt und viel Schaden (z.B. von explodierenden Bomben) ist variabel. Glück gehört einfach zum Spiel dazu, daher kann man den Ausgang eines Zuges nicht effektiv berechnen. Man muss mit Wahrscheinlichkeiten rechnen, und davon nicht wenige. Später gibt es ein Beispiel, wie komplex ein solcher Zug sein kann.
  3. Manche Decks verfolgen eine spezielle Strategie und sammeln daher Karten, um einen großen Zug zu machen. Solch eine “Strategie” bringt man dem Computer schlecht bei, vorallem da er nebenher darauf achten muss, nicht gegen den Gegner zu verlieren.

Ihr merkt, das Projekt wird komplex. Und ein solches Projekt fängt man am besten an, indem man abstrahiert und vereinfacht. Ein solch einfaches Modell kann dann später erweitert werden. Effektiv heißt das auf die beiden oberen Punkte bezogen: Wir schauen zunächst nur den aktuellen Zug unseres Spielers an und lassen die mögliche gegnerische Reaktion außer Acht. Zudem berechnen wir nur die Züge ohne Wahrscheinlichkeiten bzw Rechnen mit dem schlimmsten Ausgang für uns.

Weiterlesen

7 people like this post.

Sicherheit in Onlinespielen - Wichtige Grundregeln

Egal ob Browsergame oder Multiplayer PvP, Onlinespiele sind in der heutigen Zeit sehr gefragt. Es macht einfach mehr Spass, gemeinsam mit Freunden zu Spielen, statt sich mit einer pseudoklugen KI an der Seite herrum zu schlagen. Der Trend geht klar Richtung Onlinespiele, was wiederrum auch Gamehacker anlockt. In dem heutigen Artikel werden Grundlagen besprochen, auf die man beim Programmieren achten sollte. Anschaulich wird das ganze durch Beispiele erklärt.

Das Spektrum von Hacks, über Bots und Exploits ist zu groß, als dass man es alles in einem Artikel abdecken könnte. Daher werden primär nur Echtzeit-Onlinespiele betrachtet. Diese Techniken lassen sich aber beliebig auf andere Multiplayer-Spiele übertragen. Und noch kurz zu den verwendeten Begriffen in diesem Artikel:

  • Server: Dabei handelt es sich um einen zentralen (!) Server, der das eigentliche Spiel hostet. Er nimmt Daten von anderen Spielern entgegen und verteilt die “Änderungen” im Spiel an alle verbundenen Clients. In machen Spielen wird der Server auch als “Host” bezeichnet, was im Prinzip nichts anderes bedeutet.
  • Client: Ein Spieler, der mit dem Server verbunden ist und aktiv Stati (Schießen, Laufen) an den Server sendet

Der wohl bedeutenste Grundsatz dürfte sein: Traue nie den vom Client empfangenden Daten. Der Client schickt, wie schon oben erwähnt, in regelmäsigen Abständen Daten wie seine eigene Position und seine Attribute an den Server. Wenn ein Gamehacker die Welt-Position des Clients verändert, wird diese auch zum Server gesendet. Wenn der Server diese Daten nun ungeprüft übernimmt, haben wir einen sog. Teleport-Hack. Solche Teleport-Hacks befanden sich u.a. in großen Onlinespielen wie Metin 2.

Während sich der entstehende Vorteil durch Teleportierungen noch in Grenzen hält, hört der Spass bei sog. Masskill-Hacks auf. Dabei sendet der Client die Nachricht “Ich habe XX Schaden am Gegner Y gemacht”. Der Server übernimmt dieses ungeprüft und tötet den Gegner Y. In Battlefield 3 ist dieser Hack inzwischen gefixxt, die Kollisionserkennung ist allerdings immernoch Clientseitig. So sind “Instant Kills” möglich, indem man einfach den Schaden, den eine Waffe verursacht, ins unendliche erhöht. Daher die Regel Nummer 2: Auf Plausibilität prüfen! Im Falle einer clientbasierten Kollisionserkennung kann man durch Wände schießen. Wenn der Server nun einmalig prüft, ob Spieler X den Spieler Y überhaupt treffen kann, wären solche Hacks schon verhindert. Warum das nicht gemacht wird? Weil solche Checks relativ viel Rechenleistung brauchen und einen Server mit 64 Spielern überfordern würde.

Weiterlesen

8 people like this post.

Siedler 3 - Lobby Preview

Die letzen Tage habe ich wieder viel mit Siedler 3 verbracht. Allerdings nicht mit Spielen, sondern mit Reversing und Coding.

Durch einige Tricks habe ich es geschafft, einzelne UI Elemente zu “kontrollieren” und Aktionen mit ihnen durchzuführen, die eigentlich nur nach einem Mausklick auf das jeweilige Element erfolgen. Denn die “neue” Lobby wird über das LAN-Menu laufen, nur dass der Bildschrim komplett ersetzt wird. Um aber ein Spiel aus dem LAN-Modus zu starten, sind folgende Schritte notwendig:

  • Spieler klickt auf den Adapter in der ersten Listview (DirectPlay Adapter)
  • Spieler klickt in die Textbox und gibt die IP des Hosts ein
  • Spieler klickt auf “Suche Spiele” -> Spiele werden angezeigt
  • Spieler wählt ein Spiel aus der Lisview aus
  • Spieler klingt auf “Mitspielen”.

Diese 5 Schritte kann ich nun per Programmcode aufrufen, auch wenn es noch nicht immer klappt (Bei einem Fehlerdialog hängt sich das Programm auf, da dieser eigentlich nicht vom UIThread aufgerufen wird etc).

Das bedeutet, dass einer neuen Lobby nun nichts mehr im Wege steht. Oder doch? Die eigentliche UI für die Lobby muss ja noch geschrieben werden. Und dabei kann (wegen des alten GDI standarts, leider nicht mal GDI+) auch keine UI Library verwendet werden. Selber schreiben heißt die Devise! In den letzten Tagen habe ich daher einige Basis UI Klassen angefertigt (TextLabel, Button, ListView) um daraus eine Lobby zu konstruieren. Das Resultat:

Weiterlesen

9 people like this post.

Siedler 3 - Die GUI

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.

Siedler 3 - Draw Hook und GDI

Mit dem Wissen über COM Objekte aus dem letzen Beitrag gerüstet, sind wir nun in der Lage, Code vor bzw hinter die jeweiligen DirectX Funktionsaufrufe zu packen. DirectX 8 nutzt zum Zeichnen sog. Surfaces, also eine Fläche, auf die gezeichnet wird. Im normalfall wird jeweils das Backbuffer Surface beschrieben und im Anschluss mit dem primären Surface ausgetauscht (Flip). Das macht Siedler 3 im Hintergrund für uns, daher müssen wir uns nicht darum kümmern.

Wir könnten einerseits über die Funktion “Lock” und “Unlock” direkten Zugriff auf das Surface bekommen, um dort einzelne Pixel zu setzen. Da dieses aber nicht so effektiv ist, gibt es andere Wege. Man könnte die sog. “Blit” (bit block transfer) Funktion aufrufen, um Teile aus einer Grafik in das Surface zu schreiben. So wird das zum Beispiel mit den Menuelementen, oder auch mit Sprite-Grafiken gemacht. Da wir aber zunächst nur Text ausgeben wollen, bietet uns DirectX 8 ein nettes Feature: GDI.

S3_GDI

GDI steht für Graphics Device Interface und bietet uns Funktionen zum Zeichnen, wie wir es z.B. von dem .NET Graphics Namespace gewöhnt sind. Über die Funktion “GetDC” erhalten wir einen sog. “Device Context”, welcher einem Zeichenhandle entspricht. Mit diesem können wir nun alle Funktionen aufrufen, die ein solches Handle entgegen nehmen, z.B. FillRect oder auch TextOutA/W. Zum simplen Zeichnen nehmen wir also folgenden Code:

// In einer Init Methode
LPLOGFONT fontArial = new LOGFONT();
 
ZeroMemory(this->fontArial, sizeof(this->fontArial)); // Wir setzen lieber nochmal den Speicher auf 0
wcscpy(this->fontArial->lfFaceName, L"Arial"); // Wir kopieren den Fontnamen den wir haben wollen rein
this->fontArial->lfQuality = ANTIALIASED_QUALITY; // Und setzen noch ein bisschen Anti Analising
 
// In der Methode zum Textzeichen
HFONT hFont = CreateFontIndirect(font);		// Eine "richtige" Font aus unserer LOGFONT
SelectObject(this->currentHdc, hFont);		// GDIHandle, nutze für das nächste diese Font
SetBkMode(this->currentHdc, TRANSPARENT);	// Mach den Background Transparent, sonst gibts Rahmen
SetTextColor(this->currentHdc, color);		// Setze die Color (color ist eine COLORREF struct)
TextOutA(this->currentHdc, x,y, pText, strlen(pText));	// Und schreibe den Spass in unser Surface

Weiterlesen

9 people like this post.

Siedler 3 - COM Objekte, DirectDraw und Fenstermodus

Grau, teurer Freund, ist alle Theorie, und Grün des Lebens goldner Baum. (Mephisto)

Diese Thorie wird, so leid es mir auch tut, den gesammten folgenden Artikel durchziehen. Man braucht ein paar Grundlagen, auf die man später zurückgreifen kann, wenn es richtig spassig wird. Diese Grundlagen sind zu vergleichen mit einem Bauplan: Woher sollten wir wissen, an welchen Schrauben wir drehen müssen und wie diese Aussehen, um ein bestimmtes Ziel zu erreichen?

Fangen wir mit den COM-Objekten, bzw genauer mit Interfaces und ihren “Virtual Tables” (VTables) an. Ein Objekt das von einem Interface erbt, ist gezwungen, die im Interface definierten Methoden zu implementieren (die Methoden zu “überladen”). Dabei kommen sog. Funktionszeiger zum Einsatz, die auf eine Funktionsaddresse zeigen. Von dieser Funktion ist der Prototype bekannt, also wird diese Adresse angesprungen, nachdem die bekannten Argumente auf den Stack gepushed wurden. COM-Interfaces sind spezielle Interfaces, die alle von IUnknown erben. Konkret bedeutet das, dass jedes COM-Objekt die 3 in IUnknown deklarierten Methoden “QueryInterface”, “AddRef” und “Release” implementiert. QueryInterface erstellt ein Interface, das einer IID entspricht. Im Post CLRHosting Reloaded wurde z.B. ein Interface vom Typ “IID_CLRRuntimeHost” erstellt, das uns wiederrum bestimmte Funktionen bereitgestellt hat. AddRef erhöht den Referenzcount, Release erniedrigt den Referenzcount und löscht das Objekt, falls dieser Null wird. Wichtig ist hier zu erwähnen, dass die VTable eines Interfaces, wenn es erstellt und gelöscht wurde, sich noch immer an der selben Stelle im Speicher befindet. In OllyDbg sieht ein solches Interface folgendermaßen aus:

S3_Interface1

Hier haben ein direkten Call eines Interfaces. Es handelt sich dabei (wie bei allen Interface Calls) um die sog. Thiscall-Convention, heißt im Register ECX wird ein THIS_POINTER übergeben, sonst werden die anderen Argumente normal auf den Stack gepushed. Nun wird CALL [ECX+80h] aufgerufen, was einem Funktionsaufruf in der VTable entspricht. Genauer gesagt die Funktion (80h / 4), also 20h oder 32. Im Dump-Window habe ich hier ECX geladen, was direkt der VTable entspricht. Die ersten 3 markierten Funktionen Funktionen ensprechen den Funktionen aus IUnknown (QueryInterface, AddRef, Release), in Orange ist die aufgerufene Funktion “Unlock” markiert. Schauen wir doch mal in die ddraw.h, in welcher alle unsere Interfaces deklariert sind.

Weiterlesen

11 people like this post.

Neue Reverse Engineering Serie - Siedler 3

Wer kennt es nicht? Das Kultspiel Siedler 3, das sogar noch heutzutage mein und so manch anderes Spielerherz höher schlagen lässt. Aber keine Sorge, in dieser Serie wird kein Hack entwickelt, dafür wäre das Spiel zu schade. Es geht also um etwas anderers:

Der Publisher Ubisoft, welcher Blue Byte im Jahre 2001 aufkaufte, hat vor einiger Zeit die öffentliche Lobby (siedler3svr.bluebyte.de:3344) abgeschaltet. So ist ein Online Matchmaking umständlich nur noch über den LAN Modus und diverser Tricks zu erreichen. Über das Back2Hack Board bekam ich Kontakt zu einem Programmierer, welcher das Matchmaking wieder leichter und komfortabler machen will. Das optimale Ziel wäre es, die Lobby wiederherzustellen, aber die Matchmaking Logik über seine Server laufen zu lassen.

Nach ein wenig Kontakt beschlossen wir, ein solches Projekt in Angriff zu nehmen. Dabei geht es im ersten Schritt darum, das Matchmaking über die Spielesuche im LAN-Menu zu entwickeln. Der selbst programmierte Server empfängt alle neu gehosteten Spiele und sendet diese an andere Clients, die gerade Spiele suchen. Dabei sendet der Server Packete, wie sie der Siedler 3 Client/Host im LAN schicken würde.

S3_Lobby

Das alles klingt leichter gesagt als getan, denn Siedler 3 legt einem einige Stolpersteine, wenn nicht sogar ziemlich große Felsbrocken in den Weg. So ist das Spiel ohne Fenstermodus erschienen, was dank Auflösung von max. 800 x 600 Debugging unmöglich macht. Des weiteren wird das alte DirectDraw verwendet, was ein Segen und Fluch zugleich ist. Der DirectPlay Support ist in Windows Vista nahezu vollständig entfernt worden, was Probleme beim Erzeugen von gewrappen Interfaces gibt. Und als ob das nicht alles schon schwer genug wäre, so ist der Programmierstil der 2000der Jahre auch nicht der strukturierteste.

In der folgenden Serie wird es also darum gehen, Siedler 3 unter die Lupe zu nehmen und geschickt ein paar Hooks zu platzieren, die uns im Ende eine funktionierende Lobby ermöglichen. Ob es funktioniert weiß ich noch nicht, allerdings bin ich durch die bisherigen Ergebnisse ziemlich zuversichtlich. Zudem bekommt ihr einen Eindruck, wie Gruppen wie z.B Techogods arbeiteten, wenn sie Spiele Analysieren und Modifizieren. Des weiteren werden ein Informationen zu DirectDraw, GDI und natürlich Netzwerktechnik vermittelt.

Der nächste Artikel wird sich mit COM Objekten, Direct Draw und dem Fenstermodus befassen. Je nach Länge des Posts wird noch der DircetDraw Hook vorgestellt. Und bis dahin fröhliches Siedeln!

Greez

 

8 people like this post.