misja016 blog head

Misja Gynvaela 016

To jest trochę dziwne, rozwiązywać misję przygotowaną przez samego siebie, ale jeśli nikomu by się rozwiązywać nie chciało, to przynajmniej jedno rozwiązanie będzie 🙂

 

Misja opublikowana po polskim streamie #55PL oraz po angielskim streamie #42EN.

(link do misji po angielsku)

 

Mikołaj z jelonkiem

W pobranym pliku zip znajduje się właściwy plik ze strumieniem stream.bmp. Jak możemy się domyślić po rozszerzeniu, jest to bitmapa. Na rysunku 1 umieściłem oryginalny jej obraz (kliknij i powiększ).

misja016 stream original
Rysunek 1. Obraz pliku “stream.bmp”.

 

Klimat świąteczny, Mikołaj z jelonkiem przy choince. U dołu natomiast znajdują się jakieś postrzępione fragmenty screenów. Omówię je później. Po powiększeniu wygląda to jak bitmapa monochromatyczna. W miejscach gdzie występuje kolor, zapalony jest co drugi piksel (rysunek 2bcd).

misja016 stream zoom
Rysunek 2. Plik “stream.bmp” zoom 50%, powiększenie w trzech miejscach.

 

Standardowo, gdy mam do czynienia z bitmapą, stosuję kilka filtrów (levels, curves), aby zobaczyć czy są ukryte jakieś kolory. W tym przypadku na całej bitmapie występują tylko 2, kolor biały (#FFFFFF) i granatowy (#000080).

Przejdźmy do formatu. Rozmiar 1200×1083, 8 bitów na piksel (liczba kolorów 256). Ooo… to już jest jakiś trop. Taka bitmapa z reguły zawiera paletę kolorów. Każdy piksel w pliku to jeden bajt, natomiast paleta definiuje barwę dla każdej jego wartości (rysunek 3).

misja016 bitmap palette explain
Rysunek 3. Źródło: Pixel Data and Palettes

 

Ciekawe co się stało z pozostałymi kolorami?

Otwieram bitmapę w GIMP’ie, a następnie wybieram menu Kolory→Odwzorowania→Przełóż paletę kolorów… (Color→Map→Rearrange Colormap…), aby podglądnąć paletę kolorów (rysunek 4). Teraz wiadomo dlaczego nie ma innych kolorów. Wszystkie wartości palety zostały ustawione na kolor biały. Tylko wartość 128 została ustawiona na kolor granatowy. Zobaczmy zatem co kryje się pod innymi wartościami. W tym celu wybieram Kolory→Odwzorowania→Ustaw mapę kolorów… (Color→Map→Set Colormap…), a następnie wybieram paletę na jaką chcę zamienić (rysunek 5). Teraz już widać (rysunek 6), że bitmapa nie składa się z pikseli o tej samej wartości.

misja016 gimp rearrange colormap
Rysunek 4. GIMP – Podgląd palety.
misja016 gimp set colormap
Rysunek 5. GIMP – Podmiana palety.
misja016 gimp set colormap3
Rysunek 6. GIMP – Wynik podmiany palety.

Nie wiem jak w GIMP’ie zmienić pojedynczy kolor. Na szybko znalazłem tylko jak podmienić całą paletę. Do tej pory z paletą bawiłem się tylko w Photoshopie. Trzeba tu wybrać menu Image->Mode->Color Table… (Obraz->Tryb->Tablica kolorów).

Tu mamy możliwość wybrania koloru dla każdej wartości z palety (rysunek 7). Dodatkową zaletą jest to, że wynik podmiany jest widoczny od razu, zaraz po kliknięciu w próbniku kolorów. Nie tak jak w przypadku GIMP’a dopiero po zamknięciu okna dialogowego.

misja016 photoshop
Rysunek 7.

Można w ten sposób podmienić poszczególne kolory i zobaczyć czy jest coś ciekawego. No ale znaleźć ten jeden kolor, który mógłby coś zawierać to byłoby trudno.

Wnętrze pliku

Moim ulubionym narzędziem do podglądu plików jest Lister z Total Commander’a. Już od czasów Norton Commander’a (NC Text View HEX) bardzo lubiłem zaglądać do wnętrza plików. Nie jestem pewien czy rozwiązanie tej misji zacząłbym od grzebania w palecie kolorów. Podejrzewam, że nie, ale chciałem zamknąć jedną z możliwych ścieżek do sprawdzenia.

Jestem przekonany, że w początkowym obrazie od razu rozpoznałbym screeny z Listera. Znajdują się tam dwie pomocnicze informacje. Można rozpoznać nagłówek pliku ZIP, czyli "PK" (oczywiście plik ZIP nie musi się zaczynać od PK, ale w większości przypadków tak jest) oraz nagłówek pliku dźwiękowego "RIFF....WAVEfmt" (rysunek 2).
Uruchamiam Listera, zjeżdżam trochę niżej i widzę to co na rysunku 8 i 9.

misja016 lister pk
Rysunek 8.
misja016 lister riff
Rysunek 9.

 

Na wszelki wypadek próbuję jeszcze użyć binwalk'a, licząc na to, że zrobi za mnie brudną robotę i wyciągnie odpowiednie pliki.

 

Niestety w katalogu “_stream.bmp.extracted” znalazłem tylko plik “514.zip”. Dziwne jest to, że jest taki duży, bo ma ponad 1 MB. Explorer windowsowy mówi, że “folder skompresowany … jest nieprawidłowy”, ale Total Commander bez problemu daje radę.
Wewnątrz pliku znajduje się katalog mixerpack zawiera 4 pliki: Decoder, Encoder, Helper, Tester. Niestety nie można zobaczyć co zawierają, bo wymagają podania hasła.
Binwalk mnie zawiódł, bo nie wyeksportował pliku dźwiękowego, a powinien bo nagłówek ewidentnie tam jest. Muszę zatem to zrobić ręcznie.

Hex Editor Neo jest świetnym narzędziem do tego, o czym pisałem wcześniej (Misja Gynvaela 013 cd.), pomimo tego, iż skubany okazał się 30-dniową wersją pro i teraz ograniczył trochę swoją funkcjonalność.

Kolega z pracy podsunął jeszcze inny prosty sposób. Jeśli posiadasz Notepad++ to wystarczy, że otworzysz plik, usuniesz wszystko przed “RIFF”, zapiszesz i masz WAVE’a (rysunek 10). Notepad++ pozytywnie mnie zaskoczył. Nie zamienia on znaków tak jak inne edytory (zamieniają np. znak końca wiersza, twardą spację na miękką, itp.).

misja016 notepad++
Rysunek 10.

 

OK. Mam plik WAVE, więc uruchamiam Audacity. Uszom moim ukazuje się szum, z przerwami, z bipczeniem. Po poprzednich misjach wiemy już, że w takich sytuacjach warto przełączyć się na widok spektrogramu.

Wyjdę trochę poza ramy rozwiązania misji i powiem, że ciężko było przygotować ten spektrogram, tak aby od razu dobrze się wyświetlał. Co ciekawe zaktualizowałem sobie ostatnio Audacity z wersji 2.1 do 2.2 i okazuje się, że zmienili domyślne ustawienia spektrogramu. Dlatego też ludziom ze starszą wersją będzie się wyświetlać jak na rysunku 11, a z nowszą wersją jak na rysunku 12.

Kontynuując, ktoś kto grzebał już w ustawieniach spektrogramu powinien rozpoznać, że na początkowej bitmapie (rysunek 2), po prawej stronie znajduje się fragment okna ustawień. Można z niego odczytać jakie powinny być ustawienia. Te, które trzeba zmodyfikować to: maksymalna częstotliwość 24000 (zmniejszeniem skali pionowej), wzmocnienie częstotliwości 15 (zwiększenie kontrastu)  i rozmiar okna 1024 (rozdzielczość pionowa). O tych właśnie ustawieniach mówi wskazówka Hint3 oraz Hint5. Tak jak pisałem wcześniej głównie jest dla starszej wersji Audacity, gdzie okno ustawione jest na 256.

misja016 wave spektrogram audacity 21
Rysunek 11. Spektrogram Audacity 2.1.
misja016 wave spektrogram audacity 22
Rysunek 12. Spektrogram Audacity 2.2.

misja016 wave audacity

misja016 wave spektrogram ho ho ho
Rysunek 13. Spektrogram – okno Hanning 1024 + wzmocnienie częstotliwości.

 

HO HO HO!

Po wprowadzeniu zmian ukazuje nam się obraz jak na rysunku 13. Dodatkowo zmieniłem jeszcze zakres (dB) na 110, żeby lepiej było widać renifery. Kolejny świąteczny motyw. Mikołaj w saniach ciągniętych przez renifery w zaprzęgu, a to wszystko nad chmurami na tle księżyca. To jednak jest tylko lewy kanał dźwięku, natomiast na prawym kanale widoczny jest tylko szum. Co prawda na rysunku 12 widać jakieś zarysy czegoś, ale nie jest to czytelne (przynajmniej tak mi się zdaje). A własnie… Nie powiedziałem jeszcze nic o formacie dźwięku. Jest to zwykły plik WAVE o częstotliwości 48000 Hz, 2 kanały, głębia 16-bitów. Każda próbka zajmuje zatem 4 bajty, po 2 bajty na kanał.

No i teraz może nasuwać się pytanie, plik WAVE jest fragmentem pierwszej bitmapy. Jak widać na rysunku 2, autor narysował na nim obraz. Jednak ten obraz składa się z pikseli oddalonych od siebie (rysunek 2bcd). Może gdybyśmy usunęli piksele to obraz na drugim kanale byłby wyraźniejszy? Odpowiedź na to pytanie znajduje się w pierwszej podpowiedzi Hint1: Pliki są zapisane ciągiem, nie są pofragmentowane. Zatem nie ma sensu dalej drążyć tego tematu. Trzeba znaleźć coś innego.

Mój wzrok przykuwają białe punkty znajdujące się obok księżyca. Na rysunku 14 pokazałem powiększenie. Czyż to nie jest przypadkiem kod morsa? Wygląda to jakby ktoś poszczególne litery przekręcił o 90 stopni.

misja016 wave spektrogram mors
Rysunek 14. Kod morsa ukryty w spektrogramie.

Zapisuję więc znaki od góry do dolu, od lewej do prawej:

.-. ..- -.. --- .-.. ..-. | .-. . -.. -. --- ...

i wchodzę na stronę Morse Code Translator. Output:

"RUDOLF REDNOS"

Znaki zapisane od dołu do góry dają napis: "RDUOFL REUAOS"

Drugi napis nie jest sensowny, za to pierwszy myślę, że jak najbardziej. Nie wiem, czy ktoś jeszcze to pamięta, ale była kiedyś bajka Rudolf Czerwononosy  🙂 Nie jest to jednak flaga misji, gdyż Hint2 mówi o tym, że flaga jest wyraźnie oznaczona.

Do czego może się przydać ten tekst, hmm czyżby było to hasło do archiwum?

Bingo!

 

Zawartość plików z archiwum

Teraz mogę zobaczyć wnętrze plików:

Specjalnie nie podałem treści pliku Decoder. W pierwszej wersji misji miał on być całkowicie nieczytelny, ale foxtrot zasugerował mi, że to może być trochę irytujące i trudne do napisania. Zostawiłem więc zawartość i zmieniłem tylko fragment. Można o tym poczytać w innych writeup’ach. Załóżmy jednak, że ja mam wersję z popsutą treścią Decode.

Nie znamy zatem implementacji funkcji Decode. Jednak jeśli dobrze się przyjrzymy to zobaczymy, że jest tam napis InvertLUT. Funkcja o takiej nazwie istnieje w pliku Helper. Być może wynika z tego, że funkcja Decode z niej korzysta. Zobaczmy co jest dalej.

W pliku Encoder znajduje się funkcja Encode. Najprawdopodobniej posłużyła ona do zakodowania drugiego kanału w pliku z dźwiękiem. Sądzić tak można po tym, że parametrem (dzięki Gutek) jest ciąg bajtów nazwany wav, a wyrażenie if ((p & 2) != 0) mówi: weź co drugi short. Dodatkowo jest tam parametr off, czyli najprawdopodobniej offset (przesunięcie) oraz zagadkowy parametr LUT.

Kolejny plik Helper, a w nim funkcja InvertLUT także pobierająca parametr LUT. Najważniejszy jest jednak komentarz do funkcji: “Inverting Look Up Table for decoding”. W tym momencie wiadomo już, co oznacza skrót LUT.

 

Tablicowanie czyli Look Up Table

Tablica LUT służy do przyspieszania obliczeń (Wikipedia – Tablicowanie)(Korekcja gamma). Zapisuje się w niej wszystkie wyniki danej funkcji dla wejściowego zakresu danych. Dane wejściowe muszą być w takim formacie, aby dało się je w łatwy sposób przekształcić na indeks tablicy. Gdy już mamy wypełnioną tablicę to wynik uzyskujemy błyskawicznie poprzez skok do odpowiedniego indeksu tablicy.

Wynik przekształcenia wartości zapiszmy tak:

Funkcja Encode używa takiego właśnie sposobu do zakodowania drugiego sygnału. Jak zatem odtworzyć funkcję Decode? Trzeba sobie zadać pytanie: Jak odwrócić przekształcenie? Skoro dane wejściowe d zostają użyte jako indeks tablicy, uzyskując w ten sposób wartość q, to operacja odwrotna będzie polegała na znalezieniu indeksu d, pod którym znajduje się wartość q w tablicy.

Czyli teraz już wiemy, że funkcja Decode będzie wyglądać podobnie jak Encode, z tą różnicą, że w miejscach gdzie jest LUT[] damy LUT.IndexOf().

Jednak… można tę operację zoptymalizować. Pytanie, czy dla tej samej wartości q funkcja IndexOf zwróci ten sam index?
TAK. Czy to nie jest przypadkiem pole do zastosowania tablicy LUT?
TAK! Wystarczy zatem obliczyć wynik dla wszystkich wartości q i zapisać w tablicy, a następnie użyć zamiast funkcji szukającej indeksu LUT.IndexOf().

Dokładnie takie działanie prezentuje funkcja InvertLUT z pliku Helper.
Pobawmy się tym razem Python’em.

Funkcję InvertLUT przepisałem na Pythona i wymyśliłem jak powinna wyglądać funkcja Decode.

 

Poszukiwanie tablicy LUT

Ok, ale nadal nie wiemy jaka tablica została użyta w tej misji?
Pierwsza wskazówka to rozmiar. Do tablicy podawana jest zmienna typu byte. Może przyjmować zatem 256 różne wartości, więc tablica musi posiadać co najmniej 256 elementów, co potwierdza funkcja InvertLUT.

Zaglądam do pliku Tester – ostatniego. Jest tam funkcja TestEncodeDecode(), a w niej funkcja pobierająca tablicę LUT z pliku “stream.bmp”. Niestety nie wiemy jak ona wygląda, bo nie ma jej w plikach archiwum. Jednak jej nazwa jest bardzo sugestywna: LoadLUTFromPalletteAlphaChannel("stream.bmp")

No właśnie, przecież paleta kolorów w bitmapie indeksowanej jest idealnym przykładem zastosowania Look-Up Table. Jest to także widoczne na rysunku 3. Nazwa sugeruje, że dane znajdują się w kanale Alfa. W większości przypadków kanału Alfa nie używa się w bitmapach indeksowanych, dlatego programy graficzne wyświetlają tylko RGB.

misja016 palette in hex
Rysunek 15. Fragment palety kolorów w pliku “stream.bmp”.

 

Przyjrzyjmy się zatem palecie. Zaczyna się ona od bajtu 54 (rysunek 14). Zgodnie z tym co sugerowała nazwa funkcji, czwarty bajt koloru, zawiera różne liczby (RGBQUAD structure). Normalnie powinna być tam taka sama wartość dla całej palety, np. 0xFF. Na rysunku zaznaczyłem także niebieski kolor po środku palety, ten kolor był widoczny w programach graficznych.

Kopiujemy do tablicy.

Teraz już chyba wszystko mamy. Ostatnie pytanie. Od jakiego bajtu zacząć, czyli jaki argument podać do parametru off? Dane pliku WAVE, o ile nie posiada dodatkowych chunk’ów, zaczynają się od bajta 40. Dla przypomnienia można sobie zerknąć na plakat corkami – a mini wave binary. Dodatkowo w funkcji TestEncodeDecode() funkcje testowane są z argumentem off=40.

 

Solution

Chwila prawdy.

Uruchamiam…

Victory!

misja016 wave solution

misja016 solution
Rysunek 16. Flaga.

 

FLAGA 🚩: Merry Hacker Christmas!

GitHub


Na zakończenie dodam jeszcze jedno. Od czasu kiedy zacząłem tworzyć misję minęło sporo czasu. W tym czasie natknąłem się na pewien artykuł o formacie BMP. Byłem zaskoczony ile można w nim jeszcze namieszać 😀

Link do strony z artykułem magazynu hakin9: Format BMP okiem hackera (bezpośrednio do artykułu PDF).

Są tam także inne zajefajne artykuły Hakin9 & Linux+.

 


W najbliższym czasie planuję jeszcze napisać 4 posty:

  • Źródło inspiracji do misji
  • Jak powstała misja 016
  • Rysowanie spektrogramu
  • Rysowanie po spektrogramie

Post Author: marbel82

1 thought on “Misja Gynvaela 016

    Gutek

    (20 stycznia 2018 - 12:03)

    Ależ nie ma za co 🙂
    Moje gratulacje rozwiązania 🙂 pokręcone ostro!

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

This site uses Akismet to reduce spam. Learn how your comment data is processed.