Одной из главных проблем Java является то, что скомпилированный байткод достаточно легко восстановить в практически читаемый исходный код. Современные декомпиляторы (CFR, Fernflower, javap и другие) без особых усилий извлекают бизнес-логику, алгоритмы, механизмы лицензирования и другие внутренние детали приложения.
Для коммерческих Java-приложений это серьёзная проблема: злоумышленник может изучить логику защиты, изменить её или полностью отключить проверку лицензии.
В рамках этого проекта я разработал инструмент, который интегрируется в процесс сборки и преобразует обычный JAR-файл в защищённую версию. Полученный архив не может быть корректно обработан стандартной JVM и большинством популярных декомпиляторов, однако полностью работоспособен в специально модифицированной JVM, которая поставляется только лицензированным пользователям.
Главная цель проекта — не просто классическая обфускация, а изменение самого формата исполняемого байткода, понятного только собственной среде выполнения.
Инструмент реализован на базе ASM (OW2) и выполняет несколько этапов обработки каждого класса внутри JAR-файла.
Подмена JVM-опкодов. Часть стандартных инструкций JVM (return, invokevirtual, dup и другие) заменяется на собственные значения, отсутствующие в официальной спецификации. В результате обычная JVM и стандартные декомпиляторы не способны корректно обработать такой байткод, тогда как модифицированная JVM интерпретирует его без каких-либо изменений в работе приложения.
Шифрование строковых констант. Все строковые литералы автоматически шифруются алгоритмом AES-GCM с генерацией нового ключа при каждом запуске протектора. Вместо хранения строк в открытом виде в байткод встраиваются вызовы встроенного дешифратора, благодаря чему чувствительная информация (сообщения об ошибках, проверки лицензии, внутренние строки и т.д.) отсутствует в class-файлах.
Дополнительная защита структуры архива. Классы в итоговом JAR записываются в изменённом порядке (по глубине наследования), что усложняет поверхностное сравнение защищённой и оригинальной сборок.
Во время разработки особое внимание уделялось стабильности инструмента. Были обнаружены и устранены несколько нетривиальных ошибок, связанных с обработкой байткода, включая работу с внутренними ограничениями ASM и пересчётом стековых кадров (ClassWriter). Для предотвращения повторного появления подобных проблем были добавлены unit- и regression-тесты.
Сам проект оформлен как CLI-приложение на Gradle и включает две основные команды:
protect — защита JAR-файла;
export-assets — экспорт ресурсов отдельно от байткода.
Также подготовлены подробный README, описание архитектуры проекта и автоматизированные тесты.
В результате был создан полноценный инструмент, который принимает обычный JAR-файл и автоматически формирует защищённую сборку с изменёнными опкодами, зашифрованными строковыми константами и модифицированной структурой архива.
Инструмент используется как часть схемы лицензирования Java-приложений совместно с собственной модифицированной JVM. Без этой среды выполнения защищённое приложение невозможно корректно запустить, а попытки декомпиляции приводят к получению некорректного или нечитаемого результата вместо исходного кода.
Проект опубликован на GitHub и включает исходный код, тесты, документацию, описание архитектуры и дальнейший план развития. Помимо практического применения, он служит хорошим примером работы с JVM-байткодом на уровне ASM и может использоваться как основа для разработки систем защиты Java-приложений и других инструментов, связанных с обработкой байткода.