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 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.