02-18-2019, 12:30 AM
Небольшой гайд для разработчиков, решивших немного защитить свои труды. Мы конечно рассмотрим обфускацию сервера л2.
Многие конечно уже озаботились вопросом, но если нет - вам это пригодится.
ProGuard - мощный гибкий и главное бесплатный обфускатор. Для л2 сервера, с большим количество использования рефлекшнов*, подходит отлично.
Забираем тут ProGuard Java Optimizer and Obfuscator
По нему в принципе есть документация, но не могу сказать, что она очень подробная и многое приходится проверять самому.
Покажу встраивание на примере Ant build, по нему меньше всего документации(а вообще на офф. сайте есть примеры и для gradle и с формированием отдельнго конфига)
Для начала вам понадобится добавить задание. resouce ссылается на classpath внутри jar архива - т.е. если используете готовый билд можете не менять.
Далее делаете новый target и вставляете в него инициализацию задания(в моем примере отключены чистки кода, это может вызывать проблемы)
Добавляете все нужные библиотеки, которые используются у вас, а также библиотеки компилятора.
library.lib.classpath - это сборник путей с внешними библиотеками. Создается так:
Далее указываем файлы на вход и выход. Тут нужно понимать, что если у вас есть внешние скрипты, которые компилируются в Runtime(т.е. лежат в датапаке) - то их взаимодействие с ядром после обфускации будет невозможным, ведь в ядре будет все переименовано. Поэтому нужно либо переносить их все - в ядро; либо описывать все методы и поля классов, что в них используются- в исключения; либо собрать из скриптов отдельный архив, с новыми ссылками на ядро(если понадобится добавлю внизу как их собрать в архив)
Поэтому у меня на вход подается готовый архив gameserver.jar и scripts.jar
На выход можно указать папку, тогда входные архивы сохранятся раздельно. Это удобно)
Далее добавляет главный метод\точку доступа в приложение в исключения
На этом этапе можете запускать задание обфускации и смотреть на ошибки и уведомления. И начинать думать, какой код вам нужно сохранить, чтобы все работало.
Что получилось у меня:
- отключаем лишнее целиком, что ценности не несет
- отключаем классы, которые инициализируются с помощью рефлекшнов
- отключаем классы, методы\поля которых вызываются рефлекшнами
-отключаем конфиги
-отключаем парсеры\холдеры файлов датапака, а также все enum классы, т.к. все шаблоны данных привязываются парсерами обычно к enum типам.
includedescriptorclasses можно не указывать, у меня почему то без него были проблемы с чтением скриптов
- отключаем скрипты из датапака
- отключаем переменные всех ai, потому что они парсятся из ai.obj и npcpos.txt. хотя, у вас наверное этого нету.
- и наконец отключаем все атрибуты(аннотация, эксепшны, вложенные классы), папки ресурсов\кода и имена всех классов.
Имена классов можно также отключать выборочно.
Также можно перемешать названия пакетов c помощью опции flattenpackagehierarchy и оставить только те, что используются рефлекшнами.
*рефлекшн - библиотека java.lang.reflect. Позволяет искать, получать доступ и взаимодействовать с объектами классов снаружи класса и напрямую с ними.
Многие конечно уже озаботились вопросом, но если нет - вам это пригодится.
ProGuard - мощный гибкий и главное бесплатный обфускатор. Для л2 сервера, с большим количество использования рефлекшнов*, подходит отлично.
Забираем тут ProGuard Java Optimizer and Obfuscator
По нему в принципе есть документация, но не могу сказать, что она очень подробная и многое приходится проверять самому.
Покажу встраивание на примере Ant build, по нему меньше всего документации(а вообще на офф. сайте есть примеры и для gradle и с формированием отдельнго конфига)
Для начала вам понадобится добавить задание. resouce ссылается на classpath внутри jar архива - т.е. если используете готовый билд можете не менять.
Код:
<taskdef resource="proguard/ant/task.properties" classpath="path_to_proguard\proguard.jar" />
Код:
<proguard
shrink="false"
optimize="false"
allowaccessmodification="false"
usemixedcaseclassnames="false"
defaultpackage=""
skipnonpubliclibraryclasses="false"
printseeds="out/obfuscateseeds.txt"
printusage="out/obfuscateusage.txt"
printmapping="out/obfuscatemapping.txt">
Код:
<libraryjar name="${java.home}/lib/rt.jar"/>
<libraryjar name="${java.home}/lib/ext/jfxrt.jar"/> -- это для jdk версии явы
<libraryjar name="${commons.jar.file}"/> --эта ссылка на путь commons.jar, у вас может называться по другому
<libraryjar refid="library.lib.classpath"/>
Код:
<path id="library.lib.classpath">
<pathelement location="${basedir}/lib/annotations-3.0.1.jar"/>
.....
<pathelement location="${basedir}/lib/xmlrpc-server-3.1.3.jar"/>
</path>
Поэтому у меня на вход подается готовый архив gameserver.jar и scripts.jar
На выход можно указать папку, тогда входные архивы сохранятся раздельно. Это удобно)
Код:
<injar name="${game.jar.file}"/>
<injar name="${scripts.jar.file}"/>
<!-- the output jar file that should be created with the obfuscated java class files -->
<outjar dir="out\protected"/>
Код:
<keep name="org.mmocore.gameserver.GameServer">
<method name="main"/>
</keep>
Что получилось у меня:
- отключаем лишнее целиком, что ценности не несет
Код:
<keep name="org.jts.**">
<method/>
<field/>
</keep>
<keep name="ru.akumu.smartguard.**">
<method/>
<field/>
</keep>
Код:
<keep name="org.mmocore.gameserver.object.*">
<constructor parameters="int,***"/>
</keep>
<keep name="org.mmocore.gameserver.**" extends="org.mmocore.gameserver.model.entity.events.Event">
<constructor parameters="org.mmocore.commons.collections.MultiValueSet"/>
</keep>
<keep name="org.mmocore.gameserver.model.entity.residence.*">
<constructor parameters="org.mmocore.gameserver.templates.StatsSet"/>
</keep>
<keep name="org.mmocore.gameserver.skills.effects.*">
<constructor parameters="org.mmocore.gameserver.object.Creature, org.mmocore.gameserver.object.Creature, org.mmocore.gameserver.skills.SkillEntry, org.mmocore.gameserver.skills.effects.EffectTemplate"/>
</keep>
<keep name="org.mmocore.gameserver.skills.skillclasses.*">
<constructor parameters="org.mmocore.gameserver.templates.StatsSet"/>
</keep>
<keep name="org.mmocore.gameserver.stats.funcs.*">
<constructor parameters="org.mmocore.gameserver.templates.StatsSet, int, org.mmocore.gameserver.stats.Stats, java.lang.Object, double"/>
</keep>
Код:
<keep name="org.mmocore.gameserver.handler.bypass.Bypass">
<method/>
</keep>
<keep name="org.mmocore.gameserver.model.base.PlayerAccess">
<method/>
<field/>
</keep>
Код:
<keep name="org.mmocore.gameserver.configuration.**">
<method/>
<field/>
</keep>
includedescriptorclasses можно не указывать, у меня почему то без него были проблемы с чтением скриптов
Код:
<keep name="org.mmocore.gameserver.data.**" includedescriptorclasses="true">
<method/>
<field/>
</keep>
<keep extends="java.lang.Enum">
<method/>
<field/>
</keep>
Код:
<keep name="handler.**" includedescriptorclasses="true">
<method/>
<field/>
</keep>
<keep name="services.**" includedescriptorclasses="true">
<method/>
<field/>
</keep>
Код:
<keep name="**" extends="org.mmocore.gameserver.scripts.ai.pts.default_npc" includedescriptorclasses="true">
<field/>
</keep>
Код:
<keepattributes name="*"/>
<keepdirectories name="**"/>
<keepnames name="**"/>
Также можно перемешать названия пакетов c помощью опции flattenpackagehierarchy и оставить только те, что используются рефлекшнами.
*рефлекшн - библиотека java.lang.reflect. Позволяет искать, получать доступ и взаимодействовать с объектами классов снаружи класса и напрямую с ними.