# Как работает: техники, плюсы, минусы Каждая техника описана одинаково: **как работает → что даёт → что стоит / где ломается**. Цена указана честно: обфускация - это всегда обмен читаемости и времени против аналитика. Итоговая таблица - в конце. ## 1. Переименование (`Rename`) **Как работает.** Классы получают случайные имена в случайных пакетах (или в исходном пакете, если `spread_packages = false`). Поля и методы переименовываются, если `method_rename_safety` считает их безопасными (private/static/final - сразу; virtual - только «семейством» на всю иерархию, чтобы не разъединить переопределения и мостовые методы). Затем переписываются дескрипторы, `LDC`-классы, вызовы, поля, исключительная таблица, `Signature`/`Exceptions`/`InnerClasses`. **Плюсы.** - Самая дешёвая и самая отдающая техника: читает `javap -p` любой анализатор. - Не меняет семантику - риск сломать код минимален. - Маппиинг пишется в отчёт → stack trace'и переводятся обратно. **Минусы / цена.** - Keep-список обязателен: рефлексия по имени (`Class.forName("pkg.C")`), JNI, `serialVersionUID`, сериализация, entrypoint'ы. Пропустил - получил `ClassNotFoundException` в рантайме. - Рефлексия через строки мы ловим автоматически (анализ `Class.forName`, `getMethod`/`getField` и т.п.), но динамическую конкатенацию - нет. - JSON-(де)сериализация по именам полей (Gson/Jackson/`Codec`) не ловится: биндинг идёт через `X.class`-литерал, не строку - см. `todo.md` §2. - Для библиотек `rename_public_members = false` по умолчанию: менять публичный API библиотеки - сломать всех потребителей. Шесть стилей имён (`ShortLatin`, `IlO0`, `Unicode`, `MixedLatin`, `DecoyWords`, `Plausible`) - компромисс по-разному: `IlO0`/`Unicode` явно сигнализируют «это обфускация», `Plausible` маскируется под обычный прикладной код (`TextureCache`, `resolveToken`), ценой того, что при ручном чтении он менее заметно отличается от настоящей логики, чем намеренный мусор - скрыть обфускацию и скрыть её цену ценой чуть большей путаницы для собственной команды при отладке маппингов. **Статус: готово.** ## 2. Шифрование строк (`Strings`) **Как работает.** Каждый `LDC` со строкой короче `min_len` заменяется на `LDC cipher; LDC salt; INVOKESTATIC d(String,I)String`. Ключ строки выводится из мастер-секрета + salt, **не лежит рядом с шифротекстом**. Между строками ротируются разные схемы (`xor-splitmix`, `xtea-ctr`), nonce уникален на строку, MAC ловит порчу. **Плюсы.** - Строки - главный источник логики (`"admin"`, URL, SQL, regex). - Ротация схем: один скрипт деобфускации не покрывает весь jar. - MAC: подмена шифротекста не «молча» портит текст, а падает. **Минусы / цена.** - Декодер добавляет вызов на каждый доступ к строке → оверхед в рантайме. - Целевой анализ декодера всё равно раскроет текст; задача - поднять цену, а не сделать невозможным. - Мастер-секрет собирается в декодере из двух частей (`k1 ^ k2`). **Статус: готово** (подстановка + генератор декодера v49 + сквозной тест исполнением под настоящим `java`). ## 3. Обфускация констант (`Constants`) **Как работает.** Числовая константа не хранится как есть, а собирается из разных выражений: `x ^ k`, `a + b`, `(a << 1) + c`. Плюс настоящие opaque-предикаты - условия, истинность которых зависит от инварианта (например, `i*(i+1) % 2 == 0`), а не от константы. **Плюсы.** - Дёшево, ломает поиск «магических чисел». - Оpaque-предикаты добавляют ветки, по которым деобфускатор должен пройти. **Минусы / цена.** - Ребёнок числа раскрывается за минуты любым символном вычислителем (`executor` для `LCONST/ICONST`-цепочек). - Развдувание кода растёт по квадрату от числа вставок. - Шаблонный opaque-предикат (всегда один и тот же `A+B`) узнаётся паттерном - отсюда требование нескольких схем. **Статус: готово** для `int`/`long` (`xor`/`add`/`shift` + opaque-предикаты); `float`/`double` не трогаются. ## 4. Обфускация контрольного потока (`Flow`) **Как работает.** CFG перестраивается одной из схем: `state-spread` (таблица состояний + `switch`), `computed-switch` (счётчик-переменная вместо меток), `butterfly` (обратные переходы), `exception-goto` (управление через `throw/catch`). **Плюсы.** - Линейный проход кода больше не читается как исходник. - В сочетании с opaque-предикатами резко поднимает стоимость трассировки. **Минусы / цена.** - **Самая дорогая и самая опасная техника.** Требует пересчёта `StackMapTable` для классов версии ≥ 50 - иначе JVM не грузит класс. - Ломает профилировщики, coverage-инструменты и отладку. - Нужен верификатор: любая ошибка в CFG - это `VerifyError` в рантайме. - Шаблонная схема (все методы перестроены одинаково) детектируется гистограммой длины блоков. **Статус: готов** (`noxis-passes/src/flow/`). Одна схема на метод (ротация через PRNG) плюс нарезка тела `goto`-сплайсами во всех схемах; кадр `StackMapTable` пересчитывается после каждой перестройки. Приёмка - настоящий `java -Xverify:all` и исполнение под JVM (`calc` печатает 45 после всех схем). Ограничения: `` пропускается (форма super-call-first); методы с `jsr`/`ret`, короче 3 инструкций и длиннее `max_insns` пропускаются; `encrypt_block_bodies` добавляет opaque-guard на входе. Два правила, выученных на крови (см. `StackMapTable`): вставленная метка не должна делить байт-смещение с чужой (иначе кадры мерджатся из несовместимых состояний - после каждой такой метки `nop`), а инструкция после терминальной обязана иметь свой кадр (поэтому dead-блоки входят через opaque-ветвь и кончаются `athrow`, а не висят недостижимыми). ## 5. Шум (`Noise`) **Как работает.** Junk-классы и junk-аннотации, вплетённые в граф достижимости (`weave_reachable`): класс считается достижимым, если на него есть ссылка из настоящего кода. **Плюсы.** - Дешёвый объём: файлов много, логики ноль. - Мусор в дереве файлов ухудшает автоматический разбор. **Минусы / цена.** - Reachability-анализ отсеивает junk мгновенно: класс, на который никто не ссылается, - мёртвый груз. Отсюда обязательное `weave_reachable`. - Пустые методы/тела - признак, который ищут первым. - Раздувание jar. **Статус: готово** (junk-классы + `weave_reachable`; junk-аннотации пропускаются - сырые атрибуты не несут свежие индексы пула). ## 6. Отладочная информация (`DebugInfo`) **Как работает.** Strip `LineNumberTable`, `LocalVariableTable`; подмена `SourceFile` на ложное имя; опционально strip `Signature`/`InnerClasses`. Плюс *ложная* отладочная информация - чужие имена переменных и номера строк. **Плюсы.** - Практически бесплатно. - Восстанавливает исходные имена переменных и файлов - прямая утечка. **Минусы / цена.** - Убивает нормальные stack trace'и (поэтому маппинги в отчёте обязательны). - Слишком «чистый» класс (`LineNumberTable` есть, а `SourceFile` = `X.java`, которого нет) - тоже признак. **Статус: готово** (strip + `spoof_source_file`, детерминированная подмена от мастер-сида). Не реализовано: `fake_line_numbers` (ложные номера строк, а не только `SourceFile`) и `strip_source_file` (полное удаление вместо подмены) - см. [`todo.md`](../todo.md) §3.6. ## 7. Ресурсы (`Resources`) **Как работает.** Патч текстовых ресурсов по маппингу: `fabric.mod.json`, `*.mixins.json`, refmap, access widener, `META-INF/services/*`. Вырезание старых подписей `*.SF/*.RSA/*.DSA/*.EC`. Ограничение дубликатов. **Плюсы.** - Без этого обфускация Fabric-мода просто не стартует. - Убранные подписи - иначе jar считается подделанным. **Минусы / цена.** - Патч только текстовых ресурсов: бинарные (картинки, классы в `versions/`) трогать нельзя. - Промах в патче → тихо неработающий refmap, ловится только запуском. **Статус: готово** (патч текстовых ресурсов по маппингу, включая `fabric.mod.json`/`*.mixins.json`/`META-INF/services/*`; срез подписей в `noxis-jar`). Не реализовано: типизированный патч бинарных/специфичных форматов (refmap, access widener) сверх общего boundary-replace. ## Сводная таблица | техника | отдача | риск | цена | статус | |---|---|---|---|---| | Rename | ★★★★★ | низкий | низкая | готово | | Strings | ★★★★ | средний | средняя | готово | | Resources | ★★★★★ | низкий | низкая | готово | | DebugInfo | ★★★ | низкий | низкая | готово | | Constants | ★★ | низкий | низкая | готово (int/long) | | Noise | ★ | низкий | раздувание | готово (без аннотаций) | | Flow | ★★★★ | **высокий** | высокая | план, кадры готовы | Вывод, который стоит держать в голове: **Rename + Resources + DebugInfo** дают большую часть защиты за небольшую цену. Flow - единственная техника, которая может сломать рантайм, и она же единственная, чья отдача не покрывает риск без верификатора.