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.
This commit is contained in:
commit
a641e69455
140 changed files with 17197 additions and 0 deletions
76
info/evo-weaknesses.md
Normal file
76
info/evo-weaknesses.md
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# Где защита 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) в этом билде **не вызывается ни разу**.
|
||||
Loading…
Add table
Add a link
Reference in a new issue