Jak zablokować skompilowane klasy Java, aby zapobiec dekompilacji?

98

Jak zablokować skompilowane klasy Java, aby zapobiec dekompilacji?

Wiem, że to musi być bardzo dobrze omawiany temat w Internecie, ale nie mogłem dojść do żadnego wniosku po ich skierowaniu.

Wiele osób sugeruje obfuscator, ale po prostu zmieniają nazwy klas, metod i pól za pomocą trudnych do zapamiętania sekwencji znaków, ale co z wrażliwymi wartościami stałymi?

Na przykład opracowałeś komponent szyfrowania i deszyfrowania w oparciu o technikę szyfrowania opartą na hasłach. W tym przypadku każda przeciętna osoba korzystająca z języka Java może użyć JAD do dekompilacji pliku klasy i łatwego pobrania wartości hasła (zdefiniowanej jako stała), a także soli, a następnie może odszyfrować dane, pisząc mały niezależny program!

A może takie wrażliwe komponenty powinny być wbudowane w kod natywny (na przykład VC ++) i wywoływać je za pośrednictwem JNI ?

jatanp
źródło
Nawet zdemontowany kod natywny jest całkiem czytelny dla niektórych ludzi, prawdopodobnie musiałbyś użyć jakiegoś obfuskatora lub ręcznie wykonanego asemblera, aby uczynić go nieoczywistym (wspólny C ++ skompilowany z optymalizacją jest wystarczająco czytelny). Każdy kod uruchomiony na urządzeniu użytkownika może zostać przechwycony. Chociaż wymagania dotyczące kosztu / umiejętności takiego przechwytywania mogą być dość wysokie, na przykład włamanie do kodu chipa „odpornego na manipulacje” karty inteligentnej nie jest trywialne, możliwe do wykonania tylko przy użyciu najlepszego sprzętu i umiejętności. Z klasami Java ... Nie zawracałbym sobie głowy, prawdopodobnie możesz je zaszyfrować na tyle, aby odstraszyć dzieci od skryptów, a nie więcej.
Ped7g

Odpowiedzi:

97

Niektóre z bardziej zaawansowanych obfuskatorów kodu bajtowego Java robią znacznie więcej niż tylko zniekształcanie nazw klas. Na przykład Zelix KlassMaster może również zakodować przepływ kodu w sposób, który sprawia, że ​​jest naprawdę trudny do naśladowania i działa jako doskonały optymalizator kodu ...

Ponadto wielu obfuskatorów jest w stanie zaszyfrować stałe łańcuchowe i usunąć nieużywany kod.

Innym możliwym rozwiązaniem (niekoniecznie wykluczającym zaciemnianie) jest użycie zaszyfrowanych plików JAR i niestandardowego modułu ładującego klasy, który odszyfrowuje (najlepiej przy użyciu natywnej biblioteki wykonawczej).

Po trzecie (i prawdopodobnie zapewniające najsilniejszą ochronę) jest użycie natywnych kompilatorów wyprzedzających, takich jak na przykład GCC lub Excelsior JET , które kompilują kod Java bezpośrednio do natywnego pliku binarnego specyficznego dla platformy.

W każdym razie musisz o tym pamiętać, jak mówi estońskie przysłowie „Zamki są dla zwierząt”. Oznacza to, że każdy bit kodu jest dostępny (załadowany do pamięci) w czasie wykonywania i mając wystarczające umiejętności, determinację i motywację, ludzie mogą i będą dekompilować, rozszyfrować i zhakować Twój kod ... Twoim zadaniem jest po prostu uczynić proces tak niewygodnym jak możesz i nadal to działa ...

Roland Tepp
źródło
14
+1 za „Zamki są dla zwierząt”. Wydaje mi się, że odpowiednim terminem byłyby tu dzieciaki ze scenariuszy.
Antymon
Masz na myśli GCJ, a on jest martwy.
Markiz Lorne
1
Szyfrowanie plików jar Componio też nie działa. Zamiast tego użyj JarProtector .
J.Profi,
16

Dopóki mają dostęp zarówno do zaszyfrowanych danych, jak i do oprogramowania, które je odszyfrowuje, w zasadzie nie ma sposobu, aby uczynić to całkowicie bezpiecznym. Sposób, w jaki zostało to wcześniej rozwiązane, polega na użyciu jakiejś formy zewnętrznej czarnej skrzynki do obsługi szyfrowania / deszyfrowania, takiej jak klucze sprzętowe, zdalne serwery uwierzytelniające itp. Ale nawet wtedy, biorąc pod uwagę, że użytkownik ma pełny dostęp do własnego systemu, trudne, a nie niemożliwe - chyba że możesz powiązać swój produkt bezpośrednio z funkcjonalnością przechowywaną w „czarnej skrzynce”, jak na przykład serwery gier online.

Erlend Halvorsen
źródło
13

Zastrzeżenie: nie jestem ekspertem w dziedzinie bezpieczeństwa.

To brzmi jak zły pomysł: pozwalasz komuś zaszyfrować rzeczy za pomocą „ukrytego” klucza, który mu dajesz. Nie sądzę, aby można to było zabezpieczyć.

Może klawisze asymetryczne mogą działać:

  • wdrożyć zaszyfrowaną licencję z kluczem publicznym do odszyfrowania
  • pozwól klientowi utworzyć nową licencję i wysłać ją do Ciebie w celu zaszyfrowania
  • wyślij nową licencję z powrotem do klienta.

Nie jestem pewien, ale uważam, że klient może faktycznie zaszyfrować klucz licencyjny za pomocą klucza publicznego, który mu podałeś. Następnie możesz go odszyfrować za pomocą klucza prywatnego i ponownie zaszyfrować.

Możesz zachować oddzielną parę kluczy publiczny / prywatny dla każdego klienta, aby upewnić się, że faktycznie otrzymujesz dane od właściwego klienta - teraz jesteś odpowiedzialny za klucze ...

Daren Thomas
źródło
2
Używałem już tej techniki i działa dobrze. Jednak z punktu widzenia ataku, pierwszym podejściem byłoby po prostu zmodyfikowanie kodu, który wykonuje sprawdzenie licencji, i usunięcie go. Są rzeczy, które możesz zrobić, aby utrudnić ten wektor ataku, ale jeśli dasz atakującemu kontrolę nad sprzętem, twoim przeznaczeniem jest porażka z odpowiednio zmotywowanym i wykwalifikowanym napastnikiem. W praktyce celem jest po prostu zachowanie uczciwości w większości uczciwych ludzi.
Jim Rush,
12

Bez względu na to, co robisz, można to „zdekompilować”. Heck, możesz go po prostu zdemontować. Lub spójrz na zrzut pamięci, aby znaleźć swoje stałe. Widzisz, komputer musi je znać, więc twój kod też będzie musiał.

Co z tym zrobić?

Staraj się nie wysyłać klucza jako stałej zakodowanej na stałe w kodzie: zachowaj go jako ustawienie dla użytkownika. Spraw, aby użytkownik był odpowiedzialny za opiekę nad tym kluczem.

Daren Thomas
źródło
6

@jatanp: lub jeszcze lepiej, mogą dekompilować, usuwać kod licencyjny i ponownie kompilować. Wydaje mi się, że w przypadku Javy nie ma odpowiedniego, odpornego na włamania rozwiązania tego problemu. W Javie nawet zły, mały klucz sprzętowy nie mógłby temu zapobiec.

Moi menedżerowie biznesu martwią się tym i myślę za dużo. Ale z drugiej strony sprzedajemy naszą aplikację dużym korporacjom, które zwykle przestrzegają warunków licencji - generalnie bezpiecznego środowiska dzięki licznikom fasoli i prawnikom. Sama dekompilacja może być nielegalna, jeśli twoja licencja jest napisana poprawnie.

Muszę więc zapytać, czy naprawdę potrzebujesz wzmocnionej ochrony, tak jak szukasz dla swojej aplikacji? Jak wygląda Twoja baza klientów? (Korporacje? Lub nastoletnie masy graczy, gdzie byłby to większy problem?)

Stu Thompson
źródło
W rzeczywistości istnieje odpowiednie, odporne na włamanie rozwiązanie, które przy okazji wygląda jak klucz sprzętowy: excelsior-usa.com/blog/excelsior-jet/ ...
Dmitry Leskov
Może @DmitryLeskov „odporny na włamanie”. Ale to tylko próg zwalniający dla każdego, kto chce poznać kod. Pod koniec dnia kod bajtowy musi działać na platformie hosta w postaci niezaszyfrowanej. Kropka.
Stu Thompson
Nie przeczytałeś posta, do którego utworzyłem łącze. Kod bajtowy jest konwertowany na kod procesora klucza sprzętowego i szyfrowany. Gdy użytkownik końcowy uruchamia chronioną aplikację, zaszyfrowany kod jest przesyłany do „klucza sprzętowego”. Klucz odszyfrowuje i uruchamia go na swoim procesorze.
Dmitry Leskov
2
Korporacyjni nastoletni gracze.
odiszapc
3

Jeśli szukasz rozwiązania licencyjnego, możesz sprawdzić TrueLicense API . Opiera się na wykorzystaniu asymetrycznych klawiszy. Nie oznacza to jednak, że Twoja aplikacja nie może zostać złamana. Każda aplikacja może zostać złamana przy wystarczającym wysiłku. Jak odpowiedział Stu , naprawdę ważne jest ustalenie, jak silnej ochrony potrzebujesz.

hakan
źródło
2

Nie sądzę, aby istniała jakakolwiek skuteczna metoda antypiracka offline. Branża gier wideo próbowała to odkryć wiele razy, a ich programy zawsze były łamane. Jedynym rozwiązaniem jest to, że program musi być uruchomiony w trybie online i połączony z serwerami, aby można było zweryfikować klucz lincense i że w danym momencie istnieje tylko jedno aktywne połączenie licencjobiorcy. Tak działa World of Warcraft lub Diablo . Nawet jeśli istnieją prywatne serwery opracowane dla nich, aby ominąć zabezpieczenia.

Powiedziawszy to, nie wierzę, że średnie / duże korporacje używają nielegalnie kopiowanego oprogramowania, ponieważ koszt licencji dla nich jest minimalny (być może nie wiem, ile zamierzasz zapłacić za swój program) w porównaniu z koszt wersji próbnej.

Telcontar
źródło
2

Możesz bez obaw korzystać z szyfrowania bajtowego.

Faktem jest, że cytowany powyżej artykuł „Cracking Java Bte-Code Encryption” zawiera błąd logiczny. Głównym założeniem artykułu jest to, że przed uruchomieniem wszystkie klasy muszą zostać odszyfrowane i przesłane do ClassLoader.defineClass(...)metody . Ale to nieprawda.

Założenie pominięte w tym miejscu zakłada , że działają one w autentycznym lub standardowym środowisku wykonawczym Java . Nic nie może zmusić chronionej aplikacji Java nie tylko do uruchomienia tych klas, ale nawet do odszyfrowania i przekazania ich do ClassLoader. Innymi słowy, jeśli pracujesz w standardowym środowisku JRE, nie możesz przechwycić defineClass(...)metody, ponieważ standardowa java nie ma do tego celu interfejsu API, a jeśli używasz zmodyfikowanego środowiska JRE z poprawioną ClassLoaderlub inną „sztuczką hakerską”, nie możesz tego zrobić, ponieważ jest chroniona Aplikacja java w ogóle nie będzie działać, dlatego nie będziesz mieć nic do przechwycenia. I absolutnie nie ma znaczenia, który „wyszukiwarka łatek” jest używany lub jaką sztuczkę wykorzystują hakerzy. Te szczegóły techniczne to zupełnie inna historia.

Yury Bendersky
źródło
Jak dokładnie zamierzasz wykryć poprawioną maszynę JVM? W każdym razie wszystko to sprawia, że ​​sytuacja jest nieco trudniejsza.
Antymon
Nie możesz po prostu znaleźć wywołania do defineClass () w swoim menu z aplikacjami? Kiedy wykonujesz to wywołanie, i tak musisz podać tablicę odszyfrowanych bajtów. Czy to nie kolejny punkt, w którym oryginalne źródło mogłoby wyciekać?
Ascendant,
4
Naprawdę nie zgadzam się z tą odpowiedzią. Dla mnie brzmi to tak: „Pytanie: Jaki jest najłatwiejszy sposób na znalezienie Pi? Odpowiedź: Weź 2 * Pi i podziel przez dwa”. Nie zgadzam się z tym pomysłem, ale czy mógłbyś podać więcej szczegółów? Na przykład, czy oczekujesz, że główny program będzie napisany w czystej Javie? Czy obejmuje to kod, który szuka modyfikacji?
Patrick M
-1

P: Czy jeśli zaszyfruję moje pliki .class i użyję niestandardowego modułu ładującego klasy do załadowania i odszyfrowania ich w locie, czy zapobiegnie to dekompilacji?

O: Problem z zapobieganiem dekompilacji kodu bajtowego Javy jest prawie tak stary jak sam język. Pomimo szeregu narzędzi zaciemniających dostępnych na rynku, początkujący programiści Java wciąż zastanawiają się nad nowymi i sprytnymi sposobami ochrony swojej własności intelektualnej. W tej części poświęconej pytaniom i odpowiedziom dotyczącym języka Java rozwiewam kilka mitów dotyczących pomysłu często powracającego na forach dyskusyjnych.

Niezwykła łatwość, z jaką pliki Java .class mogą być rekonstruowane do źródeł Java, które są bardzo podobne do oryginałów, ma wiele wspólnego z celami projektowania kodu bajtowego Java i kompromisami. Kod bajtowy Java został między innymi zaprojektowany z myślą o zwartości, niezależności platformy, mobilności sieci i łatwości analizy przez interpretery kodu bajtowego i dynamiczne kompilatory JIT (just-in-time) / HotSpot. Prawdopodobnie skompilowane pliki .class wyrażają intencje programisty tak wyraźnie, że mogą być łatwiejsze do przeanalizowania niż oryginalny kod źródłowy.

Można zrobić kilka rzeczy, jeśli nie całkowicie zapobiec dekompilacji, a przynajmniej utrudnić ją. Na przykład, jako krok po kompilacji, możesz wymasować dane .class, aby kod bajtowy był trudniejszy do odczytania po dekompilacji lub trudniejszy do dekompilacji do prawidłowego kodu Java (lub obu). Techniki takie jak wykonywanie ekstremalnego przeciążania nazw metod dobrze sprawdzają się w pierwszym przypadku, a manipulowanie przepływem sterowania w celu utworzenia struktur kontrolnych, których nie można przedstawić za pomocą składni języka Java, działają dobrze w przypadku drugiego. Bardziej udane komercyjne obfuskatory wykorzystują połączenie tych i innych technik.

Niestety, oba podejścia muszą faktycznie zmienić kod, który JVM będzie uruchamiać, i wielu użytkowników obawia się (słusznie), że ta transformacja może dodać nowe błędy do ich aplikacji. Ponadto zmiana nazw metod i pól może spowodować, że wywołania odbicia przestaną działać. Zmiana rzeczywistych nazw klas i pakietów może spowodować uszkodzenie kilku innych interfejsów API języka Java (JNDI (Java Naming and Directory Interface), dostawców adresów URL itp.). Oprócz zmienionych nazw, jeśli skojarzenie między przesunięciami kodu bajtowego klasy a numerami wierszy źródłowych zostanie zmienione, odzyskanie oryginalnych śladów stosu wyjątków może być trudne.

Następnie istnieje możliwość zaciemnienia oryginalnego kodu źródłowego Java. Ale zasadniczo powoduje to podobny zestaw problemów. Szyfruj, a nie zaciemniaj?

Być może powyższe sprawiło, że pomyślałeś: „A co jeśli zamiast manipulować kodem bajtowym, szyfruję wszystkie moje klasy po kompilacji i odszyfrowuję je w locie wewnątrz maszyny JVM (co można zrobić za pomocą niestandardowego modułu ładującego klasy)? Następnie JVM wykonuje moje oryginalny kod bajtowy, a mimo to nie ma co dekompilować ani odtwarzać kodu źródłowego, prawda? "

Niestety, byłbyś w błędzie, zarówno myśląc, że to ty wpadłeś na ten pomysł jako pierwszy, jak i myśląc, że to faktycznie działa. Przyczyna nie ma nic wspólnego z siłą twojego schematu szyfrowania.

S Harish Morampudi
źródło