obfuskator/docs/architecture.md
loki5512344 c3b8d4dd62 docs: sync status with code, add AI-resistance research, restyle README
Documentation had drifted from the actual implementation: the noxis
(CLI) and noxis-verify crates were fully built and tested but every
doc called them "not started". Verified against the code (build +
test run) and corrected status across README.md, todo.md,
architecture.md, techniques.md and learnings/{lessons,priorities}.md.

Also:
- restyled README.md (badges, no em dashes, usage examples, dropped
  the redundant "Принципы" section already covered elsewhere)
- added docs/learnings/ai-resistance.md: a sourced writeup of what
  actually works against LLM-driven reverse engineering (Elastic
  Security Labs' Opus 4.6 vs Tigress study, the Acoda paper) and what
  of it applies to noxis, plus concrete todo.md §7 items
- noxis-structure-check: the 250-line/4-file gate was scanning
  docs/**, which fought against writing documentation freely; it now
  only scans crates/*/{src,tests,benches} and only counts .rs files
2026-10-08 22:34:00 +02:00

8.2 KiB
Raw Blame History

Архитектура

Слои

                 ┌──────────────────────────┐
   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

почему именно так:

  1. Rename первым — все остальные техники порождают новые ссылки (LDC-шум, вызовы декодера, junk-классы), и они уже должны быть в обфусцированных именах.
  2. Resources сразу после Rename — патч fabric.mod.json, *.mixins.json, refmap и access widener должен видеть итоговые имена классов.
  3. Strings и Constants — работают по телам методов, ещё не искажённым control-flow'ем.
  4. Flow — искажает CFG. Если поставить его раньше, Strings/Constants перестанут находить свои шаблоны в инструкциях.
  5. Noise — junk-классы и аннотации добавляются в самом конце, когда целевой граф уже финален.
  6. DebugInfo последним — strip/spoof отладочной информации после всех трансформаций, чтобы не породить свежие LineNumberTable.

Статус

этап что
готово IR, модель, конфиг, seed/PRNG, маппинги, анализ, keep, гейт правил, format::parse/write поверх ристретто, вывод кадров StackMapTable
готово пассы rename/resources/strings/constants/noise/debug, генератор декодера, вывод мастер-секрета в рантайме
готово noxis-verify (red-паттерны, java -Xverify:all, метрики), CLI (obfuscate/analyze/patterns)
план схемы пасса flow (кадры для них уже считаются), фикстуры и regression-тесты

Детали выбора — stack.md, план — ../todo.md.