W AVET INS codziennie zajmujemy się oceną różnych zabezpieczeń i analizą, czy ich siła jest wystarczająca z perspektywy założeń biznesowych. Dlatego, aby dostarczać najwyższej klasy rozwiązania naszym Klientom, stworzyliśmy szereg metryk oraz taksonomii, pozwalających na obiektywną i kompleksową ocenę siły zabezpieczenia. W przedstawionym przypadku nasz Klient chciał dokonać oceny skuteczności zabezpieczenia swojej aplikacji przed reinżynierią. Celem projektu było dobranie takiej metody zabezpieczenia, aby atak - biorąc pod uwagę charakter aplikacji i środowisko jej wykonania - był nieopłacalny czasowo.
Wprowadzenie
Obfusctator to narzędzie umożliwiające ochronę kodu aplikacji przed reinżynierją i/lub nieautoryzowanymi modyfikacjami. Zadaniem obfuscatora jest taka modyfikacja kodu (w terminologii polskiej występuje pojęcie zaciemnienia kodu), aby stał on się nieczytelny ale nadal wykonywał te same zadania, co oryginał. Istnieją 3 podstawowe klasy tego typu narzędzi - podział wiąże się ze sposobem ich działania:
- Narzędzia operujące na kodzie źródłowym
- Narzędzia operujące na kodzie pośrednim (tzw. bytecode - używany w wielu interpreterach i maszynach wirtualnych) np. Perla, Pytona, Java czy .NET
- Narzędzia operujące na wynikowym kodzie binarnym (czyli na wykonywalnych plikach i bibliotekach współdzielonych)
W niniejszym studium przypadku zajmiemy się przede wszystkim dwoma ostatnimi przypadkami.
Zastosowanie
Obfuscatory mają obecnie 2 popularne zastosowania:
- Ochrona kodu przed złamaniem zabezpieczenia (np. wprowadzanie numeru i weryfikacja seryjnego) lub kradzieżą algorytmów.
- Ochrona aktywnej zawartości aplikacji webowych (np. JavaScript, ASP czy Flash Actionscript)
W obu przypadkach cel jest zawsze ten sam: taka modyfikacja kodu, aby nie dało się go w prosty i tani sposób dezasemblować lub zdekompilować. Wynik dezasemblacji lub dekompilacji ma pozostać nieczytelny dla człowieka lub być błędny.
Niezależnie od zastosowania oraz wyboru narzędzia żadna metoda zaciemniania kodu nie daje 100% ochrony. Jak w takim razie dokonać oceny jakie rozwiązanie zastosować w swoim projekcie (biorąc pod uwagę teoretyczną mnogość dostępnych technik)?
Podstawowe techniki zaciemnienia kodu
| Technika | Opis | Ocena bezpieczeństwa |
|---|---|---|
| Wykorzystanie błędów deasembler / dekompilatora | Celowa aranżacja kodu w taki sposób aby wykorzystać znane błędy w narzędziach służących do dekompilacji | Niska: zazwyczaj nowe wersje narzędzi mają poprawione stare błędy. Wiele narzędzi umożliwia interaktywny proces dezasemblacji i dekompilacji, co umożliwia wpływ na efekt końcowy i ewentualną poprawę błędu automatu. Doskonałym przykładem może być pakiet IDA Pro, który tak naprawdę jest maszyną wirtualną, operującą na wybranym obiekcie binarnym. |
| Ukrywanie symboli | Celowe usunięcie informacji o symbolach oraz dodatkowych informacji dla debugera. | Niska: wiele struktur można identyfikować poprzez charakterystyczne ułożenie kodu oraz sygnaturę kompilatora. Dzięki temu wiele zmiennych (np. wskaźnik this w C++) można bez problemu identyfikować nie posiadając żadnych symboli. |
| Ukrywanie tablicy importu | Usunięcie oryginalnej tablicy importu w celu uniemożliwienia identyfikacji z jakich funkcji API i bibliotek program korzysta. | Niska: tablica importu nie jest trudna do odbudowania. W wielu przypadkach proces może być zautomatyzowany. |
| Pakowanie i szyfrowanie kodu | Spakowanie (kompresja) lub zaszfrowanie kodu - przed uruchomieniem oryginalnego kodu następuje jego dekompresja lub deszyfracja. Dzięki temu obraz pamięci procesu ani pliki na dysku nie są poprawnym kodem wykonywalnym (zakładając poprawność implementacji tego zabezpieczenia) | Niska: wiele tego typu obfuscatorów można usuwać automatycznie. W wielu przypadkach stosuje się metody ogólne, które nawet nie znają zastosowanego zabezpieczenia, a jedynie opierają się o wykonanie lub emulację kodu aplikacji. |
| Rearanżacja kodu | Taka przebudowa kodu aby stał on się trudny do analizy a proces dekompilacji / deasmeblacji trudny. | Średnia: pełna, poprawna dezasemblacja lub dekompilacja zazwyczaj nie jest opłacalna. Nadal jednak wybrane fragmentu kodu mogą zostać poddane dogłębnej analizie - zajmie to jednak czas. |
| Translacja kodu na kod dla maszyny wirtualnej | Przetłumaczenie kodu wynikowego na kod maszyny dedykowanej wirtualnej obfuscatora | Wysoka: pełna, poprawna dezasemblacja lub dekompilacja zazwyczaj nie jest opłacalna. W niektórych przypadkach atakujący musi stworzyć dedykowane narzędzia oraz (w pierwszym zetknięciu z technologią) poświęcić czas na analizę maszyny wirtualnej. |
Należy zwrócić uwagę, że nie każdą metodę można stosować do każdego przypadku. Na przykład metoda ostatnia praktycznie nie jest wykorzystywana w aplikacjach webowych - proces pobierania i instalacji maszyny wirtualnej byłby zbyt długi. Maszyna taka musiałby być zaimplementowana w dostępnej po stronie Klienta technologii, a to z kolei czyniłoby ją bardziej podatną na analizę.
Ocena siły zabezpieczenia
Stosując przedstawioną w poprzednim punkcie tabele można dokonać wstępnej oceny. Należy zwrócić uwagę, że wiele technologii zawiera połączenie wspomnianych technik oraz często rozszerza je o aktywne elementy ochrony kodu (np. detekcję debugera czy sprawdzanie sum kontrolnych kodu). Na potrzeby realizowanych przez AVET INS projektów musieliśmy opracować własny mechanizm oceny. Analizując wiele różnych metod oraz ich implementacji wypracowaliśmy następujący model oparty na 2 kategoriach:
- Oceny czasu i środków potrzebnych do "wydobycia" określonego fragmentu kodu
- Oceny czasu i środków potrzebnych do usunięcia wszystkich elementów dodanych przez obfuscatora (w przypadku zastosowania maszyny wirtualnej ocena na ile da się odtworzyć oryginalny kod wynikowy)
Dla każdej z kategorii stosowane są takie same punkty kontrolne:
- Ile roboczogodzin zajęło odzyskanie kodu?
- W jakim procencie automat dokonał poprawnej dezasemblacji / dekompilacji kodu?
- W jakim procencie automat dokonał poprawnej dezasemblacji / dekompilacji kodu obfuscatora
- W jakim procencie automat dokonał poprawnej dezasemblacji / dekompilacji kodu oryginalnej aplikacji
- Czy oryginalny kod jest dostępny w całości podczas wykonania procesu?
- Czy modyfikowany jest przepływ sterowania w oryginalnym kodzie aplikacji?
- Czy modyfikowane jest ułożenie danych w oryginalnym kodzie aplikacji?
- Czy modyfikowane jest sposób i kolejność przekazywania argumentów w oryginalnym kodzie aplikacji?
- Ile modyfikacji potrzeba do odzyskania kodu?
- Czy wystarczy jedna modyfikacja obfuscatora, aby odzyskać kod?
- Czy można opracować uniwersalną metodę odzyskiwania kodu dla każdego przypadku?
- Czy można opracować automat odzyskujący kod dla każdego przypadku?
- Czy sposób zaciemnienia kodu jest taki sam?
- Czy sposób zaciemnienia kodu jest zawsze taki sam dla tego samego pliku wejściowego?
- Czy sposób zaciemnienia kodu jest zawsze taki sam dla różnych plików wejściowych?
- Jak zastosowanie obfuscatora wpłynęło na wydajność aplikacji?
pl
en
