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.
8.2 KiB
8.2 KiB
Архитектура
Слои
┌──────────────────────────┐
CLI (noxis) │ clap → конфиг → отчёт │
└────────────┬─────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ noxis-jar │ │noxis-passes │ │noxis-verify │
│ ZIP, ресурсы│ │ трансформации│ │ red-паттерны │
└──────┬──────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
│ ┌──────┴───────┐ │
│ │ noxis-core │ │
│ │ конфиг, seed │ │
│ │ маппинги, Pass│ │
│ └──────┬───────┘ │
│ │ │
▼ ▼ ▼
┌────────────────────────────────────────────────────────┐
│ noxis-classfile (IR) │
│ Program → ClassFile → Code → Insn (метки, не смещения)│
│ model/ format/ insn error │
└────────────────────────────────────────────────────────┘
▲
┌──────┴──────┐ ┌───────────────┐ ┌──────────────────┐
│noxis- │ │noxis-runtime │ │noxis-structure- │
│analysis │ │ шифры, ABI │ │check (build-time)│
└─────────────┘ └───────────────┘ └──────────────────┘
Зависимости направлены только вниз. Циклов нет: сборка конвейера живёт в
noxis-passes, потому что noxis-core знает только контракт Pass.
Крейты
| крейт | ответственность |
|---|---|
noxis-classfile |
собственный IR классовых файлов, чтение/запись, StackMapTable |
noxis-core |
конфигурация, детерминированный seed, маппинги, контекст и контракт Pass |
noxis-analysis |
иерархия наследования, entrypoint'ы, рефлексия по имени, keep-правила |
noxis-passes |
сами трансформации + сборка стандартного конвейера |
noxis-runtime |
схемы шифрования строк и ABI класса-декодера |
noxis-jar |
ZIP: чтение, перепаковка, вырезание подписей, патч ресурсов |
noxis-verify |
red-паттерны, java -Xverify:all, метрики |
noxis |
CLI |
noxis-structure-check |
автогейт структурных правил (см. README.md) |
Собственный IR
Пассы не работают с API стороннего парсера — они работают с моделью:
Program все классы + ресурсы архива
├─ ClassFile версия, флаги, this/super, поля, методы, атрибуты
│ ├─ Field
│ └─ Method
│ └─ Code max_stack/max_locals, insns, exception_table, frames
└─ Resource путь + байты внутри архива
Ключевое решение — Insn::Mark(Label) вместо байтовых смещений:
- любой пасс, меняющий порядок или длину инструкций, не пересчитывает jump-офсеты;
- пересчёт делается ровно один раз — в writer'е;
- в IR нет состояния, в котором «смещение уехало, а метка осталась».
Атрибуты, которые мы не понимаем, хранятся как Attribute::Raw { name, data }
и возвращаются на место без изменений — round-trip без потерь.
Контракт пасса
pub trait Pass: Send + Sync {
fn id(&self) -> PassId;
fn apply(&self, ctx: &mut Context, program: &mut Program)
-> Result<PassReport, CoreError>;
}
Context— всё состояние прогона: конфиг, мастер-сид, маппинги, заметки. Из него берётся RNG:ctx.rng("rename/classes")— доменное разделение, чтобы случайности разных пассов не были статистически коррелированы.PassReport— что трансформировано, что сознательно пропущено и почему.Report— собирает отчёты всех пассов + счётчики, сериализуется в JSON.
Детерминированность — свойство, а не пожелание: одинаковый seed на одинаковой версии даёт байт-в-байт одинаковый выход. Это проверяется тестами.
Конвейер
Порядок пассов — не деталь:
Rename → Resources → Strings → Constants → Flow → Noise → DebugInfo
почему именно так:
- Rename первым — все остальные техники порождают новые ссылки
(
LDC-шум, вызовы декодера, junk-классы), и они уже должны быть в обфусцированных именах. - Resources сразу после Rename — патч
fabric.mod.json,*.mixins.json, refmap и access widener должен видеть итоговые имена классов. - Strings и Constants — работают по телам методов, ещё не искажённым control-flow'ем.
- Flow — искажает CFG. Если поставить его раньше, Strings/Constants перестанут находить свои шаблоны в инструкциях.
- Noise — junk-классы и аннотации добавляются в самом конце, когда целевой граф уже финален.
- DebugInfo последним — strip/spoof отладочной информации после всех
трансформаций, чтобы не породить свежие
LineNumberTable.
Статус
| этап | что |
|---|---|
| готово | IR, модель, конфиг, seed/PRNG, маппинги, анализ, keep, переименование, шифры строк, гейт правил, format::parse/write поверх ристретто, вывод кадров StackMapTable |
| P2 | пассы строк/констант/шума/потока, генератор декодера, CLI |
| P2 | генерация класса-декодера, вывод мастер-секрета, остальные пассы |
| P3 | CLI, метрики, интеграция с java -Xverify:all |
Детали выбора — stack.md, план — ../todo.md.