obfuskator/docs/learnings/priorities.md
loki5512344 a641e69455 Initial commit: noxis JVM bytecode obfuscator
Workspace of 9 crates (noxis, noxis-core, noxis-classfile, noxis-analysis,
noxis-passes, noxis-runtime, noxis-jar, noxis-verify, noxis-structure-check).
AGPL-3.0-only license, Rust 2024 edition, unsafe_code denied.
2026-10-08 22:01:03 +02:00

9 KiB
Raw Blame History

Что улучшим: стратегия и приоритеты

Материал собран из info/evo-improvements.md (план после разбора EvoVis) и info/evo-weaknesses.md (провалы). Здесь — что мы берём, в каком порядке и что осознанно не делаем.

Стратегия в одном абзаце

Максимум защиты при минимуме риска сломать рантайм. Дешёвые техники, дающие 80% результата, включаются по умолчанию; дорогие и опасные — включаются только после того, как для них есть верификатор. Каждая техника имеет измеримый критерий «сработала/не сработала», иначе это не защита, а мнение.

P0 — базовый уровень (дёшево, снимает главные утечки)

пункт что это пасс статус
A1 переименование всех символов: классы, поля, методы, локальные переменные, параметры, record-компоненты, enum-константы, SourceFile Rename ✅ готово
A2 вычистить LineNumberTable/LocalVariableTable/SourceFile/InnerClasses/Signature/MethodParameters DebugInfo план
A3 никаких сигнатурных имён у защитного кода, защитный код смешан с обычным Rename + Noise ✅ / план
A4 обфусцировать весь архив, включая ресурсы, mixin-JSON, access widener, service loader'ы Resources план
— маппинг в отчёт (иначе не читаем свои же stack trace) Rename ✅ готово

Это самый дешёвый блок и одновременно самый большой утечка в разобранном образце: оригинальные SourceFile у 160 классов, имена полей и методов, каталог модулей в атрибутах.

P1 — строки и константы

пункт что это пасс статус
B1 ключ на строку + ротация 3–5 схем + MAC + уникальный nonce Strings ✅ схемы готовы
B2 вывод ключа в рантайме, а не константа в вызове Strings + генератор декодера ⛔ блокер
B3 константы — не тождества, а вычисления, разнесённые по блокам Constants план
B4 ложная отладочная информация: чужие имена переменных и строк DebugInfo план

B2 — главное отличие от разобранного образца: у него ключ был константным выражением в вызове и расшифровывался статически, без JVM. Мастер-секрет должен собираться в рантайме из нескольких мест. Это требует сначала собрать класс-декодер (P2) — потому и блокер, а не «сделаем потом».

P2 — контрольный поток

пункт что это статус
C1 несколько схем флаттенинга вместо одной, с мешаниной внутри метода план
C2 верификатор обязателен: ошибка CFG = VerifyError план
C3 реальный объём ≠ раздувание: бюджет на размер и время план

Приоритет здесь строго подчинён: C2 раньше C1. Включать Flow без проверки кадров StackMapTable — это гарантированный битый jar. Подробности: ../stack.md §3.

P3 — измеримость (метрики, без которых ничего не доказано)

Обязательный блок, иначе фраза «обфускация есть» ничего не означает:

  1. Утечки имён. Список эталонных осмысленных идентификаторов; если хоть один остался в strings/константах — тест падает.
  2. Читаемость декомпилята. Прогон JADX/CFR → доля распознанных структур (try/catch, switch, циклы), доля классов с восстановленными именами.
  3. Противник. Минимальный скрипт-деобфускатор (свёртка констант, regex-паттерны). Если он снимает больше порога — техника провалена. Красный список паттернов держим в репозитории.
  4. Семантика. Эталонные программы до и после — 100% совпадение.
  5. Бюджеты. Размер ≤ N%, время исполнения ≤ M%, отдельно — бюджет раскрытия строк (кэш, чтобы не платить на каждый вызов).
  6. Стабильность между сборками. Имена и схемы меняются от билда к билду: у разобранного образца шифр был один на все версии — готовое окно для обучения по частотам.

Это блок noxis-verify: красные паттерны + java -Xverify:all + метрики. Единственное место, где «нам кажется защищённым» заменяется цифрой.

P4 — долгий горизонт (пока не делаем)

пункт что это почему не сразу
D1 маленький VM/interpreter для ценных кусков цена окупается только на ключевом коде
D2 split-function / раскрываемое по шагам тело режет обычный профилировщик, сложная отладка
D3 мягкий анти-анализ в рантайме требует своего нативного компонента
D4 честный anti-tamper, а не «зашейрь ключ» целая инженерная задача, не пасс
E собственный нативный/Rust-цель вместо JVM другой продукт, не наша цель

D1 — явно ограничен: включать только для действительно ценных функций, остальное оправдания не имеет. E — отдельный продукт; если он когда-нибудь понадобится, наш IR как раз делает его возможным (те же пассы, другой writer).

Что мы добавили сверх исходного плана

  1. Структурный гейт при компиляции. Лимиты «250 строк / 4 файла» гоняются в build.rs — см. ../README.md. Без этого план P0–P3 превращается в один гигантский файл через месяц.
  2. Собственный IR с метками. Исходный план предполагал работу поверх готового парсера. Insn::Mark(Label) убирает пересчёт смещений из всех пассов — это меняет цену каждой техники, меняющей CFG.
  3. Детерминированный PRNG с доменным разделением. Без этого пункт «стабильность между сборками» невозможно проверить тестом.
  4. Keep как отдельный крейт (noxis-analysis), а не флаг CLI. Рефлексия, JNI, сериализация и entrypoint'ы — это анализ иерархии, а не настройка, которую надо помнить руками.

Порядок выполнения

Смотрите ../../todo.md. Кратко:

P0 DebugInfo + Resources      → снимает главные утечки, почти бесплатно
P1 Strings (B2) + Constants   → ключ выводится, схемы ротируются
P1 backend (ristretto) + frames
P2 Flow (C1) после верификатора
P3 метрики (F) и красные паттерны