obfuskator/info/evo-weaknesses.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

76 lines
5.6 KiB
Markdown
Raw 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 провалилась — чего нам делать нельзя
Разобрано полностью за один проход, без единого рантайм-хака. Каждый пункт —
что именно дало ключ атаки.
## 1. Ключ шифрования строк лежит рядом со шифротекстом
`awc.d("<cipher>", <opaque-выражение>)` — ключ это **константное выражение в вызове**.
Ни secrets, ни runtime-derivation: выражение просто сворачивается в `int` (любой
парсер вроде `scripts/expr.rs`, ~200 строк). Итог: **8993 вызова расшифрованы
детерминированно, 0 обращений к JVM**.
Хуже: шифр один на весь продукт (LCG с фиксированными `a/c`), т. е. зная одну
пару «шифротекст → текст», остальные не нужно даже сворачивать — можно учиться.
## 2. Обфусцированы только типы, но не члены
`onDraw`, `getProjectiles`, `BreakingBad`, `key()`, `isAuthorized()`, `setUser` —
открыто. Семантика класса читается по одному `javap -p`. Класс с тремя понятными
методами опознаётся за секунду, даже если имя `wb`.
## 3. Метаданные и отладочная информация не тронуты
- `SourceFile: "StringRuntime.java"` / `"KeyAssembler.java"` / `"PayloadCrypto.java"` —
оригинальные имена 160 классов (все миксины — 100%).
- `LineNumberTable` (10 218) + `LocalVariableTable` (151) — имена локальных переменных.
- `@rn(name=..., description=...)` — полный каталог всех 57 модулей с описаниями.
- `fabric.mod.json`, `*.mixins.json` (пути и состав), `accesswidener`, ресурсы.
Все эти вещи дешевле всего чинить и дороже всего отдавать.
## 4. Flattening сделан «по учебнику»
Один шаблон на весь продукт: `while(true) { switch (i + i2) }`, номер состояния —
сумма двух opaque-полей, переходы — присваивание пары. Один regex находит 3530
автоматов. Декомпилятор восстанавливает CFG, потому что:
- `switch` по **константным** меткам (метки не закодированы);
- все переходы — прямые присваивания в одном basic block;
- нет вычислимых на входе предикатов, нет переходов через таблицы/массивы;
- нет обфускации самих тел блоков (тела — обычный код).
## 5. Opaque-предикаты сводятся к `A+B`
`(A^B)+((A&B)<<1)` и `(A|B)+(A&B)` — это школьные тождества. Распознаются
регуляркой, сворачиваются в один проход, заодно выдают «бесплатный» фолдинг
остальных констант. Ни одного настоящего opaque-предиката (зависящего от состояния
памяти/окружения и всегда истинного) в коде нет.
## 6. Декой-классы детектируются reachability
42 файла, ни одного входящего ссылока, `dangling=0`. Мгновенно вычисляются BFS'ом
от entrypoint'ов (их, кстати, 4 и они раскрыты в `fabric.mod.json`). Балласт не
смешан с графом и не создаёт ложных рёбер.
## 7. Каждая защита живёт отдельно и называется своим именем
`StringRuntime`, `KeyAssembler`, `AttestClient`, `RuntimeGuard`, `SealedBoot`,
`NativeGuard` — названия прямо говорят атакующему, куда смотреть. Плюс open-source
идентификаторы: `aln` упомянут в рефлексии строкой, `awc` — по имени вызова.
## 8. Вторичные компоненты вообще без защиты
- `evo-agent.jar` (расшифровщик payload) — открытые строки, `SourceFile`, без обфускации.
- Лаунчер .NET — статическая XOR-таблица `b ^ i ^ 0xAA`, имена сервисов не тронуты.
- Защита ключа в нативе сильная, но **код мода** защищён слабее, чем ключ.
## 9. Шифр без аутентификации и без per-string salt
LCG+XOR: нет MAC, нет случайного сида на строку, нет индивидуального алгоритма.
Частичное повреждение шифротекста не обнаруживается, анализ по частотам работает.
## Опровержение (чтобы не повторять чужие ошибки в выводах)
- «Сдвиги в байткоде могут быть отрицательными (`>> -8` == `>> 24`)» — по всем
1277 классам **0** таких случаев (32 559 сдвигов, константные — всегда `1`).
- `awc.g(...)` (session-gate) в этом билде **не вызывается ни разу**.