O diodzie, co chodziła na skróty
08.10.2012 02:00
Nie wiem czy się ze mną zgodzicie, ale uważam, że każdy dobry administrator powinien liznąć trochę wiedzy na temat programowania i wiedzieć, w jaki sposób ułatwić sobie życie pisząc mniej lub bardziej skomplikowane skrypty tudzież miniaplikacyjki. Tak samo zresztą uważam, że każdy szanujący się programista musi wiedzieć, jak skonfigurować sobie środowisko, w którym jego aplikacja będzie działać i powinien znać jego specyfikę - na przykład programując aplikacje webowe ASP.NET, powinien móc postawić sobie IISa, wiedzieć jak na zachowanie aplikacji wpłynie różna liczba procesów roboczych albo inny kontekst puli aplikacji. Powinien też wiedzieć, jak wyciągać logi sypiącej się aplikacji z serwera ;‑)
Jeśli uważacie podobnie, to pewnie nie mieliście problemów z dzisiejszym zadaniem specjalnym ;‑) Postanowiłem na zakończenie naszego konkursu zaserwować Wam kilka minut zabawy z PowerShellem, jednym z najlepszych moim zdaniem narzędzi (platform? środowisk?), jakie Microsoft wyprodukował w ciągu ostatnich kilkunastu lat. Połączenie systemowego wiersza poleceń bezpośrednio z .NET, ale także z takimi mechanizmami jak np. WMI (notabene coraz rzadziej już używanym dzięki temu, że sam PowerShell jest coraz mocniej zintegrowany z systemem) dało niewyobrażalne wręcz możliwości. Kod łatwo się pisze, nie ma na przykład obowiązku silnego typowania, bardzo łatwo też miesza się cmdlety systemowe z metodami .NET. Ot, przejrzysty nieład. Słowem - przyjemna alternatywa dla cmd.exe, gdzie żeby pobrać prosty plik trzeba było posiłkować się zewnętrzną aplikacją, zwykle wgetem, nie wspominając o wykorzystaniu pętli typu foreach czy przetwarzaniu wyników poleceń typu dir ;‑)
No, ale dość już tego gadania. Nie każdemu PowerShell może się podobać, dlatego dzisiejsze zadanie można było rozwiązać na dwa sposoby - "z" PowerShellem i "bez". Zacznijmy od sposobu "z", aka "skomplikowanego". W końcu jak mawiał Sam Fisher, sometimes you just have to do things the hard way...
PowerShell dla opornych
Przeanalizujmy więc sytuację. Mamy do dyspozycji dwa pliki: skrypt.zip oraz dioda2.zip. Rozpakowujemy oczywiście oba, nie może być inaczej. W pierwszym znajduje się skrypt PowerShella o nazwie dioda.ps1, w drugim - dysk maszyny wirtualnej dioda2.vhdx. Od razu tłumaczę, że zadanie nie jest może przesadnie skomplikowane, ale zawiera przesadnie sporo ślepych uliczek ;‑) Jedną z nich jest wspomniany plik VHDX. Jak na pewno wiecie jako wytrawni gracze diodowi, format ten jest nowością w systemie Windows Server 2012 oraz Windows 8. Oznacza to, że aby go zamontować i zobaczyć, co jest w środku, potrzebujemy jednego z tych systemów - może być na maszynie wirtualnej. Jeśli ktoś spróbuje jednak zamontować dysk w Windows 8, spotka go niespodzianka w postaci nieobsługiwanego systemu plików - ReFS. Jest on dostępny tylko w Windows Server 2012.
Jeśli ktoś ma pod ręką maszynę wirtualną z Windows Server 2012, do czego zachęcaliśmy wielokrotnie ;‑) to nie miał problemu. Zanim jednak stracimy 30 minut na postawienie systemu tylko po to, by przeczytać głupi wierszyk ;‑) sprawdźmy lepiej, co kryje skrypt PowerShella...
Skrypt łamie wszelkie dobre praktyki czytelnego kodu ;‑) Niemniej mamy tu kilka istotnych informacji. Po pierwsze, tworzony jest obiekt .NET WebClient i za jego pomocą pobierany jest plik dioda.zip. Po drugie, katalogiem roboczym dla wszystkich operacji wydaje się być C:\dioda4ever, ale jest on zawsze na początku skryptu tworzony od nowa. Tuż przed zakończeniem z kolei odpalany jest jakiś plik exe z parametrem. Spróbujmy po prostu wykonać skrypt tak jak jest i zobaczyć, co się stanie:
Ups, wygląda na to że w naszym systemie nie zezwolono na wykonywanie niepodpisanych skryptów pochodzących z Internetu. Zmieńmy to.
Set-ExecutionPolicy Unrestricted
Teraz możemy wykonać skrypt. Niestety w skrypcie przed pętlą foreach znajduje się instrukcja cls, która czyści ekran i uniemożliwia nam obejrzenie błędów. Wykomentujmy ją zatem stawiając na początku wiersza znak #. Można też oczywiście po prostu usunąć wiersz, dobry programista tego jednak tak pochopnie nie robi ;‑)
Dwa błędy. Pierwszy w operacji $d.CopyHere i drugi przy usuwaniu pliku. Możemy teraz po kolei przechodzić przez wszystkie zmienne i podstawiać, aż dojdziemy do wniosku, że skrypt usiłuje rozpakować plik dioda2.zip. Możemy też od razu założyć, że o ten plik chodzi, patrząc na ściezkę, z której skrypt usiłuje usunąć ten właśnie plik. Tak czy inaczej, musimy go dostarczyć - najwyraźniej jest niezbędny, zresztą tak sugeruje też treść zadania. Wykomentujmy więc tworzenie i usuwanie folderu w skrypcie, a folder stwórzmy sami. Skopiujmy też do niego plik dioda2.zip. Samo skopiowanie pliku do istniejącego folderu nie wystarczy, bo skrypt przy kolejnym uruchomieniu odtworzy katalog.
Uruchamiamy skrypt i...
Naszym oczom ukazuje się właściwy wynik działania skryptu. Okazuje się, że pobierany plik dioda.zip zawiera spakowane narzędzie (exe) do generowania sum kontrolnych (hashy). Skrypt nie robi nic innego jak generuje sumę kontrolną MD5 pliku VHDX, który rozpakowywany jest z archiwum dioda2.zip. Treść zadania informuje, że odpowiedzią jest 128‑bitowy ciąg znaków. Ta definicja pasuje do hasha MD5, który właśnie z tylu bitów się składa (i przez to jest mało bezpieczny, bardzo narażony na kolizje). Jeśli ktoś wpadł na pomysł, że może chodzić o reprezentację binarną tego hasha, to z błędu powinien wyprowadzić go formularz, który nie przyjmował ciągów dłuższych niż 32 znaki. Zresztą, aż takim sadystą nie jestem - już to wielokrotnie podkreślałem ;‑) Z drugiej strony konwersja MD5 do postaci binarnej jest banalnie prosta w PowerShellu dzięki wykorzystaniu .NET.
Na skróty ze skrótem
No dobra, a jak _należało_ to zrobić? :‑P Zadanie sugerowało, że analiza skryptu PowerShella wcale nie jest potrzeba do rozwiązania zadania. Można było wziąć plik VHDX i iść na skróty. Skrót nawiązuje tu oczywiście do hasha, czyli funkcji skrótu :‑) Inaczej mówiąc, to zdanie informuje, że wystarczy wygenerować hash pliku VHDX. Jaki hash? 128‑bitowy :‑) Pytanie tylko, czy na pewno chodzi o MD5, bo 128‑bitowych hashy jest kilka. Tu nie ma żadnej podpowiedzi. Żaden algorytm generujący 128‑bitowy hash nie jest jednak tak popularny jak MD5. Na tym polegało owo ryzyko. Kto je podjął, zyskał zapewne sporo czasu. Była to doskonała furtka dla osób z dalszych miejsc w rankingu, którzy w ten sposób mogli postawić wszystko na jedną kartę i dużo zyskać.
Ale, ale, Panie i Panowie, strzelać trzeba z głową ;‑) Niestety wielu uległo pokusie bez uprzedniego namysłu i poszło na skróty "za bardzo". W efekcie część z Was podawała jako odpowiedź hash MD5, ale nie tego pliku co trzeba. Trafiło się kilka hashy pliku ZIP zawierającego dysk VHDX (a wyraźnie mowa była o pójściu na skróty z samym plikiem VHDX). Niektórzy hashowali... aplikację hashującą :‑) Część z Was podawała jeszcze inne sumy, być może nawet właściwego pliku: ale np. po jego zamontowaniu i odmontowaniu w systemie, co powoduje niestety modyfikację pliku dysku i zmianę hasha... Tymczasem skrypt celowo za każdym razem na nowo rozpakowywał plik, aby zagwarantować integralność pliku VHDX. No cóż, zapewne po szybkim wysłaniu pytania i posiedzeniu nad PowerShellem kilka(naście) kolejnych minut, wielu pluło sobie w brodę... Przyznać się, kto od razu wygenerował poprawny hash MD5 pliku VHDX bez korzystania ze skryptu? ;‑) A kto bez oglądania skryptu? ;‑))))
Niektórzy próbowali też iść na "częściowe skróty" i po ujrzeniu adresu http://dioda4ever.dobreprogramy.pl/dioda.zip w skrypcie, wpisywali ten adres po prostu w przeglądarkę tylko po to, by otrzymać błąd 404. No cóż, przydał się tu moduł Request Filtering w IIS, w którym wycięliśmy wszystkie wywołania z nagłówkiem User-Agent większym od 0. WebClient domyślnie go nie wysyła, Wasze przeglądarki w większości tak :‑)
I to by było na tyle...
Bardzo dziękuję wszystkim za wspólną zabawę, bo przyznam, że przy zadaniach specjalnych bawiłem się - mam nadzieję - równie dobrze jak Wy ;‑) Na oficjalne rozstrzygnięcie i gratulacje przyjdzie naturalnie czas, ale już teraz na łamach bloga gratuluję: nie tylko zwycięzcom, ale przede wszystkim tym, którzy dotrwali do końca tego wyjątkowego, nieraz dającego mocno w kość konkursu :‑))))
Pamiętajcie: dioda4ever! :-)