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