← ST-Computer 04 / 1989

Viren im System?

Programmierpraxis

Seit einiger Zeit (spätestens seit der CeBIT ’88) geistert es nur so von allen möglichen Schlagworten um den Virus. Jeder sucht Schutz für seine Programme und Daten. VIRUS EX hilft - auf einfache Weise. Zuerst einmal eine kurze Einführung. Was versteht man unter Viren? Viren sind in der Regel kleine Programme - damit sie nicht auffallen -, die sich von Diskette zu Diskette oder von Programm zu Programm weiterkopieren.

Das Prinzip ist immer ähnlich. Startet man ein infiziertes Programm, so gelangt der Virus in den Speicher und kann von nun an sein Unwesen treiben. Dieses Unwesen besteht vorerst darin, sich an andere (Wirts-) Programme anzuhängen, bis er sich ausreichend verbreitet hat. Dann aber kann er so richtig loslegen. Die möglichen Auswirkungen reichen von einem netten Gruß zu Weihnachten bis hin zur Formatierung von Disketten. Wann die Viren ihre vielfältige Funktion ausüben, kann man kaum Vorhersagen. Es kann zum Beispiel an einem bestimmten Datum oder auf eine bestimmte Aktion hin geschehen. Die Viren auf den Disketten (sie befinden sich meist im Bootsektor) verbreiten sich ähnlich. Neben diesen beiden Typen von Programmen sind selbstverständlich auch gekoppelte Programme (Bootsektor und Programm oder Track 81 auf der Diskette o.ä.) möglich. Da die Viren aber zu vielfältig sein können, soll hier nicht weiter darauf eingegangen werden.

Wie schützt man sich nun? Ein Schutz als solcher ist schlecht möglich, besser ist die Vorbeugung. Disketten lassen sich relativ einfach untersuchen. Den Bootsektor (Track 0, Sektor 1 auf Seite 0 der Diskette) läßt man sich mit einem Diskettenmonitor anzeigen und betrachtet ihn näher. Erster Hinweis ist eine $60 (hexadezimal) gleich an erster Stelle. Zweiter Hinweis ist die Prüfsumme des Bootsektors. Sie muß $1234 ergeben, sonst ist der Bootsektor auf keinen Fall ausführbar, und der Virus - sofern vorhanden - hat auch keine Chance, sich auszubreiten. Letzter Hinweis ist der Vergleich mit bekannten Viren, beispielsweise ist ein Virus, der die Zeichenfolge BPL enthält, bekannt ... und leider sehr verbreitet. Er soll zwar unschädlich sein, aber man entfernt ihn besser. Dazu müssen die Bytes 34 bis 511 auf 0 gesetzt werden. Die geschilderten Tätigkeiten sind aber auch keine Vorbeugung, sondern eher eine Nachbehandlung.

Viren in Programmen sind schwer zu erkennen. Sicheres Merkmal ist jedoch die Länge eines Programmes. Ist ein Programm plötzlich länger, so ist ein Virus sehr wahrscheinlich! (Nur Programmentwickler müssen aufpassen, denn bei Ihnen kann eine Veränderung an einem Programm auch eine Veränderung der Programmlänge bedeuten.) Da man kaum alle Programmlängen kennen kann, ist es vorteilhaft, die Programmnamen mit Pfad und Länge abzuspeichern und von Zeit zu Zeit zu überprüfen. Und genau das macht VIRUS EX!

Zuvor aber noch ein Wort zu grundsätzlichen Vorbeugungsmaßnahmen, die man treffen kann. Eine schreibgeschützte Diskette kann auch schwerlich von einem Virus befallen werden, also möglichst Schreibschutz aktivieren. Sicherheitskopien können sowieso nicht schaden, also besser mit Kopien arbeiten. Wer über Programmierkenntnisse verfügt, kann von seinen Disketten die ersten 18 logischen Sektoren in einer Datei abspeichern und sie notfalls wieder auf die Diskette schreiben. Damit hat man für den Notfall noch die FAT und das Directory sowie sonstige Informationen der Diskette. Hat man einen Virus entdeckt, so sollte man ihn (notfalls durch Löschen des betreffenden Programmes) entfernen. Aber vorher auf jeden Fall den Rechner einmal abschalten (Reset allein hilft nicht immer!)! Mehr soll hier nicht berichtet werden, nur noch ein kleiner Hinweis: Betrachten Sie Viren bitte nicht als Scherz! Viren sind eine echte Gefahr! Und besonders Harddisk-Benutzer sollten vorsichtig sein!

Bevor es etwas programmtechnisch wird, die Hinweise zur Benutzung von VIRUS EX. VIRUS EX ist ein Accessory. Kopieren Sie es auf das Bootlaufwerk (Diskette oder Partition C). Diskettenbenutzer sollten sich vor allem eine virenfreie Bootdiskette verschaffen. Möchten Sie die Programmdaten in eine Datei aufnehmen, muß das gewünschte Laufwerk eingestellt und “aufnehmen” angeklickt werden. Es erscheint ein File-Selector. Dort wird die Datei eingetragen, unter deren Namen die Datei angelegt werden soll. Sie sollte sich auf jeden Fall an einem Ort befinden, wo sie von Viren nicht angetastet werden kann. Möchte man eine Diskette oder Partition überprüfen, so stellt man ebenso erst das Laufwerk ein und wählt dann “überprüfen”, worauf wieder ein File-Selector erscheint. Dort wählt man nun die zur Diskette oder Partition passende Datei aus. Fehlt mittlerweile ein Programm, gibt es während der Untersuchung eine entsprechende Meldung, hat sich die Programmlänge geändert, erscheint eine Alertbox, bei der man wählen kann, ob das Programm erhalten bleiben oder gelöscht werden soll. Neu hinzugekommene Programme werden nicht geprüft. Sind neue Programme hinzugekommen, prüft man erst die alten und nimmt dann die neuen Daten auf.

Aufgenommen werden alle Programme, die auf ACC, PRG, TOS oder TTP enden. Accessories mitaufzunehmen, scheint nicht sinnvoll zu sein, aber es gibt einige wenige Programme, die sich durch einfache Umbenennung von PRG in ACC und umgekehrt auch als Programm nutzen lassen.

Wenden wir uns jetzt dem Programm VIRUS EX selbst zu. Es wurde in Modula 2 (Megamax) geschrieben. Bevor das Listing eingegeben wird, muß eine Resourcedatei mit einem Resource-Construction-Programm angelegt werden. Eine Dialogbox und vier Strings für Alertboxen sind analog zu den auf den Bildern zu sehenden Mustern zu erstellen. Die Erklärungen zum Listing kann man dem Listing selbst entnehmen.

Ist das Listing eingetippt und das Resourcefile angelegt, werden die drei Quelldateien (DEFINITION- und IMPLEMENTATION-Modul für das Resourcefile und Programm-Modul) übersetzt. Fertig!

Wer Megamax Modula 2 nicht sein eigen nennen kann, muß eine Übersetzung in eine andere Programmiersprache vornehmen. Fast alle Strukturen lassen sich leicht nachbilden. Aber trotzdem ein paar Hinweise zu DirQuery. DirQuery (in Prozedur WritePrgFileSub) ruft die übergebene Routine (hier WriteProgramName) solange mit einer bestimmten Maske (hier LokalName) auf, bis keine passenden Files mehr zu finden sind. Bei vielen anderen Programmiersprachen (oder besser Entwicklungspaketen) muß eine entsprechende Routine erst aufgebaut werden. Dazu benutzt man Fsetdta (Gemdos 26), um die Diskettentransferadresse zu setzen. Fsfirst (Gemdos 78), um die erste Datei zu suchen, und Fsnext (Gemdos 79), um nachfolgende Dateien zu ermitteln.

Schließlich ist ein zweites Listing - in GFA-BASIC - aufgeführt, welches lediglich eine Aufgabe hat. Das Programm soll eine Datei, die von VIRUS EX geschrieben wurde, auswerten. Die Programmdaten werden nacheinander auf dem Bildschirm ausgegeben.

IMPLEMENTATION MODULE Virus_ex; (*$N+,M-*) END Virus_ex.

Listing 1

DEFINITION MODULE Virus_ex; EXPORT Userdial, Dec, Cdrive, Inc, Instal, Check, Abbruch, Virus, Missprg, Baddrive, Diskerr; CONST Userdial = 0; (* Formular/Dialog *) Dec = 3; (* BOXTEXT in Baum USERDIAL *) Cdrive = 4; (* BOXTEXT in Baum USERDIAL *) Inc = 5; (* BOXTEXT in Baum USERDIAL *) Instal = 6; (* BUTTON in Baum USERDIAL *) Check = 7; (* BUTTON in Baum USERDIAL *) Abbruch = 8; (* BUTTON in Baum USERDIAL *) Virus = 0; (* Meldung-String Index *) Missprg = 1; (* Meldung-String Index *) Baddrive = 2; (* Meldung-String Index *) Diskerr = 3; (* Meldung-String Index *) END Virus_ex.

Listing 2

(**************************************************) (* VIRUS EX-Ein Programm zur Überprüfung der Länge*) (* von Programmen - V. 1.00 *) (* ===============================================*) (* Sollte sich ein (beliebiger) Virus an ein *) (* Programm anhängen, so kann dies von VIRUS EX *) (* entdeckt werden. Es werden alle Programme mit *) (* den Endungen ACC, PRG, TOS und TTP überprüft. *) (* ===============================================*) (* Megamax Modula 2 ,Appl. Syst. /// Heidelberg. *) (* ===============================================*) (* Autor: Dietmar Rabich, Dövelingsweg 2, *) (* D-4408 Dülmen / 20. Juni 1988 *) (**************************************************) MODULE VirusEx; (*$R-,M-,Q+,N-,V-,P-,S-*) (* Importe *) IMPORT Virus_ex; (* Resource-File *) FROM Files IMPORT Create,Open,Close,Access,File,State,ResetState, ReplaceMode; FROM Binary IMPORT FileSize,ReadBytes,WriteBytes; FROM Strings IMPORT Append,Assign,String,Pos,Empty,StrEqual,Length; FROM Directory IMPORT FileAttr,FileAttrSet,DirEntry,Drive,DriveSet, DriveStr,DirQuery,StrToDrive,DrivesOnline,Delete, SplitPath,SplitName,DriveToStr,DefaultDrive; FROM Convert IMPORT ConvInt; FROM SYSTEM IMPORT ADR; FROM GrafBase IMPORT Rect,Rectangle,TransRect; FROM GEMEnv IMPORT InitGem,RC,DeviceHandle,GemError,SetCurrGemHandle; FROM GEMGlobals IMPORT PtrObjTree,PtrMaxStr,Root,OStateSet,MaxDepth,ObjState FROM AESEvents IMPORT MessageEvent,MessageBuffer,ccOpen,TimerEvent; FROM AESForms IMPORT FormAlert,FormDial,FormDialMode,FormCenter,FormDo; FROM AESGraphics IMPORT GrafMouse,MouseForm; FROM AESMenus IMPORT RegisterAcc; FROM AESMisc IMPORT SelectFile; FROM AESObjects IMPORT ChangeObjState,DrawObject,ObjectOffset; FROM AESResources IMPORT LoadResource,ResourceAddr,ResourcePart; FROM AESWindows IMPORT UpdateWindow; FROM ObjHandler IMPORT ObjectState,ObjectSpace,SetCurrObjTree,AssignTextStrings,SetPtrChoice; (* Konstanten *) CONST MaximalDrive=15; (* höchstens 16 Laufwerke! *) Leer =' '; NoResource ='[0][VIRUS EX verzweifelt.|Resourcefile fehlt!][Wo ist es denn?]'; Nolnstal ='[0][VIRUS EXverzweifelt.|Installation unmöglich!][Schade.]'; (* Typen *) TYPE ProgramID = RECORD Path : ARRAY [0..63] OF CHAR; (* Pfad des Programms *) Name : ARRAY [0..11] OF CHAR; (* Name des Programms *) Numb : LONGCARD (* Filegröße *) END; RWProc = PROCEDURE(ARRAY OFCHAR,CARDINAL, VAR INTEGER); (* Variablen *) VAR DriveCount,PositionVirF,PositionMiss,Position, VoidC :CARDINAL; UserBox :PtrObjTree; VirusFoundAlert,MissingProgAlert,BadDriveAlert, DiskErrorAlert :PtrMaxStr; dev :DeviceHandle; InstalPath,InstalName,CheckPath,CheckName, AccName :String; VirFile :File; PrgID :ProgramID; Message :MessageBuffer; (* Initialisierung des Programms *) PROCEDURE InitVirusEx () : BOOLEAN; VAR success,AccOK;BOOLEAN; BEGIN InitGem(RC,dev,success); (* Initialisierung*) IF ~success THEN RETURN FALSE END; LoadResource('VIRUS_EX.RSC'); (* RSC-File laden*) IF GemError() THEN FormAlert(1,NoResource,VoidC); RETURN FALSE END; UserBox :=ResourceAddr(treeRsrc,Userdial); (* RSC-File auswerten*) VirusFoundAlert :=ResourceAddr(textstring,Virus); MissingProgAlert:=ResourceAddr(textstring,Missprg); BadDriveAlert :=ResourceAddr(textstring,Baddrive); DiskErrorAlert :=ResourceAddr(textstring,Diskerr); DriveCount :=0; (* Zähler auf 0 *) PositionMiss :=Pos('%prg',MissingProgAlert^,0); (* Position für *) PositionVirF :=Pos('%prg',VirusFoundAlert^,0); (* variablen Namen *) Position :=Pos('%nnn',DiskErrorAlert^,0); AccName :=' Virus Ex'; (* Accessory *) RegisterAcc(ADR(AccName),VoidC,AccOK); IF ~AccOK THEN RETURN FALSE END; InstalName:=''; (* Texte für *) InstalPath:=DriveToStr(DefaultDrive()); (* File-Selectoren *) Append('\*.VIR',InstalPath,success); CheckName :=''; CheckPath :=InstalPath; RETURN TRUE (* Alles OK! *) END InitVirusEx; (* Ausgabe TOS-Fehlermeldung *) PROCEDURE DiskError (errCode:INTEGER); VAR ErrNumber:String; i :CARDINAL; BEGIN IF errCode#0 THEN (* Fehler aufgetreten *) ConvInt(errCode,4,ErrNumber); (* Zahl->String*) FOR i:=Position TO Position+3 DO (* Zahl in Alertstring *) DiskErrorAlert^[i]:=ErrNumber[i-Position] END; FormAlert(1,DiskErrorAlert^,VoidC) (* Ausgabe Alertbox *) END END DiskError; (* Ausgabe, wenn Programm fehlt *) PROCEDURE MissProgError (errName:ARRAY OF CHAR); VAR i :CARDINAL; LokalName :String; success :BOOLEAN; BEGIN Assign(errName,LokalName,success); Append(Leer,LokalName,success); FOR i:=PositionMiss TO PositionMiss+11 DO MissingProgAlert^[i]:=LokalName[i-PositionMiss] (* Name einsetzen *) END; FormAlert(1,MissingProgAlert^,VoidC) (* Ausgabe Alertbox *) END MissProgError; (* Ausgabe, wenn Filegrößen unterschiedlich (Merkmal für Virus) *) PROCEDURE VirusError (errName:ARRAY OF CHAR) : BOOLEAN; VAR i :CARDINAL; LokalName :String; success :BOOLEAN; BEGIN Assign(errName,LokalName,success); Append(Leer,LokalName,success); FOR i:=PositionVirF TO PositionVirF+11 DO VirusFoundAlert^[i]:=LokalName[i-PositionVirF] (* Name einsetzen *) END; FormAlert(1,VirusFoundAlert^,VoidC); (* Ausgabe Alertbox *) RETURN VoidC=2 (* TRUE=Programm löschen *) END VirusError; (* Fehlerstatus ermitteln *) PROCEDURE errState (f:File;VAR IOResult:INTEGER) : BOOLEAN; BEGIN IOResult:=State(f); IF IOResult<0 THEN (* Fehler aufgetreten? *) ResetState(f); (* Status zurücksetzen *) RETURN TRUE END; RETURN FALSE END errState; (* Feststellen, ob Laufwerk N (A=0,B=1,...) angemeldet ist *) PROCEDURE DriveOnline (N:CARDINAL) : BOOLEAN; VAR DRV :Drive; DriveString :DriveStr; DRVSet :DriveSet; BEGIN DriveString :='A:'; DriveString[0] :=CHR(ORD('A')+N); DRV :=StrToDrive(DriveString); DRVSet :=DrivesOnline(); (* Angemeldete Laufwerke ermitteln *) RETURN DRV IN DRVSet (* TRUE=Laufwerk angemeldet *) END DriveOnline; FORWARD WritePrgFileSub (name : ARRAY OF CHAR;VAR IOresult:INTEGER); (* Programmdaten in File schreiben oder Unterverzeichnis untersuchen *) PROCEDURE WriteProgramName (path:ARRAY OF CHAR;entry:DirEntry) : BOOLEAN; VAR LokalName,NAME,EXTENSION:String; Acc,Prg,Tos,Ttp :ARRAY [0..3] OF CHAR result :INTEGER; success :BOOLEAN; BEGIN Assign(path,LokalName,success); IF subdirAttr IN entry.attr THEN (* falls Subdirectory *) IF entry.name[0]#'.' THEN Append(entry.name,LokalName,success); WritePrgFileSub(LokalName,result); (* Subdirectory untersuchen *) END ELSE (* falls normale Datei *) SplitName(entry.name,NAME,EXTENSION); Acc:='ACC'; Prg:='PRG'; Tos:='TOS'; Ttp:='TTP'; IF StrEqual(EXTENSION,Acc) OR StrEqual (EXTENSION,Prg) OR StrEqual(EXTENSION,Tos) OR StrEqual (EXTENSION,Ttp) THEN Assign(path,PrgID.Path,success); Assign(entry.name,PrgID.Name,success); PrgID.Numb:=entry.size; WriteBytes(VirFile,ADR(PrgID),SIZE(PrgID)) (* speichern wenn Extension OK *) END END; RETURN TRUE END WriteProgramName; (* untersucht alle Files mit Pfadname *) PROCEDURE WritePrgFileSub (name:ARRAY OF CHAR;VAR IOresult:INTEGER); VAR LokalName:String; success :BOOLEAN; BEGIN Assign(name,LokalName,success); Append('\*.*',LokalName,success); DirQuery(LokalName,FileAttrSet{subdirAttr),WriteProgramName,IOresult) END WritePrgFileSub; (* erstellt File mit Programmdaten *) PROCEDURE WritePrgFile (name:ARRAY OF CHAR;N:CARDINAL;VAR IOresult:INTEGER); VAR DRV :Drive; DriveString :DriveStr; SearchName :String; success :BOOLEAN; BEGIN Create(VirFile,name,writeOnly,replaceOld); DriveString :='A:'; DriveString[0]:=CHR(ORD('A')+N); WritePrgFileSub(DriveString,IOresult); Close(VirFile) END WritePrgFile; (* Programmfile auswerten *) PROCEDURE ReadPrgFile (name:ARRAY OF CHAR;N:CARDINAL;VAR IOresult:INTEGER); VAR Cnt :LONGCARD; PrgFile :File; IOres :INTEGER; PrgName :String; success :BOOLEAN; BEGIN Open(VirFile,name,readonly); (* Datei mit Prog.daten *) IF errState(VirFile,IOresult) THEN Close(VirFile) ELSE LOOP ReadBytes(VirFile,ADR(PrgID),SIZE(PrgID),Cnt); (* Daten lesen *) IF Cnt=0L THEN EXIT END; Assign(PrgID.Path,PrgName,success); Append(PrgID.Name,PrgName,success); PrgName[0]:=CHR(ORD('A')+N); Open(PrgFile,PrgName,readonly); (* Daten auswerten *) IF errState(PrgFile,IOres) THEN Close(PrgFile); IF IOres=-33 THEN (* Programm fehlt *) MissProgError(PrgID.Name) ELSE DiskError(IOres) END ELSE IF FileSize(PrgFile)#PrgID.Numb THEN (* Filegrößen unterschiedlich, Virus? *) IF VirusError(PrgID.Name) THEN Delete(PrgName,IOres); (* Programm löschen *) DiskError(IOres) END END; Close(PrgFile) END END; Close(VirFile) END END ReadPrgFile; (* vergleicht Programmdaten mit abgespeicherten Werten bzw. *) (* steuert Abspeicherung der Programmdaten von Laufwerk Number in Datei P *) PROCEDURE Do (ReadWriteProcedure:RWProc; VAR Path,Name:ARRAY OF CHAR; Number:CARDINAL); VAR result :INTEGER; P,VoidStr :String; OKButn,success :BOOLEAN; BEGIN IF DriveOnline(Number) THEN (* Laufwerk angemeldet *) SelectFile(Path,Name,OKButn); (* File-Selector *) IF OKButn AND ~Empty(Name) THEN GrafMouse(bee,NIL); (* Biene darstellen *) SplitPath(Path,P,VoidStr); (* Namen zusammensuchen *) Append(Name,P,success); ReadWriteProcedure(P,Number,result); (* Datei schreiben bzw. auswerten *) GrafMouse(arrow,NIL); (* wieder Mauspfeil *) DiskError(result) END ELSE FormAlert(1,BadDriveAlert^,VoidC) END END Do; (* Dialog mit dem User durchführen *) PROCEDURE DoDialog; VAR but :CARDINAL; state :OStateSet; space :Rectangle; BEGIN UpdateWindow(TRUE); GrafMouse(arrow,NIL); (* Mauszeiger als Pfeil *) LOOP space:=FormCenter(UserBox); FormDial(reserveForm,Rect(0,0,5,5),space); FormDial(growForm,Rect(0,0,5,5),space); DrawObject(UserBox,Root,MaxDepth,space); (* Dialogbox ausgeben *) LOOP FormDo(UserBox,Root,but); (* Dialog durchführen *) state:=ObjectState(but); EXCL(state,selectObj); ChangeObjState(UserBox,but, (* selectObj zurück *) TransRect(ObjectSpace(but),ObjectOffset(UserBox,but)), state,TRUE); CASE but OF Dec : IF DriveCount=0 THEN (* "<<" angeklickt *) DriveCount:=MaximalDrive ELSE DEC(DriveCount) END Inc : IF DriveCount=MaximalDrive THEN (* ">>" angeklickt *) DriveCount:=0 ELSE INC(DriveCount) END END; IF (but=Dec) OR (but=Inc) THEN SetCurrObjTree(UserBox,FALSE); AssignTextStrings(Cdrive,setOnly,CHR(ORD('A')+DriveCount),noChange,'', noChange,''); DrawObject(UserBox,Cdrive,MaxDepth,FormCenter(UserBox)) (* Laufwerk neu *) ELSE EXIT END END; FormDial(freeForm,Rect(0,0,5,5),space); FormDial(shrinkForm,Rect(0,0,5,5),space); CASE but OF Instal : Do(WritePrgFile,InstalPath,InstalName,DriveCount)| (* "aufnehmen"-Button / = DoDriveInstal *) Check : Do(ReadPrgFile, CheckPath, CheckName, DriveCount)| (* "überprüfen"-Button / = DoDriveCheck *) Abbruch: EXIT (* "Abbruch"-Button *) END END; UpdateWindow(FALSE) END DoDialog; BEGIN IF InitVirusEx() THEN REPEAT (* Endlosschleife *) MessageEvent(Message); IF Message.msgType=accOpen THEN DoDialog (* unser Accessory wurde ausgewählt! *) END UNTIL FALSE ELSE FormAlert(1,NoInstal,VoidC); REPEAT (* auch 'ne Endlosschleife, damit VIRUS_EX nicht *) TimerEvent(10000L) (* abstürzt, wenn mal das Resourcefile fehlt.*) UNTIL FALSE END END VirusEx. (* Ende *)

VIRUS EX

' GfA-Basic-Programm zur Anzeige der Programm- ' namen, die mit VIRUS EX abgespeichert wurden. ' (c) D. Rabich, Dülmen ' 20. Juni 1988 CLS path$="\*.VIR" name$="" DO FILESELECT path$,name$,ret$ ! File-Selector zur Auswahl EXIT IF ret$="" page_count=0 i=LEN(ret$) ! Pfad für nächsten Aufruf retten REPEAT IF MID$(ret$,i,1)="\" path$=LEFT$(ret$, i) ENDIF DEC i UNTIL MID$(ret$,i+1,1)="\" OR i<l1 CLS OPEN "I",#1,ret$ ! Datei öffnen WHILE NOT (EOF(#1)) p$=INPUT$(64,#1) ! Pfad lesen pos_end=INSTR(p$,CHR$(0)) IF pos_end>0 THEN p$=LEFT$(p$,pos_end-1) ENDIF n$=INPUT$(12,#1) ! Name lesen pos_end=INSTR(n$,CHR$(0)) IF pos_end>0 THEN n$=LEFT$(n$,pos_end-1) ENDIF l=CVL(INPUT$(4,#1)) ! Programmlänge lesen PRINT LEFT$(p$+n$+SPACE$(70),70);l ! Ausgabe INC page_count IF page_count=23 THEN ! ggf. auf Tastendruck warten PRINT "- mehr -" REPEAT antw$=INKEY$ UNTIL antw$<>"" page_count=0 ENDIF WEND CLOSE #1 PRINT "- Ende —" ! auf Tastendruck warten REPEAT antw$=INKEY$ UNTIL antw$<>"" LOOP END ! Ende !

GFA-BASIC-Programm zur Anzeige der Programmnamen

Dietmar Rabich