passes: implement control-flow pass (4 schemes + rotation)
FlowPass rebuilds one method per scheme picked from FlowConfig: state-spread (dense tableswitch dispatcher), computed-switch (sparse lookupswitch over a counter local), butterfly (branch inversion), exception-goto (dispatcher under try with a dead catch-all), plus opaque goto-splices inside every method and StackMapTable recomputation afterwards. Now in standard_pipeline (between constants and noise); --no-flow is an honest off-flag. Acceptance is real JVM, not shapes: per-scheme verify, rotation spread, calc-prints-45 execution, paranoid smoke on lorep-1.1.0 (131 methods rebuilt, no new failures over standard). Two verifier rules learned the hard way are now construction invariants: no inserted mark shares a bytecode offset with a foreign one (nop pads), and no frameless gap follows a terminal (dead blocks are opaque-entered and throw). Also fixes a latent frame-emitter bug the pass exposed: wide stack values were written as slot pairs ([Double, Top]) while javac writes one entry ([Double]); HotSpot rejects the pair. Stacks now collapse like locals (emit::stack_vars), with a regression test in classfile/tests/frames.rs.
This commit is contained in:
parent
39178420e4
commit
2172206e83
15 changed files with 1360 additions and 103 deletions
|
|
@ -123,6 +123,7 @@ Rename → Resources → Strings → Constants → Flow → Noise → DebugInfo
|
|||
| **готово** | 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-тесты |
|
||||
| **готово** | пасс `flow` (4 схемы + ротация + мешанина, приёмка настоящим `java`) |
|
||||
| **план** | фикстуры и regression-тесты |
|
||||
|
||||
Детали выбора — [`stack.md`](stack.md), план — [`../todo.md`](../todo.md).
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
| 1 | ключ лежит рядом с шифротекстом | вывод мастер-секрета в декодере, ключ из `master‖salt` | Strings | **готово** |
|
||||
| 2 | переименованы типы, но не члены | rename полей/методов + семейства virtual | Rename | **готово** |
|
||||
| 3 | отладочная информация нетронута | strip + ложный `SourceFile`/номера строк | DebugInfo | **готово** (`SourceFile`; номера строк — план) |
|
||||
| 4 | шаблонный flattening | 4 схемы CFG + мешанина внутри метода | Flow | план (кадры готовы) |
|
||||
| 4 | шаблонный flattening | 4 схемы CFG + мешанина внутри метода | Flow | **готово** (4 схемы, приёмка `java`) |
|
||||
| 5 | opaque-предикаты = `A+B` | ротация тождеств + предикаты от состояния | Constants | **готово** (int/long) |
|
||||
| 6 | junk-классы вне графа reachability | `weave_reachable` — обязательная вплетённость | Noise | **готово** (без аннотаций) |
|
||||
| 7 | имена говорят «я защита» | все артефакты получают данные псевдонимы | Rename | **готово** |
|
||||
|
|
|
|||
|
|
@ -115,6 +115,6 @@ writer).
|
|||
✅ P0 DebugInfo + Resources → главные утечки сняты
|
||||
✅ P1 Strings (B2) + Constants → ключ выводится, схемы ротируются
|
||||
✅ P1 backend (ristretto) + frames
|
||||
⬜ P2 Flow (C1) — единственное, что ждёт реализации; верификатор готов
|
||||
✅ P2 Flow (C1) — готов (09.10.2026): 4 схемы + ротация + мешанина, приёмка настоящим `java`
|
||||
✅ P3 метрики, красные паттерны, CLI
|
||||
```
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
| `StringDecoderGenerator` | 205 | генерит класс-декодер | `noxis-runtime` + P2 генератор |
|
||||
| `ObfuscatorRunner` | 202 | конвейер, оркестрация | `noxis-passes::standard_pipeline` ✅ |
|
||||
| `NumberObfuscator` | 185 | константы / opaque | пасс `constants` |
|
||||
| `ControlFlowObfuscator` | 183 | flattening | пасс `flow` (ждёт `StackMapTable`) |
|
||||
| `ControlFlowObfuscator` | 183 | flattening | пасс `flow` ✅ (4 схемы, свои кадры) |
|
||||
| `FabricKeepRuleScanner` | 147 | keep из конфига Fabric | `noxis-analysis::keep` ✅ |
|
||||
| `Main` | 134 | CLI-точки | `noxis` (clap) |
|
||||
| `StringEncryptor` | 132 | AES-128 строк + chunk'и | `noxis-runtime::scheme` ✅ |
|
||||
|
|
|
|||
|
|
@ -104,7 +104,18 @@ opaque-предикаты — условия, истинность которы
|
|||
- Шаблонная схема (все методы перестроены одинаково) детектируется
|
||||
гистограммой длины блоков.
|
||||
|
||||
**Статус: не реализован; блокер — `StackMapTable` (см. [`stack.md`](stack.md)).**
|
||||
**Статус: готов** (`noxis-passes/src/flow/`). Одна схема на метод (ротация
|
||||
через PRNG) плюс нарезка тела `goto`-сплайсами во всех схемах; кадр
|
||||
`StackMapTable` пересчитывается после каждой перестройки. Приёмка —
|
||||
настоящий `java -Xverify:all` и исполнение под JVM (`calc` печатает 45
|
||||
после всех схем). Ограничения: `<init>` пропускается (форма
|
||||
super-call-first); методы с `jsr`/`ret`, короче 3 инструкций и длиннее
|
||||
`max_insns` пропускаются; `encrypt_block_bodies` добавляет opaque-guard
|
||||
на входе. Два правила, выученных на крови (см. `StackMapTable`):
|
||||
вставленная метка не должна делить байт-смещение с чужой (иначе кадры
|
||||
мерджатся из несовместимых состояний — после каждой такой метки `nop`),
|
||||
а инструкция после терминальной обязана иметь свой кадр (поэтому dead-блоки
|
||||
входят через opaque-ветвь и кончаются `athrow`, а не висят недостижимыми).
|
||||
|
||||
## 5. Шум (`Noise`)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue