TURBO PASCAL HOWTO TABLE OF CONTENTS 1.Vorwort 2.Grundlagen der Programmierung in Turbo Pascal 2.1 Aufbau eines TP Programmes 2.1.1 Der Deklarationsteil 2.1.2 Der Programmteil 2.2 Datentypen 2.3 Die wichtigsten TurboPascal Kommandos 2.4 Beispielprogramm 3. Erweitertes Programmieren in Turbo Pascal 3.1 Systemnahe Programmierung 3.2 Speicherresidente Programme 4. Möglichkeiten von Turbo Pascal 5. Last Words Vorwort Pascal ist zweifellos die beliebteste Programmiersprache. Diese Beliebtheit verdankt es vor allem dem äußerst schnellen Compiler, mit dem sich effizienter Code in kurzer Zeit entwickeln läßt. Ein Plus stellt auch die Unkompliziertheit dar. Einen großen Teil dieses Erfolges ist Borland mit seinem Dialekt TurboPascal zu verdanken. Dieses bietet im Gegensatz zu alten Implementierungen ( man erinnere sich an USCD auf dem Apple) einige Features, die die Entwicklung professioneller Software ermöglichen. Dieser Text ist in zwei Teile gespaltet. Der erste stellt die Basis für das programmieren in TP dar. Es werden kaum Grundkenntnisse erwartet. Im zweiten Teil möchte ich die Möglichkeiten der Programmentwicklung aufzeigen. Dieses Kapitel ist für einen Leser ohne Grundwissen nicht geeignet. Es werden die Kenntnis über das Hexdezimalsystem sowie das Unterbrechungskonzepts eines PCs vorausgesetzt. Er ist eine kurze Einführung in die systemnahe Programm- ierung sowie in das Erstellen von TSRs. GRUNDLAGEN DER PROGRAMMIERUNG IN TURBO PASCAL DER AUFBAU EINES TP PROGRAMMES TurboPascal ist auch als eine sog. strukturierte Sprache bekannt. Jedes TP Programm ist nach dem gleichen Schema aufgebaut. Es besteht aus dem Deklarationsteil und dem Programmtext. DER DEKLARATIONSTEIL Im Deklarationsteil werden die Werte festgelegt, die später vom Programm- code verwendet werden. Er enthält folgende Elemente: - Variablen Variablen haben die Eigenschaft das sie zwar deklariert werden, jedoch keine bestimmte Wertsetzung erhalten. Sie sind veränderlich. Die Zuweisung erfolgt so: var NameDerVariable:Datentyp; Bsp.: var wert1:integer; Genauer unterscheidet man zwischen lokalen und globalen Variablen. Wie der Name schon sagt werden die globalen im gesamten Programm verwendet, die lokalen nur in einem bestimmten Teil z.B. einer Funktion oder Prozess. - Konstanten Im Grunde sind Konstanten Variablen, die einen festen Wert mitbringen. Während des Programmablaufs können sie nicht verändert werden. Die Deklaration sieht wie folgt aus: const NameDerKonstante = WertDerKonstante Bsp.: const byte=17 - Prozesse Um den Programmcode möglichst übersichtlich und so auch verständlich zu hal- ten, werden Prozesse verwendet. Oft benutzte Programmteile werden zusammen- gefaßt. Der Aufbau dieser Prozesse ist im Grunde wie der Eines gesamten Pascalprogramms. Der Grundriß ist: procedure NameDerProzedur Deklarationsteil Programmcode Die Prozedur wird genau wie der Hauptcode nach der Reihe nach abgearbeitet. Aus dem Hauptquellcode heraus geschieht der Aufruf der Prozedur wie folgt. 1. Übergabe von Variablen (falls nötig) 2. Aufruf der Prozedur 3. Ausführung der Prozedur 4. Fortführung des Hauptprogramms - Funktion Eine Funktion ist gut mit einer Prozedur zu vergleichen. Der einzige Unter- schied ist, das eine Funktion ein bestimmtes Ergebnis ergibt . function NameDerFunktion DER PROGRAMMCODE Der Programmteil wird von der Begin...end. Schleife eingeklammert begin Quellcode end. Hier werden die im Deklarationsteil festgelegten Werte verwendet. Eine Besonderheit von Pascal ist das Fehlen des Goto-Befehls. Ein Basic Programm kann durch die vielen Sprünge im Quellcode sehr schnell unüber- sichtlich werden. Der Code von Pascal wird hingegen nach der Reihe ausgeführt ohne Sprünge im Quelltext zu machen. DATENTYPEN - byte Byte stellt den einfachsten Datentyp dar. Er belegt im Speicher genau ein Byte, also 7 Bit dar . - char Ähnlich der Byte-Variable wird char ebenfalls durch ein Byte im Speicher prä- sentiert. Anstelle des direkten Wertes wird der ASCII-Code des Zeichens angegeben. - Boolean Boolean belegen ebenfalls ein Byte. Sie können entweder den Wert 0 (false) oder 1 (true) haben. - word Word-Variablen enthalten ein Wort zu ihrer Speicherung. Diese kann 0 bis 65535 Stellen besitzen. So lassen sich words in genau zwei Bytes unter- bringen. Intern wird die Variable nochmals in ein Low-Byte und ein High-Byte unterteilt. Das LowByte enthält den niederwertigen, das HighByte den höher- wertigen Teil des Wortes. - shortinits Shortinitvariablen belegen ebenfalls ein Byte im Speicher, haben jedoch den Vorteil das nicht nur positive Zahlen sondern auch negative Zahlen dar- gestellt werden können. Da das Vorzeichen 1 Bit benötigt, kommen dem Betrag ein Bit weniger zu. - integer Integer werden in einem Wort gespeichert. Das höchste Bit des Wortes stellt das Vorzeichen dar. Wie bei word wird es in Low und HighByte unterteilt. - longinit Longinit wird in 32 Bits, also vier Bytes codiert. Das höchste Bit im höchsten Byte nimmt das Vorzeichen ein. - real Das interne Format der reals ist etwas komplizierter. Deshalb werde ich jetzt nicht besonders darauf eingehen. Als real-Variable können alle reelen Zahlen gespeichert werden. Es werden insgesamt 6 Bytes zur Verfügung gestellt. - single Single-Variablen haben die gleichen Eigenschaften wie real. Für sie sind jedoch nur 4 Bytes vorgesehen. - double Folglich sind für den Datentyp double 8 Byte vorgesehen. - extended Extended ist die Erweiterung von double und hat 10 Bytes für sich reserviert. - Arrays Array werden als Datenfelder ohne Löcher angelegt. Jedes Feld enthält die Anzahl von Bytes, die durch den Datentyp deklariert ist. Beispiel: feld: array(1..50) of integer; - Records Records werden ebenfalls in einem Block ohne Lücken gespeichert. Jeder einzelne Datentyp enthält also den ihm deklarierten Speicher. Satz = record of anzahl:integer; zeichen: char; end; Es werden folglich 3 Bytes im Speicher belegt. Die Integer Variable belegt 2 Bytes, das letzte wird vom char besetzt. WICHTIGSTEN TURBO PASCAL KOMMANDOS (Komplett aus dem TP Index übernommen!) - begin...end Konstrukt Dieses Konstrukt wird als Verbundanweisung bezeichnet Syntax: begin Anweisung Anweisung ... Anweisung end. Der Compiler betrachtet die mit begin und end eingefaßten Anweisungen als einen einzigen Block - if...then In diesem Konstrukt werden Befehle abhängig vom Wert des (boolschen) Ausdrucks ausgeführt: Wenn der Ausdruck True ergibt wird die auf then folgende Anweisung ausgeführt; ergibt der Ausdruck false so wird im ersten Fall über- haupt nichts ausgeführt, im zweiten Fall die auf else folgende Anweisung. - Write - Bei typisierten Dateien: Schreibt die über v1, v2, ... vn angegebenen Daten in die durch f bezeichnete Datei. v1, v2, ..., vn müssen denselben Typ wie die Komponenten der Datei haben. - Bei Textdateien: Schreibt die durch v1, v2, ... vn angegebenen Werte in die durch f bezeichnete Textdatei. Wenn f nicht angegeben ist, schreibt write in die Standartausgabe. - Writeln Ruft Write für Textdateien auf und schreibt dann einen Zeilenvorschub in die durch f angegebene Datei. Syntax: WriteLn([var f: text;] v1 [,v2, ... ,vn]); - Read Bei typisierten Dateien: Liest eine oder mehrere Komponenten aus der durch f angegebenen typisierten Datei in die als v1,v2, ... vn übergebenen Variablen Bei Textdateien: Liest einen oder mehrere Werte von der als f angegebenen Textdatei in die als v1, v2, ... vn übergebenen Variablen. - Readln Führt einen Aufruf von Read auf und springt dann zum Anfang der nächsten Zeile innerhalb der über f angegebenen Datei. Syntax: Readln ([var f: text;] v1 [,v2, ..., vn]); - GotoXY Setzt die Position des Cursors im Textbildschirm bzw. innerhalb des momentanen Textfensters Syntax: GotoXY (x, Y: Byte) Unit Crt: - Textcolor Legt die Zeichenfarbe der Textausgabe fest. Syntax: Textcolor(Color: Byte) Unit Crt: Farbkonstanten dunkle Farben (Vordergrund & Hintergrund) Black 0 Blue 1 Green 2 Cyan 3 Red 4 Magenta 5 Brown 6 LightGrey 7 helle Farben (nur Vordergrund) DarkGrey 8 LightBlue 9 LightGreen10 LightCyan 11 LightRed 12 LightMagen13 Yellow 14 White 15 - uses Jeder Bezeichner einer mit uses eingeleiteten Liste steht für den Namen einer Unit, die vom "momentanen" Modul importiert wird. Syntax: uses Bezeichner,... Bzeichner; Der Compiler sucht auf diese Weise angegebene Units zuerst in TURBO.TPL - falls sich dort nichts befindet, wird die Unit auf der Diskette gesucht (wobei sich der Dateiname aus dem Unitnamen und dem Suffix .TPU ergibt). Die Suche auf der Diskette beginnt grundsätzlich mit dem momentanen Standart-Directory und bezieht die via O/D/unit angegebenen Directories. - ClrScr Löscht den Bildschirm bzw. das Textfenster durch überschreiben mit der momentan gesetzten Hintergrundfarbe und setzt den Curser in die obere linke Ecke. Syntax: ClrScr; BBEISPIELPROGRAMM Ich habe ein Programm getippt, das die Verschlüsselung eines kleinen Textes mit einem leichten (na ja, sehr leichten) Algorithmus ermöglicht. ###############################BEGINQUELLCODE############################## Program Algorithm; uses crt; var k:integer; z,d:char; procedure encrypt; Begin clrscr; TextColor(green+blink); writeln ('ENCRYPTION'); TextColor(white); write('Schrittweite:'); read(k); writeln; write('Text:'); repeat read(z) if (z>='A') and (z<='Z') then z:=chr((ord(z) - ord('A')+k) mod 26 + ord('A')); write(z); until eoln; readln; end; procedure intro; Begin clrscr; GOTOXY(30,1); Textcolor(green); writeln('WEAK ALGORITHM'); GOTOXY(30,2); writeln('by XeroX 30.11.99'); TextColor(white); GOTOXY(1,20); Write ('Press (e) for encryption:'); read(d); if (d='e') or (d='E') then encrypt; readln; end; begin intro; end. ################################ENDQUELLCODE########################### Der Algorithmus ist eine Art ROT13 mit dem Unterschied das die Schrittweite nicht konstant 13 beträgt sondern variieren kann. Als Erklärung: Wenn die Schrittweite beispielsweise 13 beträgt wird jeder Buchstabe um 13 Stellen im Alphabet nach vorne versetzt. Aus 'ONLY GOD CAN JUDGE US' wird so 'BAYL TBQ PNA WHQTR HF'. Zu Beachten ist das nur Großbuchstaben verschlüsselt werden können. Zahlen und Sonderzeichen werden nicht verändert. Aus '2PAC' wird also '2CNP'. Ich habe diesen Code extra so knapp wie möglich gehalten. Es besteht die Möglichkeit ihn als gute Übung selbst noch zu erweitern. Wie wär's wenn du die Möglichkeit der Entschlüsselung gibst? Sollte man nicht anstatt nur Großbuchstaben benutzen zu können, auch kleine verschlüsseln können? Schaffst du es mehr als nur eineinhalb Textzeilen auf einmal zu verschlüsseln? Es gibt noch viele andere Ansatzpunkte um das Programm zu "verschönern"! Hab Fantasie! ERWEITERTES PROGRAMMIEREN IN TURBO PASCAL SYSTEMNAHES PROGRAMMIEREN Mit TP Um mit TurboPascal systemnah Programmieren zu können ist, wie in der Einleitung schon erwähnt die Kenntnis über das Unterbrechungskonzept eines PCs nötig Ein Teil der systemnahen Programmierung ist der direkte Zugriff auf die Interrupts. Das kann einerseits durch das TP-Kommando "msdos" sowie "intr" gemacht werden. Der Unterschied der beiden Kommando ist folgender. Mit "msdos" können nur Interrupts über 21h aufgerufen werden (DosInterrupts). intr spricht alle an. Der Aufruf der Unterbrecher kann nur über die Register des Prozessors erfol- gen. Dazu wird der Datentyp "registers" benötigt. Dieser ist jedoch nur in TP 6.0 enthalten. In niedrigeren Versionen muß er wie folgt deklariert werden. type registers = record case boolean of true: (ax,bx,cx,dx,bp,si,di,ds,es,flags:integer); false: (al,ah,bl,bh,cl,ch,dl,dh: byte); end; Jetzt ist jedes Register einzeln adressierbar. Soll beispielsweise das Register ah angesprochen werden kann man reg.ah verwenden. Häufig wird die Segment sowie die Offset-Adresse benötigt. Diese kann durch die Kommandos "ofs" und "seg" ermittelt werden. Es folgt ein Beispiel der direkten Systemprogrammierung. Wir ver- suchen über einen Interrupt den Text " JuRAsSic 5IvE" aus- zugeben. Dazu wird die Funktion 09h von Interrupt 21h benutzt. Die Adresse des Strings wird in DS:DX erwartet, wobei DS die Segmentadresse und DX die Offsetadresse darstellt. Am Ende des Strings muß ein Dollar- zeichen stehen, um das Ende zu markieren. #################################BeginnQuellcode############################## Program Interrupts; uses Crt, DOS; const msg : String = 'JuRAsSic 5IvE'; var cpu : Registers; begin clrscr; cpu.ah := 9; {ruft Funktion 09h von Interrupt 21h auf} cpu.ds := Seg(msg); {Die DS:DX Adresse wird gesetzt} cpu.dx := Ofs(msg)+1; MsDos(cpu); readln; {Der Text soll gesehen werden, nicht sofort wieder verschwinden} clrscr; GotoXY(25,20) writeln ('und nun das ganze über intr') {Der ganze Vorgang ohne Interrupts} cpu.ah := 9; cpu.ds := Seg(msg); cpu.dx := Ofs(msg)+1; Intr($21,cpu); [{Aufruf des Interrupts 21h mit funktion cpu} readln; clrscr; TextColor(green+blink); {grüne Schrift die blinkt} writeln ('jo!!!'); readln; end. ##############################ENDE QUELLTEXT################################ Achtung: Ihr werdet euch über die Bezeichnung ofs(str)+1 wundern. Es wird deshalb verwendet, da das Längenbyte des Strings übergangen werden muß. Man hätte genauso auch ofs(msg[1]) schreiben können. Die Intr Funktion arbeitet ähnlich wie die msdos Funktion. Es können jedoch alle Interrupts aufgerufen werden. Im Schema sieht es folgendermaßen aus: intr (interruptNummer) SPEICHERRESISDENTE PROGRAMME Turbo Pascal gibt die Möglichkeit speicherresidente Programme (TSR Programme- terminate and stay resistant) zu erstellen. Diese sind für Anwendungen wie Keylogger oder (mit erweiterten TP Programmierkenntnissen) Trojaner notwendig. Wie der Name TSR schon sagt ist es nötig, ein Programm zu erstellen, das nach dem Beenden immer noch im Speicher bleibt. Um die dazu Notwendigen Schritte zu verstehen ist die interne Arbeitsweise von DOS wichtig. DOS ist nicht, wie etwa UNIX oder Win95, darauf ausgerichtet, mehrere Prozesse gleichzeitig zu verwalten. So wird beim Start eines Programms "vorsichts- halber" der ganze Speicher reserviert. Nach Beenden wird er dann wieder frei- gegeben. Das Ziel ist nun aber, das Programm im Speicher zu halten. Das wird erreicht, indem, nach dem Beenden, der Speicher, der wirklich vom Programm benötigt wird, freigehalten wird. Ein weiterer wichtiger Punkt, der bei einem TSR Programm beachtet werden muß, ist das umbiegen von Unterbrechern. TurboPascal gibt die Möglichkeit, die eigentliche Funktion eines Unterbrechers abzustellen und mit einer eigenen Routine zu ersetzen. - Das Umbiegen eines Interrupts In einer Interruptroutine ist ziemlich jedes TP-Kommando möglich. Um dem PC jedoch mitzuteilen, das ein Interrupt verändert werden, sind folgende Befehle nötig. Als erstes muß die Prozedur wie folgt definiert werden. procedure neuer_int09h; interrupt; Diese Deklaration ist deshalb nötig weil ein TSR Programm auf maschinen- ebene anders beendet wird als ein normales TP Programm. Wichtig: Es ist nicht möglich, das Variablen an Interruptprozeduren über- geben werden. Man kann sie zwar in die Register schreiben, was ich jedoch hier nicht berücksichtigen werde. Beim Umbiegen der Interrupts ist ebenfalls zu beachten, das nicht jeder Unterbrecher behandelt werden kann, da das zu einem Systemabsturz führen kann. - Aufbau eines TSR Programms TSRs bestehen grundsätzlich aus folgenden Teilen INTERRUPTVEKTOR CODE DES TSR PROGRAMMS SPRUNG ZUR GELESENEN INTERRUPTADRESSE CODE DES URSPRÜNGLICHEN INTERRUPT HANDLERS Das Herzstück ist natürlich der Handler. Er ist für die eigentliche Umleitung des Unterbrechers auf die eigene Routine zuständig. Ebenfalls ist ein sogenannter Installationsteilvorhanden. Er enthält üblicher- weise eine Meldung, die den Benutzer darüber aufklärt(oder auch nicht!!!), das ein TSR läuft. - Beispiel TSR Ein gutes Beispiel für das Coden von TSRs ist ein Programm von Martin Althaus. Es soll die Uhrzeit nach dem Start des Programms in der rechten oberen Ecke angezeigt werden. Als zu verändernder Unterbrecher wird Interrupt 1Ch des Bios benutzt. (Einzelheit: Er wird vom Prozessor etwa 18,2 mal in der Sekunde ange- sprochen) #####################QUELLCODE######################################## program zeituhr; uses crt, dos; {--------------------------------------------------------------------- zeituhr.pas Speicherressidentes Programm, das nach dem Aufruf die Systemzeit be- ständig in der rechten oberen Bildschirmecke darstellt. von Martin Althaus 4/90 ---------------------------------------------------------------------- } {$s-} {Damit noch speicher frei bleibt} {$m 1048,0,0} const timer=$1c; {der umzubiegende Interrupt} var oldinit:pointer; {Zeiger auf den alten Interrupt} st,mi,se,hu:word; {Zähler für HH, MM, SS} uhr,help:string; count:byte; {Zählt die Timer Ticks} procedure zeigezeit; interrupt; {---------------------------------------------------------------------- Stellt die aktuelle Uhrzeit oben links in der Bildschirmecke dar. Die Zeit ist in dem String UHR gespeichert ----------------------------------------------------------------------- } var x,y:byte; {Bildschirmkoordinaten} begin inc(count); {Tick zählen} if count=18 then {eine Sek. um?} begin {Ja zeige Zeit} count:=0; {Zähler auf Null} if uhr[8]<'9' then inc(byte(uhr[8])) {Eine Sekunde raufzählen} else {Sprung auf neun} begin uhr[8]:='0'; {Einer Sekunden auf null} if uhr[7]<5 then inc(byte(uhr[7])) {Zehner Sekunden raufzählen} else {ein Sprung von} begin uhr[7]:='0'; {Zehner Sekunden} {auf null setzen} if uhr[5]<'9' then inc(byte(uhr[5])) {einer Minuten umspringen} else {ja, setze} begin {einer Minuten} uhr[5]:='0'; {auf null} if uhr[4]<'5' then inc(byte(uhr[4])) {zehner Minuten umsetzen} else {ja, setzten sie} begin {auf null und} uhr[4]:='0'; {zähle die Stunden hoch} if (uhr[2]<'9') and (uhr[1]<'2') then inc(byte(uhr[2])){kein Sprung von 9->10} {oder von 19->20} {doch} else begin if (uhr[2]='9') then inc(uhr[1]) {einer Stunden zählen} else if uhr[2]<'3' {Zehner-Stunden} then inc(byte(uhr[2])) {zählen,} {solange < 24} else begin if uhr[1]<'2' then inc(byte(uhr[1])) else uhr:='00:00:00'; {Wechsel von 23.59->00:00} end; end; end; {Alle if} end; {Abfragen beenden} end; end; x:=wherex; {Bildschirmkoordinaten} y:=wherey; {merken} gotoxy(70,1); {Ausgabeposition ansteuern} write(uhr); {Zeit ausgeben} gotoxy(x,y); {Alte Bildschirmposition} {wiederherstellen} end; end; {---------------------------------------------------------------------- Initialisierungsteil des residenten Programms. Setzt den Vektor und initialisiert den Z"hler. ----------------------------------------------------------------------- { begin writeln; writeln('TIMER 1.0'); writeln('---------'); writeln; write('Ein einfaches speicherresidentes Programm'); writeln('Von Martin Althaus'); writeln; gettime(st,mi,se,hu); {Lies aktuelle Zeit aus} str(st,help); {und wandle sie in einen String um} if st<10 then uhr:='0' {Führende Null einsetzen} else uhr:=''; uhr:=uhr+help+':'; {Stunden in String einbauen} str(mi,help); {Minuten in String umwandeln} if mi<10 then uhr:=uhr+'0'; {Führende Null einbauen} uhr:=uhr+help+':'; {Minuten an Stunden anh"ngen} str(se,help); {Sekunden in String wandeln} if se<10 then uhr:=uhr+'0'; {Führende Null notwendig?} uhr:=uhr+help; {Sekunden an Minuten anh"ngen} getinvec(timer,oldinit); {alten Interruptvektor auslesen} count:=0 {Zähler initialisieren} setintvec(timer,@zeigezeit); {Neuen Interrupt setzen} keep(0); {Programm im Speicher lassen} end. ############################ENDEQUELLCODE################################# Ich denke der Code ist recht gut kommentiert. Wie man sieht hat ein recht einfaches TSR einen recht komplizierten Code. Wenn man sich erstmal in die Materie eingearbeitet hat werden einem aber vielfältige Möglichkeiten geboten. MÖGLICHKEITEN VON TURBO PASCAL Turbo Pascal bietet einige Interessante Möglichkeiten zur Erstellung von Programmen. - Erstellung speicherresidenter Programme (notwendig für Keylogger, Trojaner etc) - Objektorientierte Programmierung - Einbinden von Modulen als Obj. Dateien(z.B in C) - Direktes einbinden von Maschinensprache durch Assembler Es wird also deutlich, das TurboPascal keineswegs eine Anfängersprache ist, mit der vernünftiges Programmieren nicht möglich ist. Der Text ist nur als Einführung in Pascal gedacht. Auch die Erweiterten Möglichkeiten sind nicht komplett beschrieben. Falls jemand Blut geleckt hat und sich näher mit der Programmiersprache befassen will, sollte er folgende weiterführende Literatur zur Hand nehmen. - sämtliche Bücher von Martin Althaus. - Bamir Shammas, Object-Oriented Programming with Turbo Pascal - TurboPascal Power Tools. Sehr, sehr viel Quellcode LAST WORDS Ich hoffe der Text hat manchen von euch gefallen. Wie gesagt habe ich nur ein bißchen an der Oberfläche gekratzt. Ich möchte noch Erwähnen das der Text in der Beta-Version ist. Falls ich Fehler(keine Rechtschreibfehler!!!) gemacht habe, wendet euch an XeroX99@hushmail.com. Der Text darf natürlich kopiert und veröffentlicht werden. Nur lasst ihn halt ganz und verändert nichts! :: Information :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: Dieser Text hier stammt aus dem Tutorial Archiv des Login Club´s :: Visit: http://www.login-club.org | irc.euric.net 6667 #loginclub :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: ::