obfuskator/info/evo-obfuscation.md
loki5512344 139aea9a8f
frames,jar,passes: verifier-driven hardening from real jars
Proven by paranoid runs over lorep (17 classes, fully green),
LoVisual (4081 classes) and oc2r (1278 classes) with per-class
java -Xverify:all, each fix first reproduced as a red test:

- merge: Top absorbs instead of Top-wins (javac declares Top for
  slots unwritten on some inflow; precise types reject those edges).
  Regression: loop_head_keeps_top_for_loop_local.
- emit: collapse [Long|Double, Top] pairs on operand stacks
  (javac writes one entry; the pair is rejected). Regression:
  wide_stack_join_emits_single_entries.
- xfer ArrayLoad: push the wide pair, fixing lcmp underflow.
  Regression: array_load_wide_pushes_pair.
- xfer <init>: convert only the receiver's own allocation site
  instead of every Uninitialized (nested construction clobbered
  the outer object). Regression: nested_construction_keeps_outer.
- xfer ANewArray: array operands gain one dimension ([B] -> [[B)
  instead of an illegal L-wrap ([L[B;). Covered by the same test.
- jar load: duplicate class names (multi-release module-info
  twins) keep the first occurrence instead of crashing save.
  Test: duplicate_class_names_keep_first.
- strings/constants: skip methods past MAX_METHOD_INSNS (8000)
  so giants cannot overflow the 64KB writer ceiling. Tests:
  oversized_methods_are_skipped in both passes.
- merge errors now name both types being merged.

Also: em-dash removed repo-wide (724 spots, rule added to
todo.md), allow-lints only in tests (scratch example deleted),
code comments migrated to English in every file this change
touches.
2026-10-09 13:47:27 +02:00

42 lines
2.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Обфускация EvoVis / EvolutionDLC
## Три уровня
1. **Имена** - 1277 классов вынесены в default package с именами вида `a…aa…`;
миксины разбросаны по пакетам `a/` и `b/`. Смысловых имён классов в коде нет.
2. **Строки** - все строковые литералы зашифрованы вызовом `awc.d(cipher, key)`:
- LCG: `state = state * 1103515245 + 12345` (32-бит переполнение);
- байт: `cipherChar ^ (state >>> 24)`;
- результат - UTF-8 строка.
Ключи передаются «непрозрачными» целыми (см. ниже). Второй вариант `awc.g(...)`
- то же самое плюс session-gate: строка расшифровывается только после
проверки сессии через рефлексию `aln.key()`.
3. **Контрольный поток** - тела методов разложены в машину состояний:
`while (true) { switch (состояние) { case <обфуск-константа>: ...; состояние = <следующая>; } }`.
Переходы между блоками - через присваивание целому и обратный `goto`.
Из-за этого объём декомпилированного кода раздут примерно в 25 раз
относительно реального.
## Кодировка целочисленных констант
Все литералы записаны через тождества (по модулю 2³²):
| Паттерн | Равен |
|---|---|
| `(A ^ B) + ((A & B) << 1)` | `A + B` |
| `(A \| B) + (A & B)` | `A + B` |
Так прячутся даже `0`, `1`, `16`, `255`. Например, `true`/`false` приходят как
выражение, равное `1`/`0` (javac такого не генерирует - это работа обфускатора).
Сдвиги и маски тоже закодированы: сдвиговые константы в байткоде могут быть
отрицательными - JVM маскирует количество бит по младшим 5 битам
(`ishl/ishr` со сдвигом `-8` работает как сдвиг на `24`), поэтому `>> -8`
в байткоде эквивалентен `>> 24` в исходнике.
## Прочее
- Декой-классы обфускатора - пустышки с бессмысленными членами, в логике не участвуют.
- Из-за имён-ключевых слов (`if`, `do`, …) декомпилятор переименовывает такие
классы (`Cif`, `Cdo`) - в самом jar классов с такими именами нет.