Programowanie wątków - komunikacja i synchronizacja

Zadanie

Zadaniem będzie napisanie w ANSI C wielowątkowego współbieżnego programu do łamania zahaszowanych haseł metodą słownikową. Zakładamy, że hasła są generowane metodą skrótów kryptograficznych MD5. Ze względu na dużą ilość obliczeń, program (wątek) główny będzie generował szereg wątków roboczych, i koordynował ich pracę nad łamaniem haseł.

Organizacja programu

Program łamania haseł powinien być zorganizowany według analizowanego wcześniej schematu producentów i konsumentów. Zadaniem wątku głównego jest wczytanie do pamięci globalnej programu podanej przez użytkownika listy zahaszowanych haseł do łamania, oraz podanego (lub domyślnego) słownika. Jedno i drugie jest listą słów, przy czym słownik może zawierać nawet bardzo dużą liczbę słów różnej długości, i powinien być załadowany do pamięci dynamicznej zaalokowanej funkcją malloc o dokładnie określonym rozmiarze. Natomiast lista zahaszowanych haseł jest typowo mniejsza (można przyjąć maksymalnie 1000 haseł) i będą one miały stałą długość 32 znaków, zatem może dla nich być użyta tablica statycznie alokowana.

Następnie wątek główny powinien uruchomić zdefiniowany zestaw wątków ,,producentów''. Zadaniem każdego z producentów jest próba złamania kolejno wszystkich zadanych haseł określoną metodą. Każdy z producentów będzie implementował inną metodę budowy haseł, w celu zrównoleglenia procesu ich łamania. Każdy producent będzie po kolei budował próbne hasła swoją metodą, następnie obliczał 32-znakowy skrót kryptograficzny MD5 każdego hasła, i porównywał ten skrót z całą listą zahaszowanych haseł. Jeśli któreś hasło zgodzi się z obliczonym haszem, to oznacza, że znalezione zostało hasło źródłowe. W takim przypadku należy poinformować wątek konsumenta, który zarejestruje złamane hasło i oznaczy odpowiednie hasło w tablicy globalnej, aby nie było już ono dalej sprawdzane.

Implementacja producentów

Podatne na złamanie metodą słownikową hasła są budowane ze słów danego języka, wraz z zestawem nazw własnych tego języka (imion, popularnych nazwisk, nazw geograficznych, itp.), z uwzględnieniem pewnych modyfikacji.

Przykładami modyfikacji mogą być: słowo zapisane tylko małymi lub tylko wielkimi literami, albo pierwszą wielką i pozostałymi małymi, słowo z dodatkiem jakiejś liczby na początku lub na końcu, albo z dodatkiem jakiegoś znaku interpunkcji na początku lub na końcu. Możliwe są również połączenia tych modyfikacji (np. wielka litera na początku, reszta słowa małymi literami, potem jakiś znak interpunkcji i potem liczba).

Prowadzący zajęcia określi jakich producentów należy obowiązkowo zaimplementować w danej grupie ćwiczeniowej.

Wątek konsumenta

Wątek konsumenta powinien natychmiast wyświetlać na ekranie każde kolejno znalezione złamane hasło w miarę otrzymywania ich od producentów. Warto wykorzystać w tym celu mechanizm zmiennej warunkowej.

Dodatkowe wymagania

Przez cały czas pracy program (wątek) główny powinien czytać stdin gdzie użytkownik może w dowolnym momencie wpisać nazwę nowego pliku haseł do łamania. Gdy to się zdarzy, program główny powinien wykonać ,,reset'', tzn. spowodować żeby zarówno producenci jak i konsument zarzucili pracę nad poprzednimi hasłami i zaczęli pobierać i łamać nowe.

Dodatkowo, program powinien obsługiwać ,,przerwanie'' od użytkownika. Po otrzymaniu sygnału SIGHUP przez program, wątek konsumenta powinien wyświetlić podsumowanie osiągniętych wyników dla bieżącego pliku haseł.

Wykonanie zadania

Zadanie powinno być wykonane w ciągu dwóch kolejnych terminów zajęć.

Pierwszy termin - implementacja prostych producentów

W pierwszej kolejności należy zaimplementować i uruchomić najprostsze konstrukcje producentów (generatorów haseł), oraz przetestować ich na małym zbiorze haseł testowych z minimalnym słownikiem. Szczegółowe instrukcje i zbiory danych do tej części ćwiczenia dostępne są na stronie:
https://kcir.pwr.edu.pl/~krzewnicka/?page=haszowanie

Zad.1 (3 punkty - na zajęciach)

Napisz program, który wczyta do zwykłej tablicy w programie plik słownika podany w punkcie "Test 1" i --- wykorzystując funkcję pokazaną we wskazówce w powyższym linku --- będzie obliczał hasze MD5 kolejnych słów ze słownika i porównywał je z zadanym zahaszowanym hasłem, wyświetlając wynik w przypadku wykrycia dokładnej zgodności.

Zahaszowane hasła powinny być załadowane z pliku do drugiej tablicy w programie. Program powinien w zewnętrznej pętli generować kolejne hasze słów ze słownika, i w wewnętrznej pętli każdy wygenerowany hasz porównywać ze wszystkimi haszami haseł.

Wynikiem powinno być znalezienie wszystkich haseł pokazanych w "Test 1".

Zad.2 (2 punkty - na zajęciach, lub 1 punkt - w domu)

Zaimplementuj łamanie haseł metodą opisaną w punkcie "Test 2". Do każdego kolejnego słowa ze słownika program powinien po kolei dopisywać liczby jedno- i dwucyfrowe na początku, na końcu, oraz na początku i końcu każdego słowa.

Wynikiem powinno być znalezienie wszystkich haseł pokazanych w "Test 2".

Zad.3 (2 punkty - na zajęciach, lub 1 punkt - w domu)

Zaimplementuj łamanie haseł metodą opisaną w punkcie "Test 3". Dla każdego kolejnego słowa ze słownika program powinien po kolei haszować wersję samymi małymi literami, wszystkimi wielkimi, oraz pierwszą wielką i resztę małymi. Ta wersja powinna działać w dodatku do wersji z poprzedniego zadania, czyli do wszystkich wersji pisowni danego słowa należy dopisywać liczby.

Wynikiem powinno być znalezienie wszystkich haseł pokazanych w "Test 3".

Zad.4 (2 punkty - na zajęciach, lub 1 punkt - w domu)

Należy zmodyfikować program w celu obsługi dużego słownika, takiego jak podany w punkcie "Test 5" zadania. W tym przypadku nie można już tworzyć zwykłej tablicy, tylko należy zaalokować pamięć na stercie funkcją malloc i do niej załadować słownik.

Zad.5 (3 punkty - w domu)

Należy zaimplementować program w docelowej wielowątkowej postaci. Poszczególne metody łamania haseł powinny być uruchomione jako niezależnie pracujące wątki producentów. W momencie złamania hasła przez jednego z producentów powinien on poinformować wątek konsumenta, który zaznaczy hasło jako złamane blokując jego dalsze sprawdzanie, a następnie wyświetli znalezione hasło na wyjściu. Program powinien również wyświetlić pełną listę złamanych haseł po otrzymaniu sygnału SIGHUP, a także w momencie zakończenia pracy, gdyby wszystkie możliwości sprawdzania wszystkich haseł zostały wyczerpane.

Drugi termin - zadania do wykonania

Zad.6 (8 punktów - wyłącznie na zajęciach)

W drugim terminie będzie kontynuacja zadania w zakresie ogłoszonym bezpośrednio na zajęciach przez prowadzącego. Będzie to zadanie do wykonania wyłącznie na zajęciach. Konieczne będzie posiadanie uruchomionej i działającej przynajmniej najprostszej wersji programu do łamania MD5 (Zad.1 powyżej).

Materiały źródłowe

Implementacja algorytmu MD5

Wersję źródłową algorytmu do obliczania skrótów MD5 można znaleźć na stronie Wikipedii:
http://en.wikipedia.org/wiki/MD5#Simple_implementation

Implementacja obliczania skrótów MD5 znajduje się również w bibliotece OpenSSL, która z dużym prawdopodobieństwem jest zainstalowana na każdym komputerze Unix/Linux. Przykładowy program obliczający skóty kryptograficzne różnymi algorytmami można znaleźć na stronie:
https://www.openssl.org/docs/crypto/EVP_DigestInit.html

Źródła słowników:

https://www.ef.com/wwen/english-resources/english-vocabulary/top-3000-words/
https://www.mit.edu/~ecprice/wordlist.10000
https://www.wordgamedictionary.com/english-word-list/download/english.txt
https://sjp.pl/sl/growy/
https://sjp.pl/sl/growy/sjp-20231203.zip

Publikowane zbiory haseł

Zbiory przykładowych haseł do łamania można znaleźć na stronie:
https://twitter.com/PastebinLeaks

Przykładowe zbiory haseł:
https://pastebin.com/JhN64Qkc
https://pastebin.com/Ef5gXBrU
https://pastebin.com/bwT3T0M7
https://pastebin.com/GuRjzY3Z

Inne przydatne zasoby

https://crackstation.net/
http://www.openwall.com/john/