DRM w systemie operacyjnym CROOK-5: Różnice pomiędzy wersjami

Z MERA 400 wiki
Przejdź do nawigacji Przejdź do wyszukiwania
Linia 177: Linia 177:
* załaduj zawartość '''A2''' (=A3+C) i oblicz '''A3''',
* załaduj zawartość '''A2''' (=A3+C) i oblicz '''A3''',
* załaduj zawartość '''A2+1''' (=A1+C) i sprawdź poprawność '''A1''',
* załaduj zawartość '''A2+1''' (=A1+C) i sprawdź poprawność '''A1''',
* załaduj zawartość '''A3''' (=A2+C0 i sprawdź poprawność '''A2''',
* załaduj zawartość '''A3''' (=A2+C) i sprawdź poprawność '''A2''',
* załaduj zawartość '''A3+1''' (=SYSID) i zapisz tymczasowo w rejestrze,
* załaduj zawartość '''A3+1''' (=SYSID) i zapisz tymczasowo w rejestrze,
* załaduj zawartość '''A3+2''' (=S+C), oblicz '''S''', załaduj wartość znajdującą się pod tym adresem, dodaj do niej różnicę '''A2-A1''' i zapisz ponownie pod '''S'''.
* załaduj zawartość '''A3+2''' (=S+C), oblicz '''S''', załaduj wartość znajdującą się pod tym adresem, dodaj do niej różnicę '''A2-A1''' i zapisz ponownie pod '''S'''.

Wersja z 07:33, 26 lut 2016

Przy okazji poszukiwań przyczyny nieprawidłowego działania niektórych narzędzi pod kontrolą systemu operacyjnego CROOK-5, odnaleziony został wbudowany w niego złożony mechanizm pozwalający ograniczać uruchamianie programów wyłącznie do maszyn, dla których autoryzowano daną ich kopię.

Jak się okazało, historia odnalezionych zabezpieczeń sięga dalej, zarówno w czasie jak i przestrzeni, niż piąta wersja systemu operacyjnego z Politechniki Gdańskiej.

Pochodzenie

Wszystko zaczęło się w Instytucie Informatyki Uniwersytetu Warszawskiego. Pracujące tam MERY-400 działały pierwotnie pod kontrolą systemu operacyjnego SOM-3. Z czasem, kiedy rozwijane w Instytucie oprogramowanie dojrzało do stanu, w którym można je było oferować odpłatnie potencjalnym klientom, pojawił się problem: jak radzić sobie z nielegalnymi kopiami? Rozwiązaniem stała się modyfikacja SOM-3, implementująca stosowne „mechanizmy obronne”.

Rosnąca popularność dojrzałego już wtedy i znacznie nowocześniejszego niż SOM-3 systemu CROOK spowodowała, że narzędzia powstałe w IIUW musiały w końcu zostać przeniesione i na tę platformę. Warunkiem koniecznym było jednak zachowanie istniejących już zabezpieczeń. I tak, po rozbudowaniu oryginalnego rozwiązania o nowe elementy oraz ustaleniu przez zespoły z UW i PG jego ostatecznego kształtu, CROOK wzbogacił się o mechanizm, którego złożoność wygląda imponująco nawet po 30 latach.

Konstrukcja systemu zabezpieczeń

Trzeba na wstępie zaznaczyć, że do realizacji celu nie zostały użyte metody kryptograficzne. Ograniczona moc obliczeniowa powodowała, że jedyną praktyczną linią obrony była wtedy niejawność użytych rozwiązań. Według dzisiejszych standardów takie „security by obscurity” nie jest metodą zapewniania bezpieczeństwa. Należy jednak pamiętać, że dostępne ówcześnie metody i narzędzia analizy były mniej zaawansowane, przepływ informacji utrudniony, a ilość systemów komputerowych nieporównywalnie mniejsza. To powodowało, że zastosowana protekcja przez długi czas mogła spełniać swoje zadanie.

System zabezpieczeń używany przez CROOK-5 składa się w swoim ostatecznym kształcie z następujących elementów:

  1. Sprzętowy identyfikator maszyny, unikalny dla każdego systemu MERA-400 (MX-16), na którym instalowany był CROOK.
  2. Zbiór zasad opisujący jak powinien zostać przygotowany program binarny mający wykorzystywać zabezpieczenia.
  3. Wbudowany w system loader zabezpieczonych programów.
  4. Wbudowane w system mechanizmy obrony przed korzystaniem z nieautoryzowanych kopii programów.
  5. Narzędzie, za pomocą którego binaria autoryzowane były dla maszyny o danym identyfikatorze.

Warto w tym miejscu zwrócić uwagę na to, jaki powyższy zbiór sugeruje podział na „obszary wtajemniczenia” pomiędzy poszczególnymi uczestnikami procesu. Było to jedną z metod realizacji wspomnianej wcześniej niejawności.

  • Twórcy systemu operacyjnego nie potrzebowali identyfikatorów sprzętowych maszyn klientów, aby przygotować dla nich system. Mogły one być przechowywane w innym ośrodku i tam niezależnie zarządzane.
  • Programiści również nie potrzebowali (i nie powinni byli znać) identyfikatorów maszyn klientów, do których było dystrybuowane oprogramowanie. Nie znali też algorytmu autoryzacji binariów i nie mieli środków (narzędzi) do tego celu.
  • Jedynie „centrum autoryzacji” dysponowało identyfikatorami maszyn oraz narzędziem autoryzacyjnym, co pozwalało na przygotowanie kopii programu przeznaczonej dla konkretnego klienta.
  • Klient końcowy nie był świadomy faktu zabezpieczenia programu (nie podawał np. żadnego klucza podczas instalacji). Nie wiedział też o istnieniu sprzętowego identyfikatora. Jeśli natomiast próbował użyć programu nie przeznaczonego dla jego maszyny, zabezpieczenie manifestowało się w sposób sugerujący raczej niesprawność systemu lub oprogramowania, niż swoje istnienie.

Identyfikator systemu

Pierwszym, kluczowym elementem mechanizmu i jednocześnie tym, co odróżnia wersję zabezpieczenia przygotowaną dla CROOK-a od pierwotnej, dla SOM-3, jest unikalny sprzętowy identyfikator systemu. Potocznie zwany był „numerem procesora” lub „numerem zegara”, w zależności od rodzaju implementacji. Obie jego wersje zrodziły się w Instytucie Okrętowym Politechniki Gdańskiej i tam też powstało pierwsze jego wcielenie. Służy do jednoznacznego zidentyfikowania maszyny, na której uruchamiany jest zabezpieczony program. W systemie operacyjnym przechowywany jest w zmiennej nazwanej w źródłach CROOK-a PROCNU.

Numer zegara

Pamięci PROM na zegarze czasu rzeczywistego

W pierwszej, nieco starszej wersji rozwiązania, identyfikator schowany był w zegarze czasu rzeczywistego. Urządzenie to, instalowane jako kolejna jednostka w kanale znakowym, dysponuje również pamięcią PROM, na której zapisany jest bootloader pozwalający „ściągnąć” system operacyjny z dysku do pamięci komputera (poprzez mechanizm wczytywania binarnego). Tuż za znacznikiem końca kodu bootloadera zapisany jest identyfikator systemu, zwany wtedy (ze względu na swoją lokalizację) „numerem zegara”.

Po uruchomieniu systemu CROOK-5 za pomocą tego bootloadera, sprzętowy licznik adresujący kolejne bajty w pamięci PROM zegara nie zmieniał swojej wartości. Dzięki temu, wysyłając do urządzenia kolejne rozkazy czytania, można kontynuować pobieranie danych. Identyfikator systemu zapisany jest na trzech kolejnych bajtach pamięci PROM. Ich cztery najmłodsze bity zawierają kolejne (od najstarszej) cyfry identyfikatora zapisane w kodzie BCD.

Pierwszy bajt:

P W - K a3 a2 a1 a0

Drugi bajt:

P W - K b3 b2 b1 b0

Trzeci bajt:

P W - K c3 c2 c1 c0

Znaczenie poszczególnych bitów to:

  • P – bit parzystości
  • W – bit ważności
  • K – znacznik końca
  • a, b, c – kolejne cyfry identyfikatora

Bit K we wszystkich bajtach musi mieć wartość 1. Identyfikator po złożeniu ma postać:

PROCNU = a*100 + b*10 + c

Numer procesora

EEPROM pamięci MEGA

Późniejsze, ostateczne rozwiązanie używa do przechowywania identyfikatora ostatniego słowa pamięci PAS (zrealizowanej na kościach EEPROM) w pamięci MEGA Amepolu. System operacyjny wczytuje go podczas startu:

(2) OR   r0, ?1
    RW   r5, RROB+1
    ...
(1) LW   r5, [-1]
    ...
    ...
(3) LW   r4, [RROB+1]
    BRC  ?1
    RW   r4, PROCNU

Identyfikator ładowany jest rozkazem (1) ukrytym w pętli inicjalizacyjnej pamięci. Po jej skonfigurowaniu i zasłonięciu strony PAS ustawiana jest flaga 1 w rejestrze r0 procesora (2) i identyfikator zapamiętywany jest w obszarze roboczym. Nieco dalej w kodzie (3) wartość ta jest ostatecznie (warunkowo względem flagi 1) przepisywana do zmiennej PROCNU. Takie ukrycie i rozrzucenie kodu pobierania identyfikatora jest zapewne kolejną manifestacją niejawności rozwiązania. Nie sprzyja jej natomiast inna ciekawostka - w przypadku zachowanego komputera z Politechniki Gdańskiej, identyfikator 718 zapisano również na pamięci EEPROM... pisakiem (zdjęcie obok, powyżej).

W tym rozwiązaniu identyfikator był nazywany numerem procesora, ponieważ pamięć MEGA była elementem jednostki centralnej MJC-400, zwanej potocznie procesorem.

Zabezpieczenie programu użytkowego

Struktura danych identyfikująca zabezpieczony program

Program, który ma być zabezpieczony, musi zostać wyposażony w określoną, cykliczną strukturę danych (patrz rysunek obok). Ma ona następujące cechy:

  • pozwala systemowi operacyjnemu łatwo i jednoznacznie stwierdzić, czy załadowany program jest zabezpieczony,
  • pozwala systemowi zweryfikować, czy kopia programu jest autoryzowana dla danej maszyny,
  • pozwala programowi użytkowemu sprawdzić, czy został legalnie uruchomiony,
  • jest trudna do zauważenia i rozpoznania przez intruza analizującego kod programu.

W strukturze występują następujące elementy:

  • A1, A2, A3 - adresy w programie, pod którymi umieszczone są kolejne fragmenty struktury.
  • C - arbitralna stała o wartości 17152, znana zarówno systemowi operacyjnemu jak i twórcom zabezpieczanych programów, używana jako przesunięcie dla adresów. Źródła wskazują, że mechanizm był gotowy do użycia różnych stałych dla przesunięć różnych elementów struktury.
  • SECRET_M - adres zmiennej zapisywanej przez system operacyjny, a dostępnej programowi użytkowemu, dzięki której będzie on mógł w trakcie pracy stwierdzić, czy jest uruchamiany legalnie (=0), czy nielegalnie (!=0).
  • S - adres pola używanego przez system operacyjny podczas ładowania programu.
  • SYSID - zmodyfikowany identyfikator systemu (sposób jego modyfikacji zostanie opisany w dalszej części).
  • IC0 – punkt wejścia do programu; adres, na który system operacyjny ustawia licznik rozkazów przed przełączeniem kontekstu na proces użytkownika.
  • IC0-1 – miejsce, które było ustalonym punktem początkowym struktury, znajdującym się zawsze bezpośrednio przed adresem startu programu.

Strzałkami zielonymi pokazane są na rysunku połączenia pozwalające „skompletować” całą strukturę. Znając początek struktury oraz wartość stałej C, można poruszać się w strukturze odczytując kolejne jej elementy. Strzałkami niebieskimi oznaczone są cykle służące do weryfikacji jej poprawności. Strzałki czerwone wskazują lokalizacje dodatkowych danych używanych podczas uruchamiania zabezpieczonego programu.

Przygotowanie struktury po stronie programisty polega na:

  • ustaleniu w programie adresów A1, A2, A3, SECRET_M i S,
  • zarezerwowaniu miejsca na poszczególne fragmenty struktury,
  • wypełnieniu pól łączących fragmenty struktury (oznaczonych na rysunku zielonym tłem).

Pola oznaczone kolorem czerwonym nie są przez programistę wypełniane, w szczególności nie jest wypełniane pole SYSID.

Lokalizacja adresów jest w zasadzie dowolna, jednak z założenia o niejawności powinny znajdować się one w odległych miejscach programu, tak, aby możliwie dobrze ukryć dane pod nimi zapisane przed wzrokiem intruza. Jedyną wymuszoną lokalizacją jest adres słowa zawierającego A1+C.

Struktura może być oczywiście implementowana w dowolnym języku programowania. Poniżej przykład użycia jej w programie przeznaczonym dla assemblera ASSM. Poszczególne fragmenty znajdują się w różnych miejscach pliku ze źródłem:

C1=17152.
C2=C1.C3=C1.C4=C1.C5=C1.C6=C1.C7=C1.
...
A1+C1.
START:LW,X(DESNAS)
...
A1:A2+C2.MM+C3.
...
A2:A3+C4.A1+C5.
...
A3:A2+C6.0.XX+C7.
...
MM:0.0.XX:0.

Istniał również uniwersalny moduł GASS-a dla programów łączonych konsolidatorem LINK, który pozwalał na łatwe dodanie zabezpieczenia do istniejącego programu źródłowego. Poniżej jego fragment zawierający opis struktury:

SECRET_M  .RES         2
...
C          =           17152
          .DATA        A1+C
A1        .DATA        A2+C, SECRET_M+C
S         .DATA        0
A2        .DATA        A3+C, A1+C
A3        .DATA        A2+C, SYSID+C, S+C

Przykładowa implementacja w języku C, pokazana poniżej, jest chyba najbardziej zawiła. Poszczególne fragmenty zostały nawet rozmyślnie rozrzucone po różnych plikach źródłowych:

#define _HC1 17152

int *_HA11[2] = {&_HA22[_HC1], &_HMM[_HC1]};

int *_HA22[2] = {&_HA33[_HC1], &_HA11[_HC1]};

int *_HA33[4] = {&_HA22[_HC1], 2348, &_HA33[3+_HC1], 2349};
int _HMM[2] = {2348, 2348};
static int *_HA00[2] = {&_HA11[_HC1], &_HA11[_HC1]};

Ładowanie zabezpieczonego programu

Serce mechanizmu zabezpieczeń bije w systemie operacyjnym. Jego działanie rozpoczyna się tuż po załadowaniu programu z dysku do pamięci, jeszcze przed jego uruchomieniem. Do obsługi zabezpieczonych programów, CROOK-5 używa dwóch informacji zapisanych w strukturze procesu:

  • Bit 9 pola BAR mówi o tym, czy kopia programu jest nieautoryzowana. Bit jest zapalany dla kopii nielegalnych, gaszony dla programów niezabezpieczonych oraz kopii legalnych. Przed weryfikacją programu bit jest domyślnie gaszony.
  • Pole BLPASC mówi o tym, czy podczas wykonywania programu należy podjąć kroki przeszkadzające użytkownikowi w korzystaniu z kopii nielegalnej. Przed weryfikacją programu w polu wpisywana jest wartość -1, która oznacza zaniechanie podejmowania takich czynności.

Każdy ładowany program traktowany jest jako potencjalnie zabezpieczony i jest dla niego wykonywana procedura weryfikacji struktury opisanej w poprzednim rozdziale. Poszczególne jej kroki to:

  • załaduj zawartość IC0-1 (=A1+C) i oblicz A1,
  • załaduj zawartość A1 (=A2+C) i oblicz A2,
  • załaduj zawartość A2 (=A3+C) i oblicz A3,
  • załaduj zawartość A2+1 (=A1+C) i sprawdź poprawność A1,
  • załaduj zawartość A3 (=A2+C) i sprawdź poprawność A2,
  • załaduj zawartość A3+1 (=SYSID) i zapisz tymczasowo w rejestrze,
  • załaduj zawartość A3+2 (=S+C), oblicz S, załaduj wartość znajdującą się pod tym adresem, dodaj do niej różnicę A2-A1 i zapisz ponownie pod S.

Dla programów nie posiadających zabezpieczenia, niemal na pewno przynajmniej jeden z kroków weryfikacyjnych się nie powiedzie. W takim wypadku program traktowany jest jako legalnie pozyskany i na takich zasadach uruchamiany. Jeśli natomiast program zostanie rozpoznany jako zawierający zabezpieczenie, następuje jego weryfikacja.

Rozpoczyna się ona od przygotowania dwóch wartości. Pierwsza z nich, nazwijmy ją X, wywodzona jest z numeru procesora zapisanego w systemie operacyjnym:

X = (PROCNU + C) << 1

Przesunięcie o jeden bit w lewo jest potrzebne do poprawnej pracy algorytmu opisanego dalej, ma jednak skutek uboczny: ogranicza efektywną przestrzeń identyfikatorów systemu do 32768 wartości.

Druga wartość, Y, wywodzona jest z różnicy adresów w strukturze zabezpieczającej program:

Y = ((A3 – A2) mod 63) + 56

Zauważalnym zamierzeniem autorów było uzyskać liczbę, która zależy od umiejscowienia pozycji A2 i A3 względem siebie (będzie się różnić się między programami), ale jest stosunkowo mała (w praktyce od 56 do 119).

Następnie na przygotowanym argumencie X wywoływana jest Y razy funkcja mieszająca GEN(X, Y), na którą składają się następujące kroki:

  • policz różnicę symetryczną 1 i 3 bitu argumentu (przy numeracji bitów 15-0),
  • przesuń argument o 1 w prawo,
  • wpisz bit z pierwszego kroku na najstarszą pozycję argumentu.

Wynikiem operacji jest wielokrotnie wymieszany, powiększony uprzednio o stałą C numer procesora.

Wartość ta odejmowana jest w kolejnym kroku od wartości SYSID pobranej uprzednio z ciała uruchamianego programu. Jeśli różnica jest niezerowa, oznacza to, że numer procesora maszyny jest niezgodny z tym, dla którego przygotowana została kopia programu i zostaje ona uznana za nielegalną. To pokazuje też, że SYSID zapisane w zabezpieczonym programie nie jest wprost numerem procesora, a numerem poddanym takiej samej operacji mieszania, jaką wykonuje CROOK-5. Jest to oczywiście kolejnym przejawem założeń o niejawności. Wniosek ten stał się też śladem prowadzącym do autoryzacji programów opisanej w dalszym rozdziale.

W przypadku stwierdzenia nielegalności kopii programu system wykonuje następujące czynności:

  • bit 9 w polu BAR procesu jest zapalany,
  • w polu BLPASC zapisywana jest losowa wartość różna od -1.

Niezależnie od wyniku weryfikacji w polu SECRET_M programu zapisywana jest arytmetyczna negacja obliczonej różnicy identyfikatorów. Oznacza to, że program użytkowy może sprawdzić, czy został uruchomiony na „właściwej” maszynie, poprzez porównanie SECRET_M z zerem, a w przypadku niezgodności podjąć dodatkowe akcje. Przykładem jest edytor tekstów EXM, którego nielegalne kopie odmawiają zapisywania zbiorów.

Działanie nielegalnej kopii programu

Obszary pamięci procesu atakowane przez zabezpieczenia w systemie CROOK-5

Oprócz akcji, które w przypadku stwierdzenia nielegalnego uruchomienia program użytkowy mógł wykonać we własnym zakresie, CROOK-5 sam podejmuje czynności przeciwko nieautoryzowanym kopiom programów. Mechanizm ten uruchamiany jest zawsze podczas wywołania ekstrakodów INPR i PINP. Realizują one funkcje wywołania systemowego czytania ze strumienia, a więc będą w programie niemal na pewno używane. Działanie mechanizmu jest następujące:

  1. Jeśli pole BLPASC w strukturze procesu ma wartość -1, kontynuuj standardową obsługę ekstrakodu. W przeciwnym wypadku kontynuuj działanie zabezpieczenia.
  2. Pobierz wartość z generatora liczb pseudolosowych (różną od -1) i zapisz ją pod BLPASC.
  3. Jeśli bit 9 w polu BAR struktury procesu jest zgaszony, kontynuuj standardową obsługę ekstrakodu. W przeciwnym razie kontynuuj działanie zabezpieczenia.
  4. Pobierz liczbę P z generatora liczb pseudolosowych
  5. Jeśli spełniony jest warunek: (P & 110010) == 100010, to potraktuj P jako adres w przestrzeni procesu wołającego ekstrakod i zapisz pod pod nim zawartość rejestru r6.

Ostatni warunek oznacza, że:

  • Na każdą stronę zaalokowaną w segmencie pamięci przeznaczonej dla procesu przypadało 16 komórek pamięci, których zawartość mogła być zniszczona. A to znów oznacza, że niezależnie od rozmiaru programu mógł on zostać uszkodzony.
  • Prawdopodobieństwo, że wywołanie czytania ze strumienia zniszczy komórkę pamięci w przestrzeni procesu wynosiło: ilość zaalokowanych stron / 4096. Czyli programy większe były bardziej narażone na skutki działania zabezpieczenia.

Rysunek obok pokazuje w jakie obszary w przestrzeni procesu system operacyjny „trafia" losowymi adresami. Generator liczb pseudolosowych czerpał entropię z informacji o tym, w jakim momencie okresu pracy planisty systemu operacyjnego nastąpiło wywołanie systemowe czytania ze strumienia. Ocena jakości generatora nie ma sensu w warunkach emulacji, ponieważ parametry czasowe pracy takiego systemu odbiegają znacznie od prawdziwej maszyny.

Podsumowując: Nielegalne kopie programów uruchomione w systemie CROOK-5 są w pamięci powoli, sukcesywnie niszczone. Rezultaty takiego niszczenia są na tyle przypadkowe i różnorodne, że nieświadomy zabezpieczeń użytkownik będzie najprawdopodobniej podejrzewał niesprawność systemu komputerowego lub błąd w programie. Ale na podejrzeniach jego możliwości się kończą, bo do autorów „wadliwego” programu raczej się o pomoc nie zgłosi. Kopia programu na dysku pozostaje oczywiście nienaruszona.

Autoryzacja programów

Umieszczenie stosownej struktury danych w programie jest tylko „przygotowaniem terenu”. Obserwacja zachowań systemu operacyjnego pokazuje, że pole SYSID musi jeszcze zostać w specyficzny sposób wypełnione. Służy do tego dodatkowe narzędzie: UZDAT.

Wywołanie go bez parametrów pozwala sprawdzić, jaki jest numer procesora maszyny:

`uzdat

NUMER PROCESORA : 718

Zachowanie to jest o tyle ciekawe, że identyfikator systemu nie jest informacją dostępną dla programu użytkowego wprost. Narzędzie używa sztuczki korzystającej z faktu, że system operacyjny w trakcie ładowania zabezpieczonego programu zapisuje pod SECRET_M różnicę między numerem systemu zapisanym w programie, a znanym sobie, uprzednio wymieszanym numerem procesora. UZDAT jest „zabezpieczonym” programem - zawiera strukturę danych go identyfikującą, ale w polu SYSID wpisane ma 0. SECRET_M jest więc po uruchomieniu programu ustawione przez system operacyjny na SYSID wymieszane funkcją GEN(). Ponieważ funkcja ta jest odwracalna i programowi znane są wartości C, A3 i A2, UZDAT może z SYSID obliczyć faktyczny numer procesora.

Pozostaje jeszcze pytanie dlaczego CROOK-5 nie niszczy UZDAT, skoro ten ma wpisany niepoprawny, zerowy identyfikator systemu? Autor narzędzia, znając oczywiście zachowanie CROOK-a, zabezpieczył się przed jego niszczycielskimi zapędami używając do czytania danych wyłącznie ekstrakodów transmisji blokowych, które nie wyzwalają procedury nadpisującej zawartość pamięci procesu.

Podobne użycie odwrotności funkcji GEN() ma miejsce w kolejnym zastosowaniu narzędzia. Podając jako parametr nazwę zbioru z zabezpieczonym programem można sprawdzić, dla jakiej maszyny został on autoryzowany:

`uzdat cc00

NUMER ZE ZBIORU : 718

Jeśli program nie jest zabezpieczony, narzędzie o tym poinformuje:

`uzdat stat

*PROGRAM NIEPRZYGOTOWANY

Wreszcie, odpowiednio przygotowany program można „uzdatnić” do uruchomienia na maszynie o numerze procesora podanym jako drugi argument:

`uzdat cc00 1234

PRAWIDLOWO
`uzdat cc00

NUMER ZE ZBIORU : 1234

Wektory ataku

Skoro znane już są wszystkie szczegóły dotyczące mechanizmu zabezpieczeń używanego przez system CROOK-5, można pokusić się o podsumowanie możliwych wektorów ataku (z uwzględnieniem środków dostępnych w latach '80 użytkownikowi MERY-400).

Identyfikator systemu

Atak na identyfikator systemu wymaga najmniej zaangażowania, ale jest jednocześnie najmniej elastyczny. Zakładając, że komuś udałoby się uzyskać wiedzę o istnieniu i lokalizacji numeru procesora lub numeru zegara, mógł on przygotować „klon” innego komputera i z powodzeniem uruchamiać na nim oprogramowanie zakupione dla klonowanej maszyny. Choć mało elastyczne, rozwiązanie to nie jest pozbawione praktycznego zastosowania - dwóch lub więcej użytkowników systemu mogło „umówić” się i dzielić kosztami oprogramowania. Atak ten ma jedną silną cechę: nie wymaga absolutnie żadnej wiedzy na temat tego, jak mechanizm zabezpieczeń działa.

W praktyce wymaga on podjęcia różnych kroków dla różnych lokalizacji identyfikatora. W przypadku numeru procesora zapisanego w pamięci MEGA należy ponownie zaprogramować kości pamięci EEPROM, co jest procedurą stosunkowo prostą w realizacji. W przypadku zegara czasu rzeczywistego operacja jest o tyle utrudniona, że kości PROM są jednokrotnie programowalne. Trzeba więc zakupić nowe kości pamięci, a do ich zaprogramowania użyć narzędzia potencjalnie trudniej dostępnego niż tego dla pamięci EEPROM.

System operacyjny

Ataki na system operacyjny wymagają największej wiedzy, są najtrudniejsze w realizacji, ale dają najlepsze rezultaty. Raz złamany CROOK-5 zawsze już uruchomi dowolny program, przeznaczony dla dowolnego innego komputera. Można rozpatrzyć dwa różne podejścia. W obu przypadkach atakujący musi przede wszystkim zdeassemblować i przeanalizować jądro systemu. O ile deassemblacja nie stanowi problemu, a analiza, choć żmudna, ma ten sam poziom skomplikowania co i dziś, o tyle śledzenie działania systemu wymaga podejścia do niego „z zewnątrz”. A praktycznie jedyną technologią, która pozwalała na to w latach '80 było użycie pulpitu technicznego MERY-400. To czyni z zadania iście syzyfową pracę.

Dodatkowo, po odpowiednim „poprawieniu” systemu operacyjnego czeka na intruza jeszcze jedna niespodzianka. CROOK-5, zarówno podczas startu, jak i cyklicznie, w trakcie działania, sprawdza swoją sumę kontrolną. Ostatnim krokiem musi więc być odnalezienie gdzie i jak liczona jest wspomniana suma i albo jej poprawienie, albo zablokowanie tego mechanizmu.

Atak można przypuścić z dwóch stron:

  1. Kod ładujący program - W procedurze ładowania programu użytkowego można zapewnić, aby niezależnie od wyniku testu poprawności numeru procesora, do pola BLPASC procesu zapisywana była zawsze wartość -1, a bit 9 pola BAR zawsze pozostawał zgaszony. Dodatkowo trzeba też oszukać sam program, przekonując go, że został uruchomiony na właściwej maszynie. Wymaga to wpisania 0 do pola SECRET_M w jego przestrzeni adresowej.
  2. Kod niszczący proces - W procedurze obsługi wywołania systemowego czytania ze strumienia można zablokować zupełnie podejmowanie akcji dla nielegalnie uruchomionych kopii programów. Do zupełnego wyłączenia zabezpieczeń trzeba by jednak jeszcze, podobnie jak poprzednio, zapisać 0 do SECRET_M.

Program użytkowy

Atak na program użytkowy jest o tyle prostszy od ataku na jądro CROOK-a, że śledzenie wykonywania procesu w systemie można przeprowadzić za pomocą debuggera DEBU dostarczanego wraz z CROOK-iem, co jest znacznie efektywniejsze, niż używanie pulpitu technicznego. Jedyny problem polega na tym, że bez wiedzy z zewnątrz trudno jest odnaleźć sprytnie schowaną między linijkami programu strukturę danych, która w programie używana jest (w najlepszym wypadku) jednorazowo, a i to tylko w jednym jej punkcie.

Ostatecznym rezultatem ataku jest natomiast „złamany” program, który nie tylko jest już na zawsze gotowy do użycia na danej maszynie, ale zadziała również na innych komputerach. Po niewątpliwie czasochłonnym rozgryzaniu zabezpieczeń po stronie jednego programu użytkowego dostaje się też przepis, który bez wysiłku pozwala odblokowywać inne programy. Wystarczy tylko:

  • nadpisać słowo IC0-1 dowolną, różną od znajdującej się tam, wartością,
  • zapisać w SECRET_M zero (jeśli jeszcze go tam nie ma).

Narzędzie autoryzacyjne

A jeśli już rozpatrywać wszystkie wektory ataków, to absolutnie najskuteczniejszą i wymagającą najmniej pracy umysłowej metodą jest zdobycie kopii narzędzia UZDAT. Inżynieria społeczna w latach '80 była z pewnością nie mniej skuteczna niż dziś.