For a turnkey solution for eve online memory reading see
----------------
Eine schlüsselfertige Lösung für eve online memory reading ist zu finden unter
----------------
----------------
----------------
----------------
Moin,
habe mal ein paar Dinge die ich bei der Entwicklung meines Bot über Eve Online gelernt habe zusammengefasst:
Eve Online Memory Reading
In diesem Artikel erkläre ich wie man im Speicher des Eve Online Client an Adressen mit interessanten Informationen kommt. Ich beschränke mich dabei auf die im UI Tree enthaltenen Informationen. Was man dort findet reicht aus um einen Bot mit allen benötigten Informationen über den Zustand des Spiels zu versorgen.
Der C# Quelltext im Anhang implementiert die hier vorgestellte Vorgehensweise.
Der UI Tree
Der UI Tree ist ein Baumförmiger Graph dessen Knoten jeweils eine rechteckige Region innerhalb des Elternknotens beschreiben. Die Wurzel (d.h. der Knoten ohne Elternknoten) entspricht dabei der gesamten Bildfläche des Spiels. Abgesehen von der Fläche enthalten die einzelnen Knoten noch zusätzliche Informationen wie z.B. Text oder eine Farbe welche dieser Fläche zugeordnet sind.
Folgende Grafik zeigt einen Ausschnitt einer Visualisation des Baums. Darin sind die Ränder der Knotenflächen sowie in manchen Knoten ein diesen Knoten zugewiesener Text dargestellt.
Der im Beispiel visualisierte UI Tree enthält ein Kontextmenü welches auf dem Info Panel "Route" auf dem nächsten Sonnensystemmarker der Route geöffnet wurde. Aus diesem Beispiel ist bereits ersichtlich das der UI Tree alle Informationen enthält um einen Autopilotenbot zu entwickeln.
Abbildung eines Knotens des UI Tree in Python
Jeder Knoten des UI Tree wird durch mehrere Python Objekte abgebildet. Das Hauptobjekt enthält bei +8 einen Zeiger auf ein Python dict. Die Einträge in diesem dict beschreiben unter anderem die Region welche dieser Knoten auf dem Gesamtbild, bzw innerhalb des Elternknotens einnimmt. Der zunächst wichtigste Eintrag in diesem dict ist dem Schlüssel "children" zugeordnet. Dieser führt über einige Umwege zu den Kindknoten:
Der Eintrag "children" zeigt auf ein Objekt vom Typ "PyChildrenList" welches wiederum bei +8 einen Zeiger auf ein dict enthält. Dieses dict wiederum enthält unter dem Schlüssel "_childrenObjects" einen Eintrag der auf eine Python "list" zeigt.
Diese "list" enthält die Zeiger zu den Kindknoten, d.h. an dieser Stelle wird auf die Hauptobjekte andere Knoten des Baums verwiesen. In den Blättern (d.h. Knoten ohne Kindknoten) ist dann die "list" leer oder es ist im direkt vom Hauptobjekt referenzierten "dict" kein Eintrag "children" enthalten.
Das folgende Bild zeigt die Struktur der Python Objekte in einem einzelnen Knoten welche zum traversieren des Baums verwendet werden:
Anhand dieser Struktur können ausgehend vom Wurzelknoten alle anderen Knoten des Baums gefunden werden.
Im Beispielprogramm wird das traversieren des Baums durch die Funktion "UITreeNode.EnumerateChildrenTransitive" beschrieben.
Abbildung der Python Objekte im Speicher
Ermitteln des Type eines Python Objekt
Aus der geht hervor das jedes Python Objekt bei +4 bytes den Member "ob_type" hat welcher auf ein Objekt zeigt welches den Type des Objekt repräsentiert.
Desweiteren wird in der darauf hingewiesen das dies auch für die Objekte gilt welche einen Python Type repräsentieren. Dort ist auch nachzulesen das jedes Type Object bei +12 den Member "tp_name" hat der auf einen nullterminierten String zeigt welcher dem Namen des Type entspricht.
Auch die Python Implementation im Eve Online Client verwendet diese Struktur.
Damit kann für ein Python Objekt zu welchem nur die Adresse vorliegt der Name des Type dieses Objekt ermittelt werden.
Im Beispielprogramm wird dies durch die Funktion "PyObject.TypeNameForObjectWithAddress" erledigt.
Ermitteln aller Python Objekte eines Type
Durch Rükwärtsverwenden der Zeiger aus dem Member "ob_type" kann für einen Type mit gegebenem Namen eine Teilmenge von Addressen berechnet werden welche alle Addressen von Objekten dieses Type enthält.
Im Beispielprogramm wird dies durch die Funktion "PyTypeObject.EnumeratePossibleAddressesOfInstance sOfPythonTypeFilteredByObType" beschrieben.
zum traversieren des Baums verwendete Python Type
Um von einem Knoten zu dessen Kindknoten zu kommen werden folgende Gruppen von Objekttypen verwendet:
- Hauptobjekt des Knoten. Diese Gruppe umfasst mehrere Type, darunter zum Beispiel "UIRoot".
- "dict"
- "PyChildrenList"
- "list"
Für die Hauptobjekte des Knoten und den Type "PyChildrenList" ist die gesamte Struktur im Speicher bereits bekannt da in diesen nur konstante Offsets verwendet werden.
Python Type "dict"
Hierbei handelt es sich um ein gewöhnliches Python dictionary. Im UITree werden als Schlüssel üblicherweise Objekte vom Typ "str" verwendet.
Die Abbildung auf den Speicher ist im CPython Quelltext in den Dateien und beschrieben.
Im Beispielprogramm werden Objekte dieses Type durch die Klasse "PyDict" gelesen.
Python Type "list"
Die Abbildung auf den Speicher ist im CPython Quelltext in den Dateien und beschrieben.
Im Beispielprogramm werden Objekte dieses Type durch die Klasse "PyList" gelesen.
Ermitteln der Addresse der Wurzel des UITree
Das Hauptobjekt des Wurzelknoten hat den Type "UIRoot". Um die Wurzel zu finden werden zunächst die Addressen (alle im Prozess vorhandenen) mit der Funktion "EnumeratePossibleAddressesOfInstancesOfPythonType FilteredByObType" für den Type "UIRoot" gefiltert. Dabei werden normalerweise noch mehrere Addressen übrig bleiben. Daher wird im nächsten Schritt für jede der übrigen Addressen jeweils durch Nutzung der oben beschriebenen Struktur die Anzahl der Kindknoten berechnet. Die Addresse des Knotens für welchen die meisten Kindknoten gefunden wurden wird als die Wurzel des Baums angenommen.
Im Beispielprogramm wird die Addresse des UIRoot durch die Funktion "EveOnline.UIRoot" berechnet.