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.