|
Antwort |
Registriert seit: 16. Jul 2003 170 Beiträge Delphi 6 Enterprise |
#21
Danke!!!
Das hat mir glaub ich sehr geholfen (und sicherlich auch einigen Anderen). Das größte Problem war wohl, dass ich keine initialization geschrieben hatte. Na ja, jetzt wird's schon gehn. MfG, Daniel. |
Zitat |
Registriert seit: 16. Jul 2003 170 Beiträge Delphi 6 Enterprise |
#22
Hi,
komme inzwischen ganz gut klar. Hab mir die für mich wichtigen Aspekte aus deinen Posts angesehen und die Units durchgearbeitet. Anfangs bin ich nur daran verzweifelt, dass ich Rijndael einfach nicht definieren konnte und auch nicht in der Unit fand - bis ich dann feststellte, dass es neben Cipher auch noch Cipher1 gibt!!! So weit so gut, es gibt ja die Aktionen paEncode (zum Verschlüsseln), paDecode (entschlüsseln) und dann noch paWipe. Über die Funktion des Letzten bin ich mir nicht im Klaren. Wiped das die Quelldatei oder was? Gibt es eigentlich eine Funktion zum Wipen von Dateien, sodass ich die Option "Quelldatei nach Verschlüsseln wipen" einbauen kann? Und: Der CipherManager verfügt über ein OnProgress Event. Wie kann ich darauf zugreifen, wenn ich den Manager nicht verwende (was bei mir der Fall ist)? Danke, Daniel. |
Zitat |
Registriert seit: 29. Mai 2002 37.621 Beiträge Delphi 2006 Professional |
#23
Eine Frage noch: Das Passwort wird im Header logischerweise nur als Hash-Wert gespeichert oder?
Also:
Code:
Den Header darf man natürlich nicht mit verschlüsseln, also erst die Datei verschlüsseln und dann den Header dazupacken. Beim Entschlüsseln Header auslesen, Passwort überprüfen, Header aus der verschlüsselten Datei entfernen, entschlüsseln.
////////////////////////////////
Header CipherIdentity: Cardinal HashIdentity: Cardinal PW als Hash: String; //////////////////////////////// Daten verschlüsselte Datei ... ... ... ... ... ... ... //////////////////////////////// Hm. Der Passwort Hash wird ja ein String. Wie schreibt man denn ein Header-Record mit eienm Delphi HugeString, also mit einem String dessen Länge man nicht kennt? Somit ist ja auch die Headergröße nicht bekannt. Oder kann man davon ausgehen, dass ein Hash nicht länger wird als 255 Zeichen? PS. Ich denke nur mal laut.
Michael
Ein Teil meines Codes würde euch verunsichern. |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#24
Zitat:
So weit so gut, es gibt ja die Aktionen paEncode (zum Verschlüsseln), paDecode (entschlüsseln) und dann noch paWipe. Über die Funktion des Letzten bin ich mir nicht im Klaren. Wiped das die Quelldatei oder was?
Aber in TProtection gibt es ebenfalls ähnliche Methoden, die für ALLE von TProtection abgeleiteten Klassen gültig sind, Zb. TProtection .CodeStream(). Man kann also mit einem Cipher/Hash/Random ebenfalls über diese Methoden von TProtection Daten verschlüsseln. Kryptographisch gesehen würde ich aus heutiger Sicht meine damaligen Absichten als naiv bezeichnen, und nicht mehr auf die TProtection Methoden zurückgreifen. Man kann zwar tatsächlich auch mit einer Hash-Funktion eine Verschlüsselung machen, man nennt diese offiziell MDC=Message Digest Cipher, heutzutage würde ich aber grundsätzliche solche Verfahren ablehnen. Nungut, DEC enthält sie und somit muß ich auch darauf eingehen. Nur die Methoden von TProtection enthalten die Möglichkeit eine Progress-Callback zu benutzen. Hier im Forum findest du in der Codelibrary einen TProgressStreamAdapter der diese Progressfunktionalität viel eleganter lösst. Die im DEC enthaltene Callback ist global und nicht threadsafe und funktioniert nur mit den TProtection.CodeXXXX Methoden. Also wiederum einige Nachteile. Um denoch einen Progress zu coden schauste mal in DECUtil.pas rein, dort findest du eine globale Variable "Progress: TProgressEvent". Diese musst du vor der Benutzung von TProtection.CodeXXXX() auf eine eigene Eventmethode setzen und danach wieder auf nil. Genauso gehen auch die Manager Components vor.
Zitat:
Gibt es eigentlich eine Funktion zum Wipen von Dateien, sodass ich die Option "Quelldatei nach Verschlüsseln wipen" einbauen kann?
Zitat:
Und: Der CipherManager verfügt über ein OnProgress Event. Wie kann ich darauf zugreifen, wenn ich den Manager nicht verwende (was bei mir der Fall ist)?
@Luckie:
Zitat:
Eine Frage noch: Das Passwort wird im Header logischerweise nur als Hash-Wert gespeichert oder?
Also wichtigste Regel: Ein Passwort ist ein Geheimnis das meistens durch den Menschen sehr schlecht und weit unter den Sicherheitsanforderungen ausgewählt wurde. Deshalb gehört dieses Passwort NUR in's Hirn und sollte in keinerlei Form gespeichert werden. Es sei denn, es würde mit einem viel besseren Passwort verschlüsselt oder transformiert.
Zitat:
Den Header darf man natürlich nicht mit verschlüsseln, also erst die Datei verschlüsseln und dann den Header dazupacken. Beim Entschlüsseln Header auslesen, Passwort überprüfen, Header aus der verschlüsselten Datei entfernen, entschlüsseln.
Auch solche "Ausnahmen" würde ich heutzutage NICHT mehr so programmieren.
Zitat:
Der Passwort Hash wird ja ein String. Wie schreibt man denn ein Header-Record mit eienm Delphi HugeString, also mit einem String dessen Länge man nicht kennt? Somit ist ja auch die Headergröße nicht bekannt. Oder kann man davon ausgehen, dass ein Hash nicht länger wird als 255 Zeichen?
Ein Header mit variabler Größe sollte nach dem Ident zur Erkennung des Headers eine Größenangabe in Bytes enthalten. Der Ident und die Größenangabe haben dann feste Länge und können nun direkt ausgelesen werden. Somit besteht ein Header variabler Größe im Grunde genommen aus ZWEI Teilen. Einen festen Part am Anfang aus Ident + Größenangabe und einem variablen Teil gleich danach der exakt Header.Size Bytes groß ist. Generell bevorzuge ich aber Header die immer feste Länge haben, meistens 16,32,48 Bytes. Der Rest der fehlenden Bytes um auf diese Größen zu kommen wird einfach reserviert. Damit sind dann auch Erweiterungen des Headers möglich. Auf eine Verschlüsselung des Headers sollte verzichtet werden, da sonst "known plaintext attacks" möglich sind. Gleich nach dem Header sollte man die Daten verschlüsselt speichern. ABER, diese Daten sollten mit Cipher.BufSize Bytes Zufallsdaten am Anfang erweitert werden. Gruß Hagen |
Zitat |
Registriert seit: 29. Mai 2002 37.621 Beiträge Delphi 2006 Professional |
#25
Mann, ich sollte ins Bett gehen, das Passwort speichern war natürlich Quatsch mit Soße. Und damit hat sich auch das Header-Problem gelöst.
Aber was man speichern könnte, wäre ein Hash von der original Datei, um nach dem Entschlüsseln überprüfen zu können, ob sie noch in Ordnung ist. Was machen eigentlich deine Streams, wenn man mit dem falschen Passwort entschlüsselt?
Michael
Ein Teil meines Codes würde euch verunsichern. |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#26
Zitat:
Aber was man speichern könnte, wäre ein Hash von der original Datei, um nach dem Entschlüsseln überprüfen zu können, ob sie noch in Ordnung ist.
Was machen eigentlich deine Streams, wenn man mit dem falschen Passwort entschlüsselt? Somit ist es eine gute Idee eine Prüfsumme zu berechnen. Dazu benötigt man aber nicht unbedingt eine separate Hashfunktion, sondern man kann im Ciphermode cmCTS einfach nach dem verschlüsseln der Daten mit Cipher.CalcMAC eine Prüfsumme erzeugen. Die Ciphermodes cmCBC,cmCTS ua. sind Modis die per Feedback Register blockweise die Daten zerlegen und jeden Block separat verschlüsseln. Dabei wird aber über das Feedback Register jeder Block mit dem nachfolgenden Block verknüpft. Somit würde sich ein Bitfehler eines Datenblockes in den nächsten Datenblock fortpflanzen. Nun im cmCBC Modus beträfe so ein Fehler nur den nachfolgenden Block und somit ist cmCBC ungeeignet als C-MAC = Cipher-Message Authentification Code. cmCBC nennt man auch einen "Selbst synchronisierenden Modus", da er eben bei einem Bitfehler nur ZWEI Datenblöcke falsch entschlüsselt, alle nachfolgenden Blöcke sind dan wieder ok. Den cmCTS Mode habe ich aber so konstruiert das ein einzigster Bitfehler am Anfang der Message ALLE nachfolgenden Bits beeinflusst. D.h. der Fehler beeinflusst die ganze Nachricht bis zum Ende und somit auch den letzten Block der Nachricht. Wird damit ein C-MAC berechnet und dieser in die Datei gespeichert, kann bei der Entschlüsselung der neue C-MAC mit dem gespeicherten verglichen werden. Sollten sie ungleich sein, so wurde entweder ein falsches Paswort benutzt oder die Daten wurden manipuliert. Allerdings, dieser Modus ist ein "Alles oder Nichts" Modus. Alternativ könnte man aber den von mir vorgeschlagenen Zufallsdatenblock dazu benutzten um zu erkennen ob das richtige Passwort eingegeben wurde. Der Dateiaufbau sähe dann so aus: Header + Zufallsdaten + Prüfsumme über Zufallsdaten + Message. Bis auf den Header werden alle Teile wie eine einzigste Nachricht verschlüsselt. Bei Entschlüsseln kann man sofort nach Entschlüsselung der Zufallsdaten + Prüfsumme diese erstmal abtesten. Sollte sie falsch sein ist mit hoher Wahrscheinlichkeit das Passwort falsch. Ein Angreifer der nun per Brute Force und einer Test-Entschlüsslung arbeitet müsste aber dann auch nur bis zur Prüfsumme entschlüsseln um zu sehen ob sein Testpasswort das richtige ist. Macht man es aber über die C-MAC Methode so muß der Angreifer und auch der Benutzer die Message immer vollständig entschlüsseln um zu erfahren ob alles korrekt ist. Bei all diesen Überlegungen könnte man mich fragen: "warum hast du nicht gleich ein solches Datenformat und Datenprotokoll in DEC integriert ?", denn der zusätzlich nötige Aufwand um mit DEC korrekt und sicher Daten zu verwalten ist ja immernoch erheblich ! Nun, einfach weil es viel zu viele unterschiedliche Anforderungen und Standarddaten Formate gibt. Man kann es also im Grunde keinem richtig Recht machen. Deshalb ist dies die Aufgabe des Benutzers vom DEC. Gruß Hagen |
Zitat |
Registriert seit: 29. Mai 2002 37.621 Beiträge Delphi 2006 Professional |
#27
Das war ja auch ein Punkt in der DLL von dem Russen, der hat das Passwort in der verschlüsselten Datei gespeichert.
Ich wollte erstmal den Header so einfach wie möglich halten. Das heißt nur die CipherIndetity speichern und eine Prüfsumme der original Datei. Wie lange braucht es einen Hash/Prüfsumme von einer 100 MB Datei zu erstellen? Wie schnell sind die Hash Algorithmen im DEC? Und kann man da auch mit deinem ProgressAdapter arbeiten, also mit Streams? Wenn es länger dauert könnte man dann einen Fortschritt anzeigen. Dass das sowieso in Threads ausgelagert wird ist klar.
Michael
Ein Teil meines Codes würde euch verunsichern. |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#28
Zitat:
Wie lange braucht es einen Hash/Prüfsumme von einer 100 MB Datei zu erstellen?
Zitat:
Wie schnell sind die Hash Algorithmen im DEC?
Zitat:
Und kann man da auch mit deinem ProgressAdapter arbeiten, also mit Streams? Wenn es länger dauert könnte man dann einen Fortschritt anzeigen. Dass das sowieso in Threads ausgelagert wird ist klar.
Diese Methodik kann mit JEDEM TStream basierten System benutzt werden, sogar zum Laden von DFM's, Indy Streams usw. usw. Gruß Hagen |
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#29
Zitat:
Ich wollte erstmal den Header so einfach wie möglich halten. Das heißt nur die CipherIndetity speichern und eine Prüfsumme der original Datei.
Ich empfehle dir dringendst mit der Expansion der Message per Zufallsbytes zu arbeiten. Dies erhöht nicht nur marginal die Sicherheit, sondern ist ein MUSS um es sicher zu machen. Normalerweise benutzt man dazu die sogenanten Initialization Vectors = IV's. Diese werden mit Zufallsbytes gefüllt und diese Bytes müssen unverschlüsselt mit gespeichert werden damit man wieder entschlüsseln kann. Solche IV's stellen sicher das man bestimmte effektive Angriffe nicht mehr durchführen kann. Nun, ich persönlich finde diese IV's nicht so gut, da sie eben lesbar gespeichert werden. Besser ist es dann gleich diese IV-Zufallsbytes zur Expansion der Nachricht zu verwenden und sie am Anfang der Nachricht mit zu verschlüsseln. Dies hat die gleichen Auswirkungen wie ein IV, ist aber sicherer da sie verschlüsselt sind. IV's und diese Zufallsbytes Expandierung setzen voraus das mit einem Feedback-Ciphermode gearbeitet wird. D.h. cmECB darf dann nicht benutzt werden. Diese IV's werden in DEC per default durch die Verschlüsselung von 0 Bytes mit dem Passwort initialisiert. Wenn sie also NICHT explizit benutzt werden, so ist der IV im DEC nichts anderes als die Verschlüsselung von 0 Bytes. Nur mit dem richtigen Passwort wäre also in der Entschlüssung der IV identisch mit dem IV zur Verschlüsselung. Dies hat aber den Nachteil das ein Angreifer der dies weis den IV dazu benutzen kann um nur mit einem Block Entschlüsslung und bekanntem Messageheader eine Brute Force Attacke auf das Passwort machen kann. Wird die Message mit Zufallsdaten expandiert funktioniert dies nicht mehr. Gruß Hagen |
Zitat |
Registriert seit: 16. Jul 2003 170 Beiträge Delphi 6 Enterprise |
#30
Hi,
ich bin's mal wieder: Ich hab mein Prog jetzt so weit, dass es Texte verschlüsselt und in gewisser Weise auch Dateien. Aber bei den Dateien stehe ich vor einem Problem: Ich schaffe es einfach nicht den Hashwert der Originaldatei (ein 28-stelliger String) so zu speichern, dass ich ihn hinterher auch wieder lesen kann. Und vor dem Entschlüsseln der Datei muss ich den String ja sicherlich auch irgendwie wieder aus dem Header entfernen. Mein Code sieht bis jetzt folgendermaßen aus:
Delphi-Quellcode:
Irgendwas muss da doch falsch sein. Ich weiß nur nicht so genau was.
const
DefCipherClass: TCipherClass = TCipher_Rijndael; //mögliche siehe Cipher.pas und Cipher1.pas DefHashClass: THashClass = THash_SHA1; //mögliche siehe Hash.pas DefStringFormat: Integer = fmtMIME64; //mögliche siehe DECUtil.pas HashLength = 28; // Länge des Hash-Wertes als String geschrieben CipherMode = cmCTS; // Cipher-Mode: cmCTS, cmCBC, cmCFB, cmOFB, cmECB ErrorMessageText = 'Fehler: Es wurde entweder ein falsches Kennwort eingegeben oder der Ciphertext wurde manipuliert.'; // Nachricht, die bei falschem Passwort etc angezeigt wird ErrorMessageFile = 'Fehler: Es wurde entweder ein falsches Kennwort eingegeben oder die Quelldatei wurde manupuliert.'; // Modi zur Definition der Dateioperationen mdNormal = 0; mdClear = 1; mdWipe = 2; // Modus: mdNormal: Quelldatei bleibt erhalten, Temp (selbst verschlüsselt) wird normal gelöscht // mdClear: Quelldatei wird normal gelöscht, ansonsten s.o. // mdWipe: Quell- und Tempdatei werden gewiped NoSourceMessage = 'Die Quelldatei konnte nicht gefunden werden'; DestExistsMessage = 'Die Ausgabedatei existiert bereits. Soll Sie überschrieben werden?'; DestExistsCaption = 'Ausgabedatei existiert bereits'; ErrorMessage = 'Fehler: Eine der durchzuführenden Operationen konnte eventuell nicht ordnungsgemäß ausgeführt werden.'; implementation {$R *.dfm} function TFrmCipher.ReadHeader(Filename: string): string; var Header: string; FileStream: TFileStream; begin FileStream := TFileStream.Create(Filename, fmOpenRead); if not Assigned(FileStream) then begin RaiseLastOSError(); exit; end; try FileStream.ReadBuffer(Header, Hashlength); result := Header; finally FreeAndNil(FileStream); end; end; function TFrmCipher.DecodeFile(Input, Output, Passwd : string; Modus : word):boolean; // mdNormal = 0 // mdClear = 1 // mdWipe = 2 var DateiHash : string; OldHash : string; begin If FileExists(Input) then begin // Abbruchkriterium, falls DestFile Existiert If FileExists(Output) then if Application.MessageBox(DestExistsMessage,DestExistsCaption,MB_YESNO)=7 then exit; // Original-Hash-Wert aus Dateiheader auslesen OldHash := ReadHeader(Input); // Datei verschlüsseln with DefCipherClass.Create('',nil) do begin try // Cipher einstellen Mode := CipherMode; HashClass := DefHashClass; InitKey(Passwd,nil); CodeFile(Input,Output,paDecode); // Hash-Wert der Originaldatei berechnen with DefHashClass.Create(nil) do begin try DateiHash := CalcFile(Output,nil,DefStringFormat); finally Free; end; end; finally Free; end; If OldHash <> DateiHash then begin ShowMessage(ErrorMessageFile); Exit; end; If FileExists(Output) then begin Case Modus of 1 : DeleteFile(Input); 2 : begin with DefCipherClass.Create('',nil) do begin try InitKey('',nil); CodeFile(Input, Input, paWipe); DeleteFile(Input); finally Free; end; end; end; end; end else ShowMessage(ErrorMessage); end; end else ShowMessage(NoSourceMessage); Result := FileExists(Output); end; function TFrmCipher.AddHeader(Header: string; Filename: String; Modus : integer): Boolean; var SourceFile, DestFile: TFileStream; const extension = '.tmp'; begin result := FALSE; SourceFile := TFileStream.Create(Filename, fmOpenRead); if not Assigned(SourceFile) then begin exit; end; try DestFile := TFileStream.Create(Filename+Extension, fmCreate); if not Assigned(DestFile) then begin exit; end; try { Header in die Zieldatei schreiben } DestFile.WriteBuffer(Header, sizeof(Header)); { Quelldatei dahinter kopieren / anhängen } DestFile.CopyFrom(SourceFile, SourceFile.Size); result := TRUE; finally FreeAndNil(DestFile); end; finally FreeAndNil(SourceFile); end; // Löschen der temporären Datei Case Modus of 0 or 1: DeleteFile(Filename); 2:begin with DefCipherClass.Create('',nil) do begin try InitKey('',nil); CodeFile(Filename,Filename,paWipe); DeleteFile(Filename); finally Free; end; end; end; end; if not RenameFile(Filename+Extension, Filename) then begin result := FALSE; end; end; function TFrmCipher.EncodeFile(Input, Output, Passwd : string; Modus : word):boolean; // mdNormal = 0 // mdClear = 1 // mdWipe = 2 var DateiHash : string; begin If FileExists(Input) then begin // Abbruchkriterium, falls DestFile Existiert If FileExists(Output) then if Application.MessageBox(DestExistsMessage,DestExistsCaption,MB_YESNO)=7 then exit; // Datei verschlüsseln with DefCipherClass.Create('',nil) do begin try Mode := CipherMode; HashClass := DefHashClass; InitKey(Passwd,nil); CodeFile(Input,Output,paEncode); // Hash-Wert der Originaldatei berechnen with DefHashClass.Create(nil) do begin try DateiHash := CalcFile(Input,nil,DefStringFormat); finally Free; end; end; // Hash-Wert in Datei-Header schreiben AddHeader(DateiHash,Output,Modus); finally Free; end; If FileExists(Output) then begin Case Modus of 1 : DeleteFile(Input); 2 : begin with DefCipherClass.Create('',nil) do begin try InitKey('',nil); CodeFile(Input, Input, paWipe); DeleteFile(Input); finally Free; end; end; end; end; end else ShowMessage(ErrorMessage); end; end else ShowMessage(NoSourceMessage); Result := FileExists(Output); end; function TFrmCipher.EncodeText(Input, Passwd : string):string; begin // Hash-Wert des Originaltextes hinzufügen (zur späteren Prüfung) with DefHashClass.Create(nil) do begin try Result := CalcString(Input,nil,DefStringFormat); finally Free; end; // Text verschlüsseln with DefCipherClass.Create('', nil) do begin try Mode := CipherMode; // auch möglich: cmCTS, cmCBC, cmCFB, cmOFB, cmECB HashClass := DefHashClass; // erforderlich InitKey(Passwd, nil); Result := Result + CodeString(Input,paEncode,DefStringFormat); finally Free; end; end; end; end; procedure TFrmCipher.Button1Click(Sender: TObject);// Text verschlüsseln begin // Text verschlüsseln Screen.Cursor := crHourGlass; Memo2.Text := EncodeText(Memo1.Text,Edit1.Text); Screen.Cursor := crDefault; end; function TFrmCipher.DecodeText(Input, Passwd : string):string; var HashStr1, HashStr2 : string; begin // alten Hash-Wert auslesen HashStr1 := Copy(Input,1,HashLength); delete(Input,1,HashLength); //Text entschlüsseln with DefCipherClass.Create('', nil) do begin try Mode := CipherMode; // auch möglich: cmCTS, cmCBC, cmCFB, cmOFB, cmECB HashClass := DefHashClass; // erforderlich SetDefaultCipherClass(DefCipherClass); //nicht zwingend erforderlich, da normalerweise automatisch eingestellt InitKey(Passwd, nil); Result := CodeString(Input,paDecode,DefStringFormat); finally Free; end; end; // neuen Hash-Wert berechnen with DefHashClass.Create(nil) do begin try HashStr2 := CalcString(Result,nil,DefStringFormat); finally Free; end; end; // Hash-Werte vergleichen If HashStr1 <> HashStr2 then ShowMessage(ErrorMessageText); end; procedure TFrmCipher.Button2Click(Sender: TObject);// Text entschlüsseln begin // Text entschlüsseln Screen.Cursor := crHourGlass; Memo1.Text := DecodeText(Memo2.Text,Edit1.Text); Screen.Cursor := crDefault; end; procedure TFrmCipher.Button3Click(Sender: TObject);// Datei verschlüsseln var Modus : word; begin Screen.Cursor := crHourGlass; If RadioButton1.Checked then Modus := 0 else If RadioButton2.Checked then Modus := 1 else If RadioButton3.Checked then Modus := 2 else begin ShowMessage(ErrorMessage); exit; end; EncodeFile(Edit2.Text,Edit3.Text,Edit1.Text,Modus); Screen.Cursor := crDefault; end; procedure TFrmCipher.Button4Click(Sender: TObject);// Datei entschlüsseln var Modus : word; begin Screen.Cursor := crHourGlass; If RadioButton1.Checked then Modus := 0 else If RadioButton2.Checked then Modus := 1 else If RadioButton3.Checked then Modus := 2 else begin ShowMessage(ErrorMessage); exit; end; DecodeFile(Edit2.Text,Edit3.Text,Edit1.Text,Modus); Screen.Cursor := crDefault; end; end. Und noch etwas: Ich habe auf das Auffüllen mit Zufallsdaten verzichtet, weil ich sonst irgendwann an einem Punkt ankomme, an dem ich wahrscheinlich selbst nicht mehr durch den Code durchsteige (was jetzt schon schwierig ist) und ich doch mal denke, dass der Plaintext ansonsten ja auch nicht so known ist, oder? Abgesehen davon hab ich von der Verschlüsselung an sich zu wenig Ahnung um nun sagen zu können, wie sicherheitsrelevant das ist. THX for your help, Daniel! |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |