Jesteśmy na terytorium wrogiego państwa, a naszą misją jest przejęcie planów inwazji. Mamy bezpośredni dostęp do systemu, na którym się znajdują. Problem jest taki, że nie znamy hasła, jednak odrobiliśmy pracę domową i poznaliśmy jego część. Musimy, na podstawie fragmentu, który znamy, wygenerować potencjalne hasła, aby jak najszybciej dostać się do systemu i przejąć plany. Zaczynajmy!
Co wiemy?
- Losy kraju są w naszych rękach.
- Znamy fragment hasła, a dokładnie jego bazowe słowo, które podsłuchaliśmy. Brzmi ono – 'analityk’
- Wiemy, że ~76% haseł, jakie ustawiają ludzie, składa się z hasła bazowego, zakończonego cyfrą, przy czym któraś litera jest zamieniona na wielką. Może to być hasło 'Analityk7′, albo 'analityK3′.
- Na szczęście znamy język programowania Python, programowanie rekurencyjne oraz umiemy pisać skrypty.
- Nasz agent równie wie, że proces nauki języka Python, jest o wiele skuteczniejszy, jeżeli sami powtarzamy ćwiczenia i je modyfikujemy 🙂
Potencjalne rozwiązanie.
Na myśl, nasuwają się dwie funkcje:
- dodajLiczbę – Funkcja, przyjmująca jako parametr hasło, natomiast zwracająca listę zawierającą hasło + liczbę
- zamieńLiterę – funkcja, przyjmująca jako parametr hasło, natomiast zwracająca listę zawierającą listę tego samego hasło, z jedną literą wielką.
Potencjalnie, mogli byśmy pobierać hasło, generować, dla niego, wszystkie wariacje z liczbę (dzięki funkcji dodajLiczbę) następnie tworzyć wariacje z jedną literą wielką (dzięki funkcji zamieńLitere).
Zobaczmy jak mogła by wyglądać taka funkcja, mająca dodać liczbę:
def dodajCyfre (haslo, liczba, nowaLista): nowaLista.append(haslo + str(liczba)) if liczba < 9: return dodajCyfre(haslo, liczba+1, nowaLista) else: return nowaLista # na potrzeby testów, możemy wywołać naszą funkcję, i zobaczyć rezultat print (dodajCyfre ('analityk', 0, []))
Jeżeli się przyjrzymy, okazuje się że funkcja jest dość prosta:
- Pierwszy parametr, to hasło bazowa, drugi to liczba, którą chcemy dodać, trzeci, to lista, do której dodajmy poszczególne hasła.
- Funkcja dodaje do listy – hasło + liczba
- Funkcja, wywołuje sama siebie, zwiększając liczbę o 1
- Jeżeli wywołamy ją poprzez dodajCyfre (’analityk’,0,[]), to wykona się 10 razy, po czym zwróci listę zawierającą – analityk0, analityk2, …. analityk9, czyli zwróci zmienną – nowaLista
Analogicznie, możemy napisać funkcję rekurencją, która, dla zadanego hasła, wygeneruje Analityk, aNalityk, anAlityk itd
Może ona wyglądać tak:
def zamienLitere (haslo, pozycja, nowa): if haslo[pozycja].islower(): haslo2 = haslo[0:pozycja] + haslo[pozycja].upper() + haslo[pozycja+1:] nowa.append(haslo2) if pozycja < len(haslo)-2: return zamienLitere(haslo, pozycja+1, nowa) else: return nowa
Wygląda i działa ona bardzo podobnie jak wyżej opisana funkcja rekurencyjna, dodająca cyfrę. Co więcej, zwróćmy uwagę, że równie przyjmuje takie same parametry ( co będzie bardzo ważne za chwilę 🙂 ). Pierwszy to hasło, drugi, to liczba, tutaj nazwana pozycję, a trzeci to nowo tworzona lista, która zostanie zwrócona po zakończeniu naszej rekurencji.
Świetnie. Mamy zatem 2 funkcje – dodajCyfrę oraz zamieńLiterę. Zastanówmy się, co z nimi możemy zrobić.
- Jeżeli wywołamy jedną funkcję, to dostaniemy w rezultacie listę hasłem z dodaną cyfrą. Druga funkcja, również przyjmuje jako parametr wejściowy hasło, tak więc jak dostanie listę, to nie zadziała.
- Przydała by się jeszcze jedna funkcja, która przechodzi przez listę haseł, i (1) poszerza listę o hasła z dodaną cyfrą, dzięki funkcji dodajCyfrę (2) następnie przechodzi o rozszerzoną listę potencjalnych haseł, i znowu ją rozszerza, tym razem o hasła ze zmienioną literą na wielką
To wszystko brzmi na zakręcone, ale zobaczmy czy w rzeczywistości takie jest:
def przejrzyj(lista, pozycja, nowa, funkcja): nowa += funkcja(lista[pozycja], 0, []) if pozycja < len(lista)-1: return przejrzyj(lista, pozycja+1, nowa, funkcja) else: return nowa lista = przejrzyj(lista, 0, [], dodajCyfre) lista = przejrzyj(lista, 0, [], zamienLitere) print(lista)
Co się dzieje:
- jako parametr, podajemy listę, która zawiera hasło lub hasła bazowe
- drugi i trzeci parametr to znane nam parametry, pozycja oraz nowa lista, które możemy traktować jako parametry 'techniczne’, które umożliwiają nam przemieszczanie się po liście. Nic dla nas nowego
- Bardzo ciekawy jest, za to, czwarty parametr – ’funkcja’. Jest to coś o czym jeszcze nie rozmawialiśmy w kursach Python. Otóż, jak się okazuje, jako parametry, możemy przekazywać, nie tylko zmienne, ale również funkcje które chcemy wywołać. W ten sposób, możemy wykonać funkcję 'przejrzyj’, raz z funkcją 'dodajCyfre’, jako parametr, a raz z funkcją 'zamienLiterę’. Jest to bardzo wygodne, gdyż mamy jedną funkcję, która przechodzi przez wszystkie hasła, i wywołuje dla każdego hasła, odpowiednią funkcję, przekazaną w parametrze. Jest to dość proste, gdyż obydwie funkcje, posiadają taki sam zestaw parametrów, tak więc z punktu widzenia funkcji 'przejrzyj’, nie jest ważne, co robi funkcja, która została przekazana jako parametr.
Spójrzmy na nasze kody, jeszcze raz. Wszystkie 3 funkcje:
def dodajCyfre (haslo, liczba, nowa): nowa.append(haslo + str(liczba)) if liczba < 9: return dodajCyfre(haslo, liczba+1, nowa) else: return nowa def zamienLitere (haslo, pozycja, nowa): if haslo[pozycja].islower(): haslo2 = haslo[0:pozycja] + haslo[pozycja].upper() + haslo[pozycja+1:] nowa.append(haslo2) if pozycja < len(haslo)-2: return zamienLitere(haslo, pozycja+1, nowa) else: return nowa def przejrzyj(lista, pozycja, nowa, funkcja): nowa += funkcja(lista[pozycja], 0, []) if pozycja < len(lista)-1: return przejrzyj(lista, pozycja+1, nowa, funkcja) else: return nowa # przykładowe użycie **** lista = ['analityk'] print("Nasza lista z hasła, przed wywołaniem funkcji przejrzyj \n") print(lista) print ("\n") lista = przejrzyj(lista, 0, [], dodajCyfre) print ("Lista po wywołaniu funkcji przejrzyj, z parametrem dadajCyfre \n") print ( lista) print ("\n") lista = przejrzyj(lista, 0, [], zamienLitere) print ("lista po wywołaniu funkcji przejrzyj, z parametrem zamienLitere \n") print( lista ) print ("\n")
Wywołanie kodów, daje następujący wynik:
Kwesta, która nie została jeszcze zaadresowana, to zamienienie naszego programu, na skrypt, czyli możliwość jego wykonywania z parametrami. Aby zbytnio, nie komplikować. Pierwszy parametr to nasze hasło bazowe, drugi parametr to nazwa pliku, w którym chcemy zapisać wyniki.
Tym samym, początek programu, może wyglądać następująco
import sys if len(sys.argv)<2 or len(sys.argv)>3: print (""" Program wymaga co najmniej 1 parametru: 1 - hasło (wymagane) 2 - nazwa pliku w którym mają zostać zapisane wyniki. Domyślenie hasla.txt""") sys.exit() elif len(sys.argv) == 3: plik = sys.argv[2] else: plik = "hasla.txt" lista = [sys.argv[1]]
- Jeżeli nie podano żadnego parametru, lub podano więcej niż 2, program wydrukuje informację o sposobie jego wywołania
- Jeżeli jest tylko jeden argument, to nazwa pliku, w którym mają zostać zapisane hasła, jest domyślna – hasla.txt
- Hasło bazowe, jest zapisywane do zmiennej 'lista’
Samo zapisanie do pliku, może odbyć się następująco:
plik = open("./"+plik, "w")<br> plik.write('\n'.join(lista))<br> plik.close()
A nasze finalne kody źródłowe, mogą wyglądać następująco:
import sys if len(sys.argv)<2 or len(sys.argv)>3: print (""" Program wymaga co najmniej 1 parametru: 1 - hasło (wymagane) 2 - nazwa pliku w którym mają zostać zapisane wyniki. Domyślenie hasla.txt""") sys.exit() elif len(sys.argv) == 3: plik = sys.argv[2] else: plik = "hasla.txt" lista = [sys.argv[1]] def dodajCyfre (haslo, liczba, nowa): nowa.append(haslo + str(liczba)) if liczba < 9: return dodajCyfre(haslo, liczba+1, nowa) else: return nowa def zamienLitere (haslo, pozycja, nowa): if haslo[pozycja].islower(): haslo2 = haslo[0:pozycja] + haslo[pozycja].upper() + haslo[pozycja+1:] nowa.append(haslo2) if pozycja < len(haslo)-2: return zamienLitere(haslo, pozycja+1, nowa) else: return nowa def przejrzyj(lista, pozycja, nowa, funkcja): nowa += funkcja(lista[pozycja], 0, []) if pozycja < len(lista)-1: return przejrzyj(lista, pozycja+1, nowa, funkcja) else: return nowa lista = przejrzyj(lista, 0, [], dodajCyfre) lista = przejrzyj(lista, 0, [], zamienLitere) print(lista) plik = open("./"+plik, "w") plik.write('\n'.join(lista)) plik.close()
Podsumowując. Gratulacje! Napisaliśmy swój pierwszy program, z użyciem funkcji rekurencyjnych. Dodatkowo, przekazywaliśmy funkcję jako parametr.
Zdajemy sobie sprawę, że funkcje rekurencyjne, często potrafią sprawiać problemy. Wielu ludzi, nie spostrzega ich jako czegoś intuicyjnego, i potrzeba czasu, aby oswoić się z tą koncepcją.
Rekomendujemy powtórzenie ćwiczeń z lekcji o funkcjach rekurencyjnych w Python, oraz samodzielne uruchomienie mini projektu, na swojej maszynie.
W następnej lekcji, odejdziemy od funkcji rekurencyjnych, jednak zostaniemy blisko samych funkcji. Będziemy rozmawiać o mechanizmie, rozszerzającym ich funkcjonalności, zwanym – dekoratorami. Widzieliśmy ich zastosowanie, w lekcji na temat programowania obiektowego, oraz dziedziczenia, jednak nie tłumaczyliśmy czym są i w czym nam mogą pomóc. Tym właśnie, zajmiemy się w następnej lekcji. Zapraszamy!
Ćwiczenia
Ćwiczenie 1. Nasz agent, właśnie zdał sobie sprawę, że potrzebuje również wariacje hasła, zawierające znaj specjalny ’!’, umieszczony w losowych miejscach hasla. Jak może to zaimplementować?
Przykładowo: !analityK7, analit!yk4
Python, NIE tylko dla początkujących
- Wstęp do kursu
- Moduły i pakiety
- Programowanie obiektowe – wstęp
- Programowanie obiektowe – dziedziczenie
- Programowanie obiektowe – hermetyzacja
- Mini projekt – Organizer
- Funkcje rekurencyjne w Python
- Pisanie skryptów w Python
- Mini projekt – tajny agent, generator haseł <– bieżąca lekcja
- *args oraz **kwargs
- Dekoratory w Python
- Mini projekt – tajny agent – kontakty
- Odwzorowanie list
- Podsumowanie oraz dalsze kroki