Anti-Jammer-Thread

Hier kann über allgemeine Themen diskutiert werden, die sonst in kein Forum passen.
Insbesondere über Szene, Games, Kultur, Weltgeschehen, Persönliches, Recht, Hard- und Software.
Benutzeravatar
Krishty
Establishment
Beiträge: 8416
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

State of the Art ist seit einigen Jahren das Ghidra der NSA. Das kostet im Gegensatz zu IDA nichts.

Ghidra hat eine offene Architektur, man lädt sich also bspw. das Plugin zum Disassemblieren und Dekompilieren von PlayStation-Executables herunter.

Das kommt mit einer Erkennung aller Standardfunktioneren der Sony-C-Laufzeitbibliothek, die damals ausgeliefert wurden und in jedem PSX-Executable ähnlich gelinkt sind. Damit sind schonmal alle Aufrufe an sprintf() und scrnBlit() und malloc() und Hunderte mehr im Dekompilat als solche gekennzeichnet.

Weiterhin sind auch ihre Rückgabetypen gekennzeichnet, Ghidra weist dann also jeder Variable, die aus malloc() befüllt wird, rekursiv void * zu. Und jeder, die in strcpy() gereicht wird, char *. Damit sind schonmal 80–90 % aller Zeiger, Integer, und Gleitkommazahlen (hatte die PSX nur extrem begrenzt) im Dekompilat unterscheidbar. Man weiß zwar noch nicht, ob es ein signed oder unsigned int ist, oder welcher konkrete Datentyp hinter einem Zeiger steckt, aber das macht alles gehörig einfacher.

Und das hatten wir alles 2019, als wir Driver reverse engineered hatten, ohne KI.

Mit Debug-Symbolen bekommt man dann nochmal die Namen aller Funktionen dazu. 2019 war das ein scheiß Akt, die Debug-Informationen ins passende Format für Ghidra zu konvertieren, aber mittlerweile gibt es für so ziemlich alle Formate Ghidra-Plugins.

Jetzt kommen die neuen KI-Assistenten und Agenten, die in den letzten Jahren entstanden sind.

Was Jonathan erwähnt – der KI sagen, dass links unten ein HUD sein sollte, und das im Code finden – wird bei PS2-Spielen bspw. von https://github.com/hkmodd/PCSX2-MCP versprochen. Häng deinen Agenten an PCSX2-MCP und Ghidra, und lass ihn werkeln. Ich hab’s noch nicht selber ausprobiert, nur grob verfolgt wie andere das machen.

… dann hängt man einen SDL-basierten Wrapper für PlayStation-Standardfunktionen dran (gibt’s zu hauf im Internet; für ReDriver2 hatte Soapy damals Psy-Cross weiterentwickelt https://github.com/OpenDriver2/PsyCross) und schon ist der vibe-coded Port fertig.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Jonathan
Establishment
Beiträge: 2959
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Jonathan »

Spiele Programmierer hat geschrieben: Heute, 19:38 Okay, es ist ein Missverständnis mit dem Begirfflichkeiten: C-Code generieren ist "out of scope" für einen Disassembler, denn der erzeugt nur Assemblercode. Alle normalen Debugger kriegen das wunderbar hin und denke gibt auch dedizierte Projekte. Deswegen meinte ich, dass es simpel ist.
Ah, ja, natürlich. Zwei ganz unterschiedliche Dinge, Assemblercode anzeigen kann in der Tat nicht so schwierig sein.

Nochmal bezüglich templates: Natürlich war das ein bisschen weitgegriffenes Wunschdenken. ABER: Wenn man einmal C-Code mit sinnvollen Namen und so hat, ist der Rest ja quasi nur noch Umstrukturieren. Da würde ich KI schon recht viel zutrauen. "Kann man diesen Code kompakter uns lesbarer machen, indem man ähnliche Teile zusammen fasst?" - Fast gleiche Funktionen die nur unterschiedliche Typen haben werden sicherlich erkannt. Außerdem kann man ja die Anforderungen lockern. Sagen wir, man könnte aus einem Haufen Funktionen eine Klasse machen, aber dann würde nach dem erneuten kompilieren immer noch ein this-Zeiger kopiert werden müssen - macht mein Programm vielleicht 0.2% langsamer, und das ist heute egal. Dadurch kommt hinten dann sicherlich nicht der identische Code raus, der vor 20 Jahren tatsächlich geschrieben wurde, aber das Verhalten bleibt gleich, er ist gut strukturiert und lesbar und performancetechnisch immer noch total ok.

Vollautomatisch würde ich das wohl eh nicht machen wollen, am Ende muss ja der Mensch entscheiden, was tatsächlich lesbar ist. Aber solche Vorschläge finden und auf Entwicklerwunsch automatisch umsetzen und auf Korrektheit testen halte ich für recht realistisch.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Spiele Programmierer
Establishment
Beiträge: 435
Registriert: 23.01.2013, 15:55

Re: Anti-Jammer-Thread

Beitrag von Spiele Programmierer »

Krishty hat geschrieben: Heute, 20:17 Weiterhin sind auch ihre Rückgabetypen gekennzeichnet, Ghidra weist dann also jeder Variable, die aus malloc() befüllt wird, rekursiv void * zu. Und jeder, die in strcpy() gereicht wird, char *. Damit sind schonmal 80–90 % aller Zeiger, Integer, und Gleitkommazahlen (hatte die PSX nur extrem begrenzt) im Dekompilat unterscheidbar. Man weiß zwar noch nicht, ob es ein signed oder unsigned int ist, oder welcher konkrete Datentyp hinter einem Zeiger steckt, aber das macht alles gehörig einfacher.
Ja das ist lieb, aber in einem echten Programm sind doch die wenigsten Zeiger char-, Integer- oder Gleitkommazahlzeiger. Statt einem int-Zeiger, ist eStattdessen ist es ein Zeiger auf ein Spielobjekt, welches von einem dynamischen Typ in einem Spiel-Manager-Objekt liegt, welches in einem Levelobjekt liegt, welches...
Es reicht nicht rekursiv Funktionen und Register zu verfolgen. Du musst auch durch Speicheroperationen durchschauen. Da wird es erst lustig und das ist wo dann alles mögliche passieren kann.
Oder reden wir einfach von so alten Spielen, wo noch alles in globalen Arrays ohne OO-Hölle lag?
Krishty hat geschrieben: Heute, 20:17 Was Jonathan erwähnt – der KI sagen, dass links unten ein HUD sein sollte, und das im Code finden – wird bei PS2-Spielen bspw. von https://github.com/hkmodd/PCSX2-MCP versprochen. Häng deinen Agenten an PCSX2-MCP und Ghidra, und lass ihn werkeln. Ich hab’s noch nicht selber ausprobiert, nur grob verfolgt wie andere das machen.
Ich bin da sehr skeptisch. 0 issues, 11 Sternchen, Readme sieht nach KI-Slop aus. Das eigentliche soll wohl auch eine externe KI lösen, aber hat diese KI überhaupt schon so viel debugging und Reverse-Engeneering-Erfahrung? Ich finde das von Jonathan beschrieben Szenario recht komplex. Ich wäre da echt erstaunt, wenn das quasi automatisch geht.
Krishty hat geschrieben: Heute, 20:17 … dann hängt man einen SDL-basierten Wrapper für PlayStation-Standardfunktionen dran (gibt’s zu hauf im Internet; für ReDriver2 hatte Soapy damals Psy-Cross weiterentwickelt https://github.com/OpenDriver2/PsyCross) und schon ist der vibe-coded Port fertig.
Implizierst du, dass dein Dekompilator bereits Code erzeugen kann, der bis auf Funktionsaufrufe direkt wieder für x86 kompiliert?
Falls ja, dann bin ich auf jeden Fall beieindruckt. SO habe ich das nicht in Erinnerung bei uns.
ABER: Wenn man einmal C-Code mit sinnvollen Namen und so hat, ist der Rest ja quasi nur noch Umstrukturieren.
Ja, wenn nicht geinlined ist, die Namen ähnlich sind und es analog aussieht.
Inlining ist wohl der größte Spielverderber.
erneuten kompilieren immer noch ein this-Zeiger kopiert werden müssen - macht mein Programm vielleicht 0.2% langsamer, und das ist heute egal.
Wieso soll eine Klasse das Programm 0.2% langsamer machen?
Benutzeravatar
Krishty
Establishment
Beiträge: 8416
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Krishty »

Spiele Programmierer hat geschrieben: Heute, 21:58Ja das ist lieb, aber in einem echten Programm sind doch die wenigsten Zeiger char-, Integer- oder Gleitkommazahlzeiger. Statt einem int-Zeiger, ist eStattdessen ist es ein Zeiger auf ein Spielobjekt, welches von einem dynamischen Typ in einem Spiel-Manager-Objekt liegt, welches in einem Levelobjekt liegt, welches...
Es reicht nicht rekursiv Funktionen und Register zu verfolgen. Du musst auch durch Speicheroperationen durchschauen. Da wird es erst lustig und das ist wo dann alles mögliche passieren kann.
Ist seit zehn Jahren drin. Sobald du eine Reihe von Zeiger-Dereferenzierungen mit Integer-Offsets im Dekompilat siehst, klickst du rechts drauf und wählst “Auto Create Structure”. Dann legt Ghidra dir eine struct-Definition an und leitet aus den Speicheroperationen und Offsets her, an welcher Stelle welches Member liegt und wie groß die Struktur wohl insgesamt ist. Und der neue Zeiger-Typ wird dann wieder aus der aktuellen Funktion hoch und quer propagiert.

Bild

(Sorry, das PSX-Dekompilat kriege ich gerade nicht auf weil ich das Plugin ewig nicht aktualisiert habe)

Hier links ist das Disassembly von einem Programm aus dem Job, für das große Teile des Quelltextes verloren gegangen sind. Voller movs. Du drückst auf eine Adresse, drückst L (label), und wenn sie im Daten-Segment liegt legt Ghidra eine globale Variable an der Stelle an (Größe und Typ entsprechend aller Zugriffe, die programmweit lesen/schreiben). Du siehst überall mov mit Offset, markierst die Variable im Dekompilat, Rechtsklick, Auto Create Structure, Ghidra legt dir ein struct an. Im Dekompilat rechts siehst du ja deutlich, wie der Code auf dreifach geschachtelte Klassen samt Array-Indizes zugreift. Habe dir die Definition der Struktur geöffnet, voller unbekannter Bytes, weil keine Funktion in der Nähe auf die zugreift.

In das Dekompilat kannst du nicht schreiben, das habe also nicht ich dahingemacht. Das wird von Ghidra immer on-the-fly generiert, während du navigierst, aus der Ground Truth im Disassembly links plus meine Variablennamen und Strukturdefinitionen.

this kommt auch von Ghidra, weil es den Compiler erkannt hat und weiß, in welchem Register der das this hält.

Ich will nicht sagen, dass es ein Kinderspiel wäre, aber schon wesentlich einfacher und scheller als was ich 2014 mit IDA machen musste.
Implizierst du, dass dein Dekompilator bereits Code erzeugen kann, der bis auf Funktionsaufrufe direkt wieder für x86 kompiliert?
Falls ja, dann bin ich auf jeden Fall beieindruckt. SO habe ich das nicht in Erinnerung bei uns.
Ghidra spuckt C aus mit Platzhalter-Datentypen und Platzhalter-Funktionen für Dinge, an denen sich das Dekompilieren verschluckt hat. Wie viel nachgebessert werden muss, hängt von Architektur und Compiler der Executable ab. Das if oben ist schräg optimiert worden; keine Ahnung, ob das so kompiliert. Geht vor allem am Anfang fast nie, aber wenn man die Worst Offender glattgebügelt hat, ist man recht nah dran. Ich weiß übrigens nicht, wie man das ganze Projekt nach C exportiert – ich picke mir immer nur die Funktionieren raus, die ich brauche. Aber ich mache das halt auch nicht professionell.

Ich weiß auch nicht, warum ich hier auf “Ja aber” antworte, guck doch einfach ein Video über Ghida 😁
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Jonathan
Establishment
Beiträge: 2959
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Anti-Jammer-Thread

Beitrag von Jonathan »

Spiele Programmierer hat geschrieben: Heute, 21:58 Wieso soll eine Klasse das Programm 0.2% langsamer machen?
Ich auch nicht wirklich, war aber auch nur ein zufälliges Beispiel. Vielleicht realistischer: Man packt alle strings in std::string. Das macht den Code netter lesbar und man kann ein paar mehr High-Level Funktionen verwenden, aber ab und an verursacht es extra Kosten die vielleicht nicht rausoptimiert werden können. Um meinen Spielernamen in die Highscore zu schreiben ist mir sowas aber total egal, deshalb würde ich wollen, dass so etwas ersetzt wird. Aber das wäre wohl wirklich erst ein zweiter Schritt, nachdem man schon Code hat den man gut neukompilieren kann, denke ich.

Hmmm, Ghidra klingt echt interessant. Aber selbst wenn es so gut und einfach wäre, das Dekompilieren ähnlich zeitaufwändig ist, wie das ursprüngliche Schreiben, ist das ja für ein durchschnittliches Spiel trotzdem noch jahrelange Arbeit. Würde ich gerne mal ausprobieren, aber naja, es wird ja eh nicht fertig :'(
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Antworten