Source Code aus Spielen ansehen

4 Antworten

Es gibt Decompiler, der Wikilink wurde ja schon gepostet. Das Problem an den Dingern ist, dass der Sourcecode, der am Ende dabei rauskommt, in der Regel kaum zu lesen ist, insbesondere bei größeren Projekten. (Ich hoffe dir ist bewusst, dass dabei nicht der ursprüngliche Code, wie ihn die Entwickler geschrieben haben, entstehen kann - ansonsten würde ich mir ernsthaft Gedanken machen, was in deinen ersten 2 Lehrjahren passiert ist.)

Mein Tipp daher an dich: Es gibt inzwischen einige Spieleklassiker, deren Sourcecode frei im Netz veröffentlich ist (Quake z.B.). Such nach denen. Die generelle Programmstruktur denke ich wird sich auch über die Jahrzehnte nicht großartig verändert haben. Mal vielleicht davon abgesehen, dass damals OOP noch nicht so verbreitet war (C statt C++).

Also mal vorweg ich bin kein Informatiker ;)

Aber richtig es gibt decompiler -> http://de.wikipedia.org/wiki/Decompiler Die einfachste Art an sowas wie du meinst zu kommen sind die Ressourcen des Spiels anzuschauen... Sprich wenn jetzt zum Beispiel bei einem Spiel in den Objekt-Daten, wo zum Beispiel die Spielcharakter gespeichert sind, man die Definition eines weiteren Charakters findet, der aber im regulären Spielverlauf nicht auftaucht...

Das ganze kann man aber meist auch ohne Decompiler feststellen. Entweder komplett altmodisch in dem man sich die Dateien im normalen Dateiviewer ansieht und bestimmte Definitionen sucht... Zum anderen zum Beispiel durch Spieleditoren die die Ressourcen-Dateien bearbeitbar machen...

Ich hoffe es war einigermaßen verständlich für's Erste... vielleicht kann ja der ein oder andere etwas detaillierter drauf eingehen

Wenn man Assembler kann, und sich mit Reversing (Reverse Engineering) beschäftigt hat, kann man eine ausführbare Datei (EXE, DLL, ELF, SO, ...) durch einen Disassembler jagen, und am Ende den Assembler-Code lesen.

Viele Leute gruseln sich vor Assembler und denken, dass es schwer zu lernen sei, das liegt aber nur daran, dass sie es nie wirklich gelernt haben, bzw. nicht von Anfang bis Ende durchhalten konnten.

Wer Assembler wirklich kann, der liest disassemblierten Maschinencode annäherend so flüssig, wie C++ Quelltext! Allerdings erfährt man anhand des Assembler-Codes noch wesentlich mehr, als nur durch reinen C++ Sourcecode:

  • Man sieht genau, mit welchem Compiler und mit welchen Flags kompiliert wurde.

  • Man erkennt in welcher Sprache das Programm geschrieben wurde.

  • Man erkennt für welches Betriebssystem entwickelt wurde.

  • Man erkennt an welchen Stellen, welche Sprachkonstrukte eingesetzt wurde.

  • Man sieht wie Parameter übergeben, und Werte zurück gegeben werden.

  • Man sieht Exceptions und den genauen Aufbau von Strukturen und Typen.

  • Man erkennt ob Konstanten und Variablen lokal oder global sind.

Eigentlich gibt es nichts, was ein geübtes Auge nicht sieht! Aber die Betonung liegt hier auf "geübt"! Die meisten Anfänger bis Fortgeschrittene kommen damit nicht klar.

Kleines Beispiel: Ich habe mir die gängigsten Compiler für die gängigsten Programmiersprachen auf den gängigsten Plattformen installiert, und dann kurze Testprogramme mit den gängigsten Flags kompiliert.

Die kleinen Testprogramme waren oft nur wenige Zeilen lang, und enthielten meist nur ein einziges Sprachkonstrukt (Schleife, Bedingung, Funktionsaufruf, Addition, Exception, ...) welches dann nach dem kompilieren automatisch wieder disassembliert wurde.

Das habe ich mehrere Tage lang gemacht, mit diversen Kombinationen aus Plattformen, Compilern, Sprachen, Konstrukten und Flags. Das waren zwar Hunderte (oder gar Tausende) von Tests, die sich aber sehr gelohnt haben! Ich weiß jetzt ziemlich genau, was ein Assembler-Bruchstück tut, wenn ich es nur aus dem Augenwinkel sehe!

Ich denke, so ähnlich arbeiten auch viele andere Reverser! Wie du siehst, ist im Endeffekt gar kein richtiger "Quelltext" in einer höheren Sprache mehr nötig. Der Assembler-Code verrät eigentlich alles, was man wissen möchte.

Darüber hinaus kann man sein Zielprogramm auch durch einen Debugger laufen lassen, und sich (teilweise automatisiert) angucken, was das Programm live bei der Ausführung macht. (Online-Analyse)

Disassembliert man nur und guckt sich dann den trockenen Assembler-Code an, ist das eine sog. Offline-Analyse.

Und um auf deine Frage zurück zu kommen: Ich bezweifel, dass eine Art JetPack bei GTA im Programm-Code fest einkompiliert ist. Wenn da jemand etwas gefunden hat, dann a) bei den Assets oder b) bei irgendwelchen Skripten des Spiels (die dann tatsächlich mehr oder weniger direkt im Quelltext vorliegen!).


Youkakun  21.11.2014, 13:46

"Wer Assembler wirklich kann, der liest disassemblierten Maschinencode annäherend so flüssig, wie C++ Quelltext!"

Wenn er denn die Zeit und Geduld hat, denn der Vorteil der Hochsprachen ist eben auch, dass viel weniger Code für eine Aufgabe vonnöten ist. Bei alten Spielen kann man das noch machen, bei moderneren wird sich kaum einer dies erlauben.

"Man sieht genau, mit welchem Compiler und mit welchen Flags kompiliert wurde."

Das würde ich nicht unterstreichen. Bei Optimierungsleveln gibt es teilweise minimale Unterschiede, wofür man einen Großteil des Codes analysieren müsste. Feedback-Flags, wie für Warnungen, bekommt man nicht heraus. Compiler sind auch nicht so leicht zu unterscheiden, wie z.B. GCC & Clang, dafür schreiben sie allerdings ihre Signatur (lästigerweise) fast immer dazu :/ (Was mancher auch manuell entfernt -> Pech gehabt)

"Man erkennt in welcher Sprache das Programm geschrieben wurde."

Kommt ganz auf die Arbeitsweise von Compilern an, die sich auch weiterentwickeln... mir fällt dies bis heute extrem schwer.

"Darüber hinaus kann man sein Zielprogramm auch durch einen Debugger laufen lassen, und sich (teilweise automatisiert) angucken, was das Programm live bei der Ausführung macht. "

Wenn denn mit Debug-Symbolen kompiliert wurde! Dies liefert man üblicherweise nicht aus, das Programm soll ja wenig Platz brauchen und schnell geladen werden.

"Ich bezweifel, dass eine Art JetPack bei GTA im Programm-Code fest einkompiliert ist."

Meine ich auch. So leicht liest man ein komplettes Spielelement nicht heraus und sowas wird auch normalerweise über eine Plugin-Schnittstelle hinzugefügt.

0
TeeTier  21.11.2014, 22:44
@Youkakun
Wenn er denn die Zeit und Geduld hat, denn der Vorteil der Hochsprachen ist eben auch, dass viel weniger Code für eine Aufgabe vonnöten ist. Bei alten Spielen kann man das noch machen, bei moderneren wird sich kaum einer dies erlauben.

Da hast du Recht, und das hätte ich dazu schreiben sollen! Zum Reversen benötigt man Zeit, um sich ein Gesamtbild des Programms / Moduls im Kopf, auf Papier, als Strukturdiagramm oder im Texteditor zusammenbauen zu können.

Dennoch versteht man i. d. R. auch C++ nicht sofort, wenn man plötzlich mitten in ein unbekanntes Stück Code springt.

Und da der Compiler gewisse Konstrukte immer gleich, oder sagen wir mal annäherend gleich übersetzt, erkennt man dann wirklich auf den ersten Blick, welcher Zweig einer if-Abfrage z. B. Fehlerbehandlung oder interessanten Programmcode enthält. Und solche Sachen sieht man dann mindestens genauso schnell wie in C++ Quellcode geschrieben. :)

Allerdings, gebe ich dir - wie gesagt - Recht, vor allem wenn man Strukturen im Speicher analysieren will. Das dauert bei sehr sehr Komplexen schon gerne mal zwei bis drei Stunden in Anspruch nehmen! :)

Das würde ich nicht unterstreichen. Bei Optimierungsleveln gibt es teilweise minimale Unterschiede, wofür man einen Großteil des Codes analysieren müsste.

Also -O0 bis -O3 inkl. -Ofast und -Osize kann man ALLEN gängigen Compilern definitiv IMMER erkennen, und das ist auch keine Kunst! Man erkennt beim näheren Hinsehen an bestimmten Konstrukten sogar, ob -std=c89 oder -std=c99 gesetzt war!

Feedback-Flags, wie für Warnungen, bekommt man nicht heraus.

Natürlich nicht, und das war auch nicht damit gemeint. Ein -Wall wird man nicht erkennen können, da es ja gar keinen Einfluss auf den resultierenden Maschinencode hat. Ich habe mich da missverständlich ausgedrückt, sorry! :)

Aber man kann durchaus sagen, dass alle Flags, die in irgendeinerweise Einfluss auf das Kompilat haben, mab eben dort auch in irgendeiner Form wiederfindet. Und wenn bestimmte Kombinationen davon auftreten, kann man ziemlich treffsicher raten, dass es ein -Ox mit einigen weiteren Flags war, die man aber ebenfalls recht treffsicher erraten kann.

Im Übrigen meine ich mit "wissen" immer "gut geraten"! Denn in weit über 90% trifft das zu, was am Wahrscheinlichsten ist. Und wenn sich zwei konträre Punkte gegenseitig ausschließen, dann ist man bei 99% ... bei drei Punkten ist man schon bei 99,9% ... Ich arbeite jetzt schon viele Jahre auf dem Reversing Gebiet, und ich erinner mich nicht, auch nur ein einziges mal so grob daneben gelegen zu haben, dass ich mehr als 2 Punkte revidieren musste!

Wenn man gut rät, kommt man immer zum Ziel! IMMER! (Auch dann, wenn man sich zwischendurch mal irrt!) :)

Compiler sind auch nicht so leicht zu unterscheiden, wie z.B. GCC & Clang, ...

Oh doch, gerade diese beiden erzeugen völlig anderen Code! Ohne Quatsch, dafür reichen meistens schon 10 Assembler-Befehle in Folge, um definitiv sagen zu können, welcher Compiler im Einsatz war!

Sogar die Unterschiede zwischen dem ICC und dem GCC sind gravierend, schon bei einfachsten Konstrukten, obwohl die sich wesentlich näher sind, als GCC und Clang! Die Microsoftschen Compiler erkennt man sowieso. :)

Und ich würde sogar noch soweit gehen und sagen, dass man auch die einzelnen Versionen ein und des selben Compilers auseinander halten kann! Natürlich nicht jeden Bugfix, aber wenn man weiß, was geändert wurde, und die entsprechenden Konstrukte kennt, kann man sogar Minor-Releases unterscheiden! Das trifft vor allem auf SSE, 3Dnow, AVX und Floating-Optimierungen zu, da sich dabei regelmäßig sehr viel ändert bzw. verbessert wird! :)

wie z.B. GCC & Clang, dafür schreiben sie allerdings ihre Signatur (lästigerweise) fast immer dazu :/

Bevor ich ein Programm analysiere, gucke ich mir sowieso erstmal die Strings und diverse Tabellen an, bevor ich den Disassembler an die Arbeit lasse. Auf diese Art und Weise erfährt man meist schneller mehr über gewisse Grundeigenschaften eines Programms.

(Was mancher auch manuell entfernt -> Pech gehabt)

Nicht nur das! Einige Leute ersetzen die entsprechenden Strings auch durch die von anderen Compilern, um einen Reverser aufs Glatteis zu führen! Aber wie gesagt, sind solche Strings nur Bonus ... man braucht sie nicht, und die Wahrheit kommt früher oder später sowieso ans Licht! :)

Wenn denn mit Debug-Symbolen kompiliert wurde!

Nein, nein, natürlich rede ich von gestrippten Binaries! Debug-Symbole sind purer Luxus, der aber fast immer entfernt wird! :)

Man kann mit jedem Debugger jede Binary in Assembler "debuggen"! Das ist gängige Praxis bei Crackern oder Sicherheitsforschern! :)

Diese Debugger kann man i. d. R. auch skripten oder per API bzw. Plug-In programmieren, dass alles automatisiert abläuft, und z. B. Kopierschutzmechanismen zur Laufzeit automatisch umgangen werden können. Das macht man auch bei der Malware-Analyse so, um die "Selbstzerstörung" eines Trojaners während der Ausführung zu deaktivieren! :)

1

Es gibt da zwei Arten einmal den Dissasembler der den Binärcode in Assemblerinstruktionen rückübersetzt und den Decompiler der versucht den Binärcode in Quelltext einer Hochsprache zurück zu übersetzen. Da heutzutage aber Compiler viel optimieren kommt da in den seltensten Fällen was wirklich lesbares bei raus.