obfuskator/docs/learnings/priorities.md
loki5512344 2172206e83
passes: implement control-flow pass (4 schemes + rotation)
FlowPass rebuilds one method per scheme picked from FlowConfig:
state-spread (dense tableswitch dispatcher), computed-switch
(sparse lookupswitch over a counter local), butterfly (branch
inversion), exception-goto (dispatcher under try with a dead
catch-all), plus opaque goto-splices inside every method and
StackMapTable recomputation afterwards. Now in standard_pipeline
(between constants and noise); --no-flow is an honest off-flag.

Acceptance is real JVM, not shapes: per-scheme verify, rotation
spread, calc-prints-45 execution, paranoid smoke on lorep-1.1.0
(131 methods rebuilt, no new failures over standard).

Two verifier rules learned the hard way are now construction
invariants: no inserted mark shares a bytecode offset with a
foreign one (nop pads), and no frameless gap follows a terminal
(dead blocks are opaque-entered and throw).

Also fixes a latent frame-emitter bug the pass exposed: wide
stack values were written as slot pairs ([Double, Top]) while
javac writes one entry ([Double]); HotSpot rejects the pair.
Stacks now collapse like locals (emit::stack_vars), with a
regression test in classfile/tests/frames.rs.
2026-10-09 11:11:55 +02:00

9.5 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 ✅ готово (int/long)
B4 ложная отладочная информация: чужие имена переменных и строк DebugInfo ✅ SourceFile

B2 — главное отличие от разобранного образца: у него ключ был константным выражением в вызове и расшифровывался статически, без JVM. Мастер-секрет теперь собирается в рантайме из двух частей (k1 ^ k2) декодером класса — см. ../stack.md §4. B4 пока покрывает только SourceFile; ложные номера строк (fake_line_numbers) — см. ../../todo.md §3.6.

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

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

Приоритет здесь строго подчинён: C2 раньше C1. Кадры StackMapTable и приёмка java -Xverify:all уже готовы (см. ../stack.md §3) — единственное, чего не хватает, это сами схемы флаттенинга (C1).

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

Обязательный блок, иначе фраза «обфускация есть» ничего не означает. Крейт noxis-verify реализован — вот что из списка уже цифра, а что ещё мнение:

  1. Утечки имён. ✅ collect_names/find_leaks — сверка идентификаторов с учётом keep-списка.
  2. Читаемость декомпилята. ⬜ Прогон JADX/CFR → доля распознанных структур — не начато.
  3. Противник. ✅ scan_secrets + RED_PATTERNS держим в репозитории; ⬜ полноценный скрипт-деобфускатор (свёртка констант) как тест — план.
  4. Семантика. ⬜ Эталонные программы до/после — не начато (сейчас семантику подтверждает только java -Xverify:all).
  5. Бюджеты. ✅ размер (inflation_ratio/check_inflation); ⬜ время исполнения — не начато.
  6. Стабильность между сборками. ✅ mappings_changed — маппинги обязаны отличаться между прогонами с разным seed.

Итог: 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–P3 и P3-измеримость уже готовы; единственный крупный пункт, который остался, — схемы Flow (C1), потому что верификатор (C2) для них уже есть:

✅ P0 DebugInfo + Resources      → главные утечки сняты
✅ P1 Strings (B2) + Constants   → ключ выводится, схемы ротируются
✅ P1 backend (ristretto) + frames
✅ P2 Flow (C1) — готов (09.10.2026): 4 схемы + ротация + мешанина, приёмка настоящим `java`
✅ P3 метрики, красные паттерны, CLI