Hallo Besucher, der Thread wurde 5,5k mal aufgerufen und enthält 24 Antworten

letzter Beitrag von oobdoo am

Disassembler Entwicklung (OT aus: Heute so gecodet...)

  • Heute eine Funktion in den Disassembler eingebaut mit welcher man unsinnigen Code erkennen kann. :puhh:


    Was ist unsinnigen Code? Im CPC gibt es beim Z80 die Besonderheit das einige Befehle nicht bzw.

    nicht sinnvoll anwendbar sind. https://k1.spdns.de/Vintage/Sc…C%20Systembuch/z180.htm#A

    Auch ein CALL 0 (führt Reset durch) macht höchstens 1x im Programm Sinn. EIn mehrfaches auftauchen

    kann also nur bedeuten das dies kein Z80 Code darstellt sondern irgendwelche Daten. Als Sonderfall

    mag das bei selbstmodifizierenden Code anders sein. Nun habe ich die Möglichkeit eine Liste mit

    bestimmten OpCode-Kombinationen zu verwenden, die mir einen Hinweis geben wo im Code mit Daten

    zu rechnen ist oder nicht.

  • Hmm, bei meinem Disassembler verfolge ich einen anderen Ansatz.

    Ich deklariere erstmal den ganzen Speicher als "BINARY"

    Als nächstes hangel ich mich von der Einsprungadresse des Programms durch sämtliche Branches oder JMP/JSR Aufrufe.

    Bereits besuchte Bereiche werden als "VISITED" gekennzeichnet. Wenn der Bereich einem Opcode entspricht dann zusätzlich als "CODE".

    Dabei versuche ich auch Sprungtabellen zu identifizieren. Klappt im Grunde schon ganz ordentlich.

  • Auch ein CALL 0 (führt Reset durch) macht höchstens 1x im Programm Sinn. EIn mehrfaches auftauchen

    kann also nur bedeuten das dies kein Z80 Code darstellt sondern irgendwelche Daten.


    Das sehe ich anders. Was ist mit Subroutinen? Dort kann CALL 0 (oder ein anderer "unsinnger" Befehl) mehrfach vertreten sein und Sinn machen. Pauschal von der Häufigkeit eines Befehls darauf zu schließen, ob er sinnvoll ist, finde ich in einem Disassembler eher problematisch.

  • Dabei versuche ich auch Sprungtabellen zu identifizieren. Klappt im Grunde schon ganz ordentlich.

    Für den C64? Ich vermute das dort wegen der Vielzahl an verwendeten Speicheradressen für Sound, GFX usw. eine einfachere Zuordnung was Code und Daten ist einfacher umzusetzen ist.

    Der CPC hat wegen der der eingesetzten Z80 CPU nur wenige Adresse die direkt von der Hardware angesprochen werden (RST-Befehle und Bildschirmspeicher). Der Rest wird über IN/OUT

    Befehle der CPU erledigt. Außerdem finde ich so einen "intelligenten" Disassembler schwerer umzusetzen. Daher habe ich einen ganz anderen Weg eingeschlagen.



    Mein Disassembler geht erstmal davon aus das alles nur Code ist. Das Ergebnis kann man in der mittleren TextBox (mit hochgeheimen FruttyMan Z80 Code) sehen.

    Links davon ist eine kleine Editor-TextBox wo ich mit einigen Steuerbefehlen den Disassembler Anweisungen geben kann wie die Ausgabe aussehen soll.

  • Das sehe ich anders. Was ist mit Subroutinen? Dort kann CALL 0 (oder ein anderer "unsinnger" Befehl) mehrfach vertreten sein und Sinn machen. Pauschal von der Häufigkeit eines Befehls darauf zu schließen, ob er sinnvoll ist, finde ich in einem Disassembler eher problematisch.

    Damit magst Du Recht haben. Ich hatte aber auch geschrieben "die mir einen Hinweis geben wo im Code mit Daten zu rechnen ist oder nicht". ;)

    Da mein Disassembler recht dumm ist (siehe hier) ist das für mich eine Möglichkeit den Anwender mit Hinweisen zu versorgen.

  • Außerdem finde ich so einen "intelligenten" Disassembler schwerer umzusetzen.

    Naja, vollautomatisch wird es wohl auch nicht gehen.

    Eine manuelle Nachanalyse wird man wohl immer benötigen.


    Ich denke aber das man das Konzept "DATA first" auch so für den Z80 umsetzen kann.

    Meiner Meinung nach ist dabei egal ob ein Code viele SystemAdressen anspringt/liest/schreibt oder nicht.

    Man muss halt nur wissen wo der Code startet.

  • Naja, vollautomatisch wird es wohl auch nicht gehen.

    Ich mein duke hatte so einen Disassembler geschrieben.


    Ich denke aber das man das Konzept "DATA first" auch so für den Z80 umsetzen kann.

    Gut möglich. Das hatte ich ganz zu Anfang auch überlegt, aber verworfen weil ich nicht recht wusste wie ich das Umsetzen soll und mir nicht ganz klar war ob meinen Programmierkenntnisse dafür ausreichen würden.

  • Ach, du schaffst das :)

    Ich mein duke hatte so einen Disassembler geschrieben.

    Bist du sicher? Ich meine da muss man auch noch ein wenig manuell eingreifen. Kann mich aber auch irren.


    Ich werde auch jeden Fall versuchen meinen so vollständig wie möglich zu machen. Daraus kann man nur lernen ;-)

  • Und wie werden die Programmteile gefunden, die nur über Adresstabellen angesprungen werden?

    Wenn man so einen Disassembler über das PET-Basic laufen lässt, würden vermutlich 90% des Codes nicht als Programm erkannt.


    Bei meinem 6502-Disassmbler kann ich einfach die Programm- und Datenbereich über eine Konfigurationsdatei vorgeben.

    Ich mache dann CODE FIRST und schaue dann, an welchen Stelle illegale Opcodes oder Sprünge ins Nirvana stehen. Man sieht dann meist auf einen Blick, wo diese Bereich beginnen und enden. Meistens erkannt man auch gleich, ob es sich um Adressetabellen handelt.


    Das wiederhole ich dann so lange, bis keine illegalen Opcodes und keine illegalen Sprungziele mehr vorhanden sind. Das sieht man sehr schön an der Liste der externen Labels. Das ganze dauert dann vielleicht 30 Minuten bei einem 10K großen Programm, aber dafür habe ich in der Zeit auch schon Teile des Programms analysiert.


    Das einzeige, was der Assembler automatisch versucht, ist die Erkennung von Unterprogrammen und abgeschlossenen Routinen (anhand der Branches). Da werden dann zur Übersicht automatisch Trennlinien eingezogen.


    Ganz übel ist natürlich selbstmodifiziernder Code. Wenn ich sowas sehe, höre ich sofort auf. So ein Mist interessiert mich nicht und wird daher nicht analysiert.

  • Ich schaue halt auf den Opcode. Wenn ich z.B. ein JMP ($5678) finde, versuche ich Code zu analysieren welcher Werte in die Adresse $5678 schreibt.

    Mir ist klar das das alles sehr wage ist.

  • Das PET/C64-Basic macht seine indirekten Sprünge über den Stack. Das habe ich auch in anderen Programmen schon häufiger gesehen. Da muss man sogar noch die Sprungadresse korrigieren, damit sie passt.

    Ich habe auch schon über automatische Erkennungen nachgedacht, aber letztendlich wird es bei vielen Programmen dann doch nicht funktionieren.

    Vielleicht ist es auch beim Z80 einfacher als beim 6502 - keine Ahnung.

  • Ach, du schaffst das :)

    Ich mein duke hatte so einen Disassembler geschrieben.

    Bist du sicher? Ich meine da muss man auch noch ein wenig manuell eingreifen. Kann mich aber auch irren.

    Ich hatte das damals so verstanden. Rick Dangerous to Source

  • Bei meinem 6502-Disassmbler kann ich einfach die Programm- und Datenbereich über eine Konfigurationsdatei vorgeben.

    Also so ähnlich wie bei mir. :)


    Das einzeige, was der Assembler automatisch versucht, ist die Erkennung von Unterprogrammen und abgeschlossenen Routinen (anhand der Branches). Da werden dann zur Übersicht automatisch Trennlinien eingezogen.

    Sowas ähnliches ist bei mir auch (noch nicht ganz fertig) eingebaut.



    Gelb ist Code und die roten Kästchen sind CALL Befehle.

    Später kann man per Klick in den betreffenden Bereich

    hinspringen und sich den Code genauer anschauen.



    Ganz übel ist natürlich selbstmodifiziernder Code. Wenn ich sowas sehe, höre ich sofort auf. So ein Mist interessiert mich nicht und wird daher nicht analysiert.

    Meinen Disassembler habe ich mit sowas noch nicht ausprobiert. Dafür werde ich bei Gelegenheit meinen Z80 Sprite-Dreh-Code zur 6510 Comp testen.

  • Wenn der Dissassembler eine GUI besitzt fände ich es praktischer die zu bearbeitenden Bereiche z.B. mit der Maus zu markieren anstatt das über Config Files zu realisieren.

    Bei CommandLine Tools sehe ich das mit der Config Datei natürlich ein :-).


    Bei einer GUI kann man dann schön sich den CODE links und den HexDump(in dem man Bereiche als DATA oder CODE markiert) rechts im Fenster anzeigen lassen.

  • Ich hatte das damals so verstanden. Rick Dangerous to Source

    Auch da steht etwas von manueller Nachbearbeitung. Allerdings nicht in welcher Form.

  • Wenn der Dissassembler eine GUI besitzt fände ich es praktischer die zu bearbeitenden Bereiche z.B. mit der Maus zu markieren anstatt das über Config Files zu realisieren.

    Inzwischen sind wir hier ziemlich offtopic. Vielleicht könnte das Thema Disassembler mal ein Mod in einen eigene Thread verschieben?


    Also das klappt vermutlich nicht so einfach mit dem Markieren, weil der Anfang der Bereiche oft mitten in einem Mehrbyte-Befehl liegt, solange Daten als Code interpretiert werden. Man muss die Bereiche schon byte-genau angeben können.


    Außerdem will man evtl. die Datenbereiche genauer spezifizieren. Soll da ein DB oder ein DW oder ein DW mit Offset generiert werden. Oder will man den ASCII-Code als Kommentar dazu haben. Bei DW ist noch wichtig, ob Code oder Daten-Labels erzeigt werden sollen.


    So sieht z.B. bei meinem Disassembler die Konfigurationsdatei aus:


  • Jo, kann gerne ausgelagert werden.

    Ich finde das Thema recht spannend. Die verschiedenen Verangehensweisen usw...

  • Vielleicht könnte das Thema Disassembler mal ein Mod in einen eigene Thread verschieben?

    :dafuer: Weil's ein interessantes Thema ist.


    Das mit dem Rücksprung über den Stack wird auch im Action Replay gemacht, bei jedem Menü. Macht das Debuggen und Ändern nicht gerade einfacher.


    Was ich auch häufig mache, ist ein Byte einfügen, um einen BIT-Befehl ($24 oder $2c) als Dummy zu erzeugen, um je nach Verzweigung unterschiedliche Werte zu laden oder zu manipulieren. Man hat also sozusagen Code im Code versteckt und erhält unterschiedlichen Quellcode, je nachdem wo man anfängt zu disassemblieren, wobei bei einer Version eben ein loses Byte als Datum übrigbleibt.


    Ich glaube, der Disassembler bräuchte K.I. oder zumindest eine Menge Expertenwissen, um effektiv zwischen Code und Daten unterscheiden zu können. Fällt uns ja selbst nicht mal immer leicht.

  • Damit magst Du Recht haben. Ich hatte aber auch geschrieben "die mir einen Hinweis geben wo im Code mit Daten zu rechnen ist oder nicht". ;)

    Da mein Disassembler recht dumm ist (siehe hier) ist das für mich eine Möglichkeit den Anwender mit Hinweisen zu versorgen.


    Solange so ein Feature ein-/ausschaltbar ist, ist das kein Problem. :) Ich würde das aber auf keinen Fall als Standard einbauen. Abgesehen davon sind alle Disassembler mehr oder weniger dumm, mir ist zumindest bisher noch keiner über den Weg gelaufen, der 100% brauchbaren Code in allen Fällen erzeugt hat, es waren immer manuelle Anpassungen notwendig, um dem Disassembler etwas auf die Sprünge zu helfen. Und das ist auch völlig OK, schleßlich weiß derjenige, der Code disassembliert (meistens) besser als der Disassembler, was Code und was Daten sind. Zumindest sollte das so sein. :)

  • Damit magst Du Recht haben. Ich hatte aber auch geschrieben "die mir einen Hinweis geben wo im Code mit Daten zu rechnen ist oder nicht". ;)

    Da mein Disassembler recht dumm ist (siehe hier) ist das für mich eine Möglichkeit den Anwender mit Hinweisen zu versorgen.


    Solange so ein Feature ein-/ausschaltbar ist, ist das kein Problem. :)

    Da kann man (bisher) nix ausschalten. Das ist nur eine optische Hilfe.Was Code oder Daten ist muss bei mir links im speziellen Editor angegeben werden.

    Eine Automatikfunktion (auch abschaltbar) wäre später denkbar, wenn ich besser in der Lage bin Z80 Code zu lesen und in VB.net zu programmieren. ;)

    Der Disassembler ist nicht das größte Projekt von mir (das kann man in meiner Galerie sehen), aber bestimmt das komplexiste Projekt.