Сборки серверов

СливПлатные

Сейчас онлайн

  • homalend
  • lalakochik
  • tmitri4
  • fafu813
  • Maksoperde
  • mark1
  • ddzetov
  • iceblue
  • 60iq_hahaha
  • Tieumiu
  • DenPlayStar
  • ccvrsxdd
  • Rhaegar
  • Sagaan
  • RedLine_
  • DanyLords
  • eldar112
  • GuardTime
  • coller123
  • sal1sss
  • fikswern
  • goatsolitude
  • PlugManYT
  • Zahar2014
  • motikkampotik1012
  • ChildFreak
  • dirtysonic
  • Vadim_Khv
  • KazuYaUa
  • wenz
  • Vilariqu
  • 314314
  • Numania
  • d1scuss
  • DJamMix
  • frokie66
  • qwrty
  • gckvb
  • Mapars
  • Nelton
  • helixmusicoff
  • kirvel
  • Lazoor
  • Miller_00
  • arbuz1k
  • Pupsikmopsik
  • VitaliyXX
  • timka889
  • ScorpRex
  • Setarik
  • sevi912
  • Masterok
  • violentuso

Защита лицухи LPX

1793543436600​
Сегодня я разберу защиту лицухи плагина LPX. Как работает, что происходит, и что от неё осталось в сборке.

Речь про LPX. Не про «где-то в jar нашлась строка». Про метод, который у автора решает, жить плагину или нет. В моём файле этот метод больше ничего не решает.

Что делает стоковая дверца
У автора проверка сидит в it.ytnoos.lpx.z. Имени этого класса в констант-пуле соседей нет. Ядро на старте достаёт его из зашифрованной строки, делает Class.forName, берёт конструктор от API и сразу зовёт метод проверки.

Строки закрыты DES/CBC. Пока таблица не открыта, адрес в файле не ищется. Я её открыл отдельно, сервер для этого не нужен. Там одна дверь:


Второй слот, force-fallback, ведёт туда же. С третьей попытки код сам переключается на вторую строку и снова приходит сюда. Запасного сервера нет.

Из конфига читаются два поля. license уходит как есть. force-fallback только выбирает слот, а слоты одинаковые.

Дальше POST. На соединение 15 секунд, на чтение 6. Живым считается только ответ 200. Обрыв повторяется, пока счётчик не станет больше 6, потом обычный IOException и плагин тушит сам себя. Ключ при обрыве мог быть любым. До него просто не дошли.

User-Agent каждый раз один и тот же труп:
Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2

Тело — три кадра. Сначала длина, потом байты.
  1. Ключ из конфига. Открытый.
  2. Версия jar. Открытая.
  3. Порт сервера. Закрытый ключом от байтов собственного z.class. Сам class по сети не едет.

До 1.20.5 байты класса берутся как ресурс: точки в имени меняются на слэши, хвост .class. С 1.20.5 класс ищет себя в plugins по маске LPX*.jar, запись должна кончаться на /z.class. Не нашёл — в лог пишет, чтобы jar не переименовывали, и проверка мертва ещё до сокета. Имя файла тут часть протокола, не каприз.

Ответ — не true. Первые два кадра открываются тем же ключом и становятся именем класса и именем метода. Дальше Class.forName, конструктор без аргументов, invoke. В метод передаётся сам LPX. Поведение после проверки приезжает с lpx.ytnoos.it и поднимается в уже запущенной JVM. Третий кадр — boolean, его запоминают как флаг обновления.

Сошлось:
License successfully validated!

Не сошлось:
Unable to verify your license!

И disable. Середины нет. Канал при этом голый http. Кто отвечает по этому имени, тот и выбирает класс.

Что я с этим сделал в 3.9.1
Верхний метод проверки я не трогал ради красоты. Я вырезал тот, на который он опирается.

Сток: верхний зовёт проверку и по boolean пишет успех или гасит плагин.
Мой файл: этот вызов больше не доходит до сокета.

Тело метода теперь такое.
  1. Поднимается локальный хук it.ytnoos.lpx.q. Он сам регистрирует чеки и доводит старт, который раньше должен был приехать классом в ответе.
  2. setUpdated(true). Флаг, который автор ждал с того конца, ставится здесь.
  3. com.blackminecraft.patch.Banner.run(). Один раз в лог плагина LPX пишется Black-Minecraft.com.
  4. return true.

Наверх уходит успех. Верхний метод честно пишет, что лицензия проверена. Проверять было нечего: true собрано локально. lpx.ytnoos.it в этой секунде не участвует. Конфигный ключ можно не класть. Переименование jar эту дверцу больше не роняет, потому что байты z.class для замка больше не читаются.

Старый POST, Firefox 3.6.2, поиск LPX*.jar и Class.forName по ответу в классе z лежат ниже. Это труп. Я его не вычищал до последней инструкции: на живой путь он не подписан. Кто откроет метод, который реально зовётся с onEnable, увидит баннер и ireturn единицы. Кто откроет соседний метод, увидит лицуху автора целиком и ни одного вызова на неё.

Вот и вся защита. У них удалённый класс по голому HTTP, привязанный к байтам собственного файла. У меня на ресурсе этот класс больше не имеет голоса.
 
ВерхНиз