Kontrollstrukturen und Exception Handling

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
pythondhst123
User
Beiträge: 3
Registriert: Samstag 4. Dezember 2010, 07:07

Hi,

ich code seit einem Tag Python und bin sehr begeistert.
Ich komme aus der JAVA Ecke und bleibe auch dort.

Nun habe ich ein kleines Problem in Python:

Ich will folgendes Tool schreiben (Lediglich zu legalen Penetrationtestzwecken,
ich distanziere mich von jeglichen Straftaten im IT-Bereich).

Es wird sich automatisch eine Reihe von Passwörtern ausprobiert mit denen probiert wird,
sich in einem FTP Account einzuloggen (Ich muss dieses konkrete Beispiel nenne, weil nur Beispiele solcher Art folgendes Problem erzeugen)

Code: Alles auswählen

import sys
import re
from ftplib import FTP
import ftplib

proto	= sys.argv[1]
host	= sys.argv[2]
user	= sys.argv[3]
file	= sys.argv[4]

fobjc 	= open(file, "r")
fobj 	= open(file, "r")

index	= 0

def count():
	a = 0
	for i in fobjc:
		a += 1
	return a
	
def fill(a):
	list = range(0,a,1)
	a = 0
	for word in fobj:
		word = word.split("\n")
		list[a] = word[0]
		a += 1
	return list	
	
def	brute(a):
	print "Checking Password: " + a
	ftpconn = FTP(host,user,a)
	s = ftpconn.getwelcome()
	if (re.search("203",s)) == None:
		print s
		sys.exit()
			
def main(index):
	list = fill(count())
	while(1):
		try:
			brute(list[index])
		except ftplib.all_errors as detail:
			index += 1
			main(index)

main(index)
Also der Code funktioniert so:
Es wird eine Textdatei geladen, die eine Passwortliste enhält.
Diese Passwörter werde in ein Array passender Größe gefüllt.
Wenn der Array befüllt wurde, wird dieser Array zurückgegeben in die Hauptfunktion,
die undendlich oft durchläuft.

Sie sollte eigentlich unendlich viele Exceptions diesen Typs auffangen können,
weil bei einer geworfenen exception von brute, main sich selber aufruft.

Jedoch erscheint bei mir die Fehlermeldung: "list index out of range"

Doch egal was ich mache oder ändere, es wird die List nicht durchlaufen,
ich hoffe ihr könnt ihr mit bissl helfen, danke :)

euer pythondhst123
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Bitte Traceback und beispielhaft ein paar Zeilen aus der Passwortdatei posten. Mich wundert, wieso du nach dem Auslesen einer Zeile auf den Index 0 dieser Zeile zugreifen willst. Davon ab muss man in Python keine Arrays mit vordefinierter Größe übergeben. Die Dinger heißen hier Listen und wachsen dynamisch, wenn Elemente hinzugefügt werden. So kannst du dir beispielsweise die count()-Funktion komplett sparen.

EDIT: Achso, du splittest und gebrauchst dann nur das erste Element vom Ergebnis. Das ist ja noch wirscher. :lol:
BlackJack

@pythondhst123: Das Programm ist an einigen Stellen etwas komisch bis umständlich.

Ausser Konstantendefinitionen sollte sich kein Code auf Modulebene befinden, und die Hauptfunktion sollte durch folgendes Konstrukt vor dem Ausführen bei einfachem importieren geschützt werden:

Code: Alles auswählen

if __name__ == '__main__':
    main_function()
Dann kannst Du Dein Modul im Interpreter importieren und die einzelnen Funktionen "live" ausprobieren und testen.

Bei der Formatierung des Quelltextes weichst Du vom Style-Guide ab, der unter anderem Einrückung mit 4 statt 8 Leerzeichen (oder gar Tabs) empfiehlt, keine Ausrichtung von Gleichheitszeichen, und ein Leerzeichen nach Kommata.

Die Zuweisung der Kommandozeilenargumente kann man mit "tupel unpacking" einfacher schreiben. Dabei steht auf der linken Seite der Zuweisung ein Tupel mit Namen und auf der rechten ein "iterable" mit entsprechend vielen Elementen:

Code: Alles auswählen

    proto, host, user, filename = sys.argv[1:]
Wie Du siehst habe ich `file` in `filename` umbenannt. Erstens ist das ein passenderer Name, denn es handelt sich ja nicht um ein Datei-Objekt, sondern um den Namen einer Datei, und zweitens ist an den Namen `file` an einen eingebauten Typ für Datei-Objekte gebunden.

Dateien sollte man immer explizit auch wieder schliessen oder die ``with``-Anweisung verwenden, an sprechende Namen binden, und nicht unnötigerweise zweimal gleichzeitig öffnen. Hier sind auch die Namen wieder sehr nichtssagend gewählt.

"Globale" Werte sind schlechter Stil. Alles was eine Funktion ausser Konstanten benötigt, sollte als Argument übergeben werden. Der Name `index` auf Modulebene ist überflüssig weil der genau einmal an die 0 gebunden und dann nie wieder geändert wird.

Der rekursive Aufruf der `main()` ist keine gute Idee. Wäre es auch in Java nicht, denn beide Sprachen garantieren nicht, dass der Compiler so eine Endrekursion zu einer Schleife optimiert, und so wird der Aufrufstapel gefüllt bis es zu einem Programmabruch kommt, wenn nur genügend Zeilen in der Datei mit den Hostnamen sind.

`list` ist wieder der Name einer eingebauten Funktion, den man nicht an etwas anderes binden sollte.

Statt erst alle Zeilen zu zählen, dann eine Liste mit Zahlen von 0 bis Zeilenanzahl - 1 zu erstellen, die dann in einem zweiten Lesevorgang durch die Zeilen ersetzt werden, könntest Du entweder alle Zeilen an eine Liste anhängen, oder mal überlegen warum Du die überhaupt alle im Speicher haben möchtest. Du verarbeitest ja sowieso eine Zeile nach der anderen, also könntest Du Dir die auch gleich direkt aus der Datei holen. Indexzugriffe mit Zählvariablen sind in Python sehr selten, weil die Containerobjekte selbst "iterable" sind, man den Umweg über einen Index in der Regel nicht gehen muss.

Mit `split()` die Zeilenenden zu entfernen ist zumindest ziemlich ungewöhnlich. Ebenso wie ein regulärer Ausdruck um eine konstante Zeichenkette in einer anderen zu suchen. Schau Dir im Tutorial noch einmal an was man mit Zeichenketten machen kann. Das Tutorial in der Dokumentation hast Du durchgearbeitet!?

Deine Programmlogik ist total konfus. Wenn Du sagst Du kommst aus der Java-Ecke heisst das wahrscheinlich Du hast ein Buch über Java aber keine Programmiererfahrung, denn die Probleme die hier offensichtlich werden, sind ziemlich unabhängig von der Programmiersprache. Ich würde sagen, schreib das nochmal neu. Weniger umständlich und so dass man dem Kontrollfluss einfacher folgen kann.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Laden der Wörter und durchprobieren sollte nicht komplizierter als so sein:

Code: Alles auswählen

with open("...") as f:
    words = [w.strip() for w in f]
for pw in words:
    try:
        brute(pw)
    except ftplib.all_errors:
        continue
Da kommt es dann auch zu keiner Endlosschleife. Eigentlich kann ich mir auch noch das explizite Erzeugen des words-Arrays schenken und das direkt in die for-Schleife als Generatorausdruck in runden statt in eckigen Klammern setzen, oder es noch einfacher machen:

Code: Alles auswählen

with open("...") as f:
    for l in f:
        try:
            brute(l.strip())
        except:
            continue
Viel Spaß noch mit Python :)
Stefan
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

pythondhst123 hat geschrieben:Sie sollte eigentlich unendlich viele Exceptions diesen Typs auffangen können,
weil bei einer geworfenen exception von brute, main sich selber aufruft.

Jedoch erscheint bei mir die Fehlermeldung: "list index out of range"
Der Typ aber ist der springende Punkt, ein IndexError wird wohl kaum durch ftplib.all_errors abgedeckt sein. Unendlich viele Ausnahmen kannst du damit aber auch nicht abfangen. Da wird bei CPython früher oder später das Rekursionslimit zuschlagen.
pythondhst123
User
Beiträge: 3
Registriert: Samstag 4. Dezember 2010, 07:07

Ich danke euch erstmal für eure guten Antworten.

Um zB Die Frage zu beantworten warum ich die Wörter splitte um dann das erste Element zu gebrauchen,
weil ich \n rausfiltern will.

Das mit den Arrays is ne coole Info danke :)

Aber es geht mit um das unendlich male abfangen von Exceptions.
Es rechne eben nur mit der Exception "Login incorrect" oder "Permission denied"
Beide vom selben Typ.

Aufbau der Wortliste
password1
password2
password3
...

mfg,
pythondhst123

PS: Ich sehe gerade diesen Codebaustein der bei der Exception einfach continue befiehlt.
Das geht? Werde ich gleich ausprobieren, ihr seid die besten danke :)

PPS: Wie gesagt ich code erst seid sehr kurzem Python,
ich mache das Galileo Openbook für meinen schlechten Stil mitverantwortlich...
nomnom
User
Beiträge: 487
Registriert: Mittwoch 19. Mai 2010, 16:25

Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

pythondhst123 hat geschrieben:PS: Ich sehe gerade diesen Codebaustein der bei der Exception einfach continue befiehlt.
Das geht? Werde ich gleich ausprobieren, ihr seid die besten danke :)
Geht doch bei Java genauso…

Code: Alles auswählen

        FileInputStream fin = new FileInputStream("filename");
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(fin));
            String line;
            while((line = reader.readLine()) != null) {
                try {
                    brute(line.trim());
                } catch(Exception e) {
                    continue;
                }
            }
        } finally {
            fin.close();
        }
In dem Fall würde es aber auch ein pass bzw. nichts bei Java tun.
pythondhst123
User
Beiträge: 3
Registriert: Samstag 4. Dezember 2010, 07:07

hätte ich bei der Behanldung von Exception Handling besser aufpassen sollen.
Vielen Dank!

mfg,
pythondhst123
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Code: Alles auswählen

        FileInputStream fin = new FileInputStream("filename");
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(fin));
            String line;
            while((line = reader.readLine()) != null) {
                try {
                    brute(line.trim());
                } catch(Exception e) {
                    continue;
                }
            }
        } finally {
            fin.close();
        }
Alter das kann ja kein Mensch lesen o_O
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Dauerbaustelle hat geschrieben:Alter das kann ja kein Mensch lesen o_O
Stimmt. Ich würde es wohl so machen:

Code: Alles auswählen

public void checkAllPasswords(String fileName) throws IOException {
  BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), "utf-8"));
  try {
    String line = reader.readLine();
    while (line != null) {
      checkPassword(line.trim());
      line = reader.readLine();
    }
  } finally {
    reader.close();
  }  
}

private void checkPassword(String password) {
  try {
    brute(password);
  } catch (BruteException e) {
    /* ignored */
  }
}
Im einzelnen: Den BufferedReader in einer Zeile erzeugen und dann diesen schließen und nicht den FIS. Außerdem immer das Encoding angeben und sich nicht auf das Plattform-Encoding verlassen. Dann auf das drive-by-assignment verzichten - das meckert einem ein Tool wie Sonar sonst an. Statt 2 try-Anweisungen zu schachteln, den inneren Teil lieber in eigene Funktion auslagern. Nun kann ich leider nicht mehr einfach "continue" benutzen, um den sonst leeren catch-Block zu füllen, also muss ich da einen Kommentar reinschreiben, den Sonar (bzw. Checkstyle, PMD und die Analysen von IDEA) akzeptiert.

Stefan
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Na so sieht das ja gleich viel lesbarer aus!1
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Dauerbaustelle hat geschrieben:Na so sieht das ja gleich viel lesbarer aus!1
Gell? :mrgreen:
BlackJack

Ich benutze ja gerne die Apache-Commons. Da gibt's einen `LineIterator` für Dateien:

Code: Alles auswählen

    private static void checkAllPasswords(String filename) throws IOException {
        LineIterator lines = FileUtils
                .lineIterator(new File(filename), "utf-8");
        try {
            while (lines.hasNext()) {
                checkPassword(lines.nextLine());
            }
        } finally {
            lines.close();
        }
    }
An der Stelle fasse ich mich aber immer an den Kopf was die Java-Entwickler dazu gebracht hat, dass man Objekte, die das `Iterator`-Interface implementieren, nicht in einer "foreach"-Schleife verwenden kann.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Da gibts doch diese neumoderne Syntax in Java 6 (oder so), die unter anderem auch mit Arrays funktioniert. Kannst du ja mal ausprobieren:

Code: Alles auswählen

for(T item : iterableWithItemsOfTypeT) { /* blah */ }
Konkret dann also

Code: Alles auswählen

for(String line : FileUtils.lineIterator(...)) { /* blah */ }
lunar

@Dauerbaustelle: Das zweite Beispiel wird nicht kompilieren, genau aus dem Grund, den BlackJack bereits angesprochen hat: Diese Syntaxform funktioniert nicht mit "Iterator"-Objekten, den sie erwartet nur und ausschließlich ein Objekt vom Typ "Iterable".
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Achso, ich hatte nicht realisiert, dass er mit "foreach" diese "for"-Variante meinte. (Aber was denn sonst...)
Antworten