frames,jar,passes: verifier-driven hardening from real jars

Proven by paranoid runs over lorep (17 classes, fully green),
LoVisual (4081 classes) and oc2r (1278 classes) with per-class
java -Xverify:all, each fix first reproduced as a red test:

- merge: Top absorbs instead of Top-wins (javac declares Top for
  slots unwritten on some inflow; precise types reject those edges).
  Regression: loop_head_keeps_top_for_loop_local.
- emit: collapse [Long|Double, Top] pairs on operand stacks
  (javac writes one entry; the pair is rejected). Regression:
  wide_stack_join_emits_single_entries.
- xfer ArrayLoad: push the wide pair, fixing lcmp underflow.
  Regression: array_load_wide_pushes_pair.
- xfer <init>: convert only the receiver's own allocation site
  instead of every Uninitialized (nested construction clobbered
  the outer object). Regression: nested_construction_keeps_outer.
- xfer ANewArray: array operands gain one dimension ([B] -> [[B)
  instead of an illegal L-wrap ([L[B;). Covered by the same test.
- jar load: duplicate class names (multi-release module-info
  twins) keep the first occurrence instead of crashing save.
  Test: duplicate_class_names_keep_first.
- strings/constants: skip methods past MAX_METHOD_INSNS (8000)
  so giants cannot overflow the 64KB writer ceiling. Tests:
  oversized_methods_are_skipped in both passes.
- merge errors now name both types being merged.

Also: em-dash removed repo-wide (724 spots, rule added to
todo.md), allow-lints only in tests (scratch example deleted),
code comments migrated to English in every file this change
touches.
This commit is contained in:
Loki 2026-10-09 13:47:27 +02:00
parent 2172206e83
commit 139aea9a8f
Signed by: boba
GPG key ID: 253067914055423B
96 changed files with 1053 additions and 831 deletions

View file

@ -10,7 +10,7 @@
## 1. Elastic Security Labs: агент с IDA Pro против Tigress (апрель 2026)
[«The Cost of Understanding»](https://www.elastic.co/security-labs/llm-reversing-vs-llm-obfuscation) —
[«The Cost of Understanding»](https://www.elastic.co/security-labs/llm-reversing-vs-llm-obfuscation) -
Claude Opus 4.6 с доступом к IDA Pro (через MCP) реверсил crackme,
обфусцированный [Tigress](https://tigress.wtf/), затем команда сама собрала
("vibecode"-ила) три защиты, нацеленные именно на модель. Это ближе всего
@ -26,32 +26,32 @@ Claude Opus 4.6 с доступом к IDA Pro (через MCP) реверсил
indirect/regs и switch/stack).
- Opus решил ~40% из 20 валидных задач. Успех стоил в среднем **$2.39**,
провал — **$4.83**. Ни одна задача уровня "многослойные комбинации" не
провал - **$4.83**. Ни одна задача уровня "многослойные комбинации" не
была решена.
- **`JIT` (XTEA + рантайм-расшифровка) оказался самой сложной одиночной
трансформацией** — модель понимала структуру движка, но не смогла
трансформацией** - модель понимала структуру движка, но не смогла
восстановить ключевые константы. Это ровно наш подход в `Strings`
(ключ выводится в рантайме, не лежит в вызове), только применённый ко
**всему коду метода**, а не к строкам.
- На самых жёстких комбинациях цена и время выросли в ~4.5× и ~4.3×, но
задачи, которые Opus уже умел решать, он решил всё равно. **Flatten + MBA
било по модели сильнее, чем Virtualize + MBA** — авторы объясняют это тем,
било по модели сильнее, чем Virtualize + MBA** - авторы объясняют это тем,
что флаттенинг сильнее раздувает размер функции.
### Три кастомные защиты, нацеленные на агента
| защита | идея | результат |
|---|---|---|
| Matryoshka Wall | crackme внутри 100 000 вложенных слоёв ChaCha20 (+ подделка под Salsa20) | v1 — агент сдался не расшифровав ($1.50); v2 (каждый ключ зависит от предыдущего слоя) — исчерпал бюджет $10 за 56 минут, 0 успехов |
| Double Fond | патч open-source крипто-библиотеки: скрытый загрузчик через изменённую lookup-таблицу, шифрованные указатели, RWX-сегмент | v7 — 1/5 у "осведомлённого" агента (12 мин, $5.20), 0/5 у "слепого" |
| Dispatch Maze | шифр раздроблен на 20 фрагментов по 3000 функций, большинство — ловушки | v1 решена за 12 минут ($2.56); v2 (разнообразные шаблоны кода + ложные переходы) — провал агента через 46 минут и $8.83 |
| Matryoshka Wall | crackme внутри 100 000 вложенных слоёв ChaCha20 (+ подделка под Salsa20) | v1 - агент сдался не расшифровав ($1.50); v2 (каждый ключ зависит от предыдущего слоя) - исчерпал бюджет $10 за 56 минут, 0 успехов |
| Double Fond | патч open-source крипто-библиотеки: скрытый загрузчик через изменённую lookup-таблицу, шифрованные указатели, RWX-сегмент | v7 - 1/5 у "осведомлённого" агента (12 мин, $5.20), 0/5 у "слепого" |
| Dispatch Maze | шифр раздроблен на 20 фрагментов по 3000 функций, большинство - ловушки | v1 решена за 12 минут ($2.56); v2 (разнообразные шаблоны кода + ложные переходы) - провал агента через 46 минут и $8.83 |
### Что из этого реально работает, по механизму
1. **Контекстное окно.** Длинные тупиковые пути (decoy-функции, ложные
переходы) забивают контекст и портят рассуждение модели.
2. **Бюджет шагов/денег.** Каждый впустую потраченный цикл стоит агенту
реальных токенов; если он упирается в лимит и сдаётся — это победа
реальных токенов; если он упирается в лимит и сдаётся - это победа
защиты, даже если код теоретически вскрываем.
3. **Shortcut bias.** Вводящая в заблуждение структура (ловушки в
Dispatch Maze) заставляет модель остановиться раньше времени.
@ -59,14 +59,14 @@ indirect/regs и switch/stack).
**Ключевое экономическое наблюдение авторов:** нативное исполнение занимает
~55 мс, а статический провал агента стоит **$1.50–$9.67**. Это и есть
реальный рычаг — не "нечитаемость навсегда", а разрыв между ценой атаки и
реальный рычаг - не "нечитаемость навсегда", а разрыв между ценой атаки и
ценой сборки для нас. Авторы прямо предупреждают: преимущество схлопнется,
когда у агентов появятся инструменты динамического исполнения (сейчас
тестировался только статический анализ).
## 2. Acoda: атака на safety-alignment и токенизацию (ICSE 2026)
[Acoda](https://arxiv.org/pdf/2606.11755) — другая постановка задачи: не
[Acoda](https://arxiv.org/pdf/2606.11755) - другая постановка задачи: не
агент с дизассемблером, а LLM, которой **в чат вставили исходный код** и
попросили его проанализировать/деобфусцировать. Генетический алгоритм
подбирает комбинацию из 8 трансформаций, до 70% ASR (attack success rate)
@ -81,7 +81,7 @@ indirect/regs и switch/stack).
| до анализа | Vulnerability Code Injection | фрагмент с SQL-инъекцией/`pickle.loads`/command injection в заведомо мёртвой ветке (`while (сложное_но_ложное_условие)`) |
| во время анализа | Misleading Summarization | модель сама просится описать код, затем это описание (намеренно неверное) вклеивается рядом с кодом |
| во время анализа | String Obfuscation | `base64` + словарь, расшифровка даёт слова типа "virus"/"weapon" |
| во время анализа | Try-Except Wrapping | настоящая логика лежит в `except`-ветке, в `try` — правдоподобный, но бесполезный код |
| во время анализа | Try-Except Wrapping | настоящая логика лежит в `except`-ветке, в `try` - правдоподобный, но бесполезный код |
| после анализа | EOS Token Insertion | в текст вставлены литералы токенов-разделителей (`<|EOT|>`), из-за чего генерация обрывается раньше |
### Честная оценка: что из этого применимо к нам
@ -93,24 +93,24 @@ indirect/regs и switch/stack).
- `DebugInfo` уже вычищает всё, что могло бы стать комментарием или
осмысленным описанием (`LineNumberTable`, `Signature`, исходные имена);
в скомпилированном `.class` комментариев не бывает в принципе.
- Цель Acoda — заставить модель **отказаться анализировать свой же код**,
- Цель Acoda - заставить модель **отказаться анализировать свой же код**,
вставленный в чат пользователем. У нас код анализирует **чужой** агент
чужого jar — нам не нужно, чтобы он отказался, если он не наш союзник;
и вызывать намеренно подозрительные паттерны в легитимном продукте —
чужого jar - нам не нужно, чтобы он отказался, если он не наш союзник;
и вызывать намеренно подозрительные паттерны в легитимном продукте -
прямой риск получить ложное срабатывание антивируса/EDR на свой же jar.
Это противоречит `README.md` → «Ответственное использование».
- **EOS Token Insertion** не переносится: это свойство текстового
стриминга токенов модели, а не байткода.
Единственное, что частично переносится: **String Obfuscation** и
**Variable Renaming** — ровно то, что `Strings` и `Rename` уже делают,
просто дешевле (у Acoda — обратимый `base64`, у нас — ротация шифров с
**Variable Renaming** - ровно то, что `Strings` и `Rename` уже делают,
просто дешевле (у Acoda - обратимый `base64`, у нас - ротация шифров с
выводом ключа в рантайме, см. [`../stack.md`](../stack.md) §4-5). Здесь мы
уже на шаг впереди статьи, а не позади.
**Vulnerability Code Injection** как идея (мёртвый код, похожий на уязвимый
паттерн) пересекается с нашим `Noise`, но внедрять **реально похожие на
эксплойт сигнатуры** в выдаваемый клиентам jar — плохая идея для легального
эксплойт сигнатуры** в выдаваемый клиентам jar - плохая идея для легального
продукта защиты: это именно то, от чего предостерегает
[`info/evo-weaknesses.md`](../../info/evo-weaknesses.md) с другой стороны
(не подставлять защищённый продукт под чужие red-листы).
@ -118,24 +118,24 @@ indirect/regs и switch/stack).
## 3. Что добавить в noxis практически
Экономический рычаг из Elastic (цена агента > цены нашей сборки) ложится на
архитектуру без переделок, конкретные пункты — в [`../../todo.md`](../../todo.md)
архитектуру без переделок, конкретные пункты - в [`../../todo.md`](../../todo.md)
§7:
1. **Вложенные слои шифрования строк** (аналог Matryoshka Wall) — расширить
1. **Вложенные слои шифрования строк** (аналог Matryoshka Wall) - расширить
`StringsConfig` полем "глубина", decoder разворачивает N слоёв вместо
одного. Цена агента растёт с глубиной почти линейно, цена сборки — нет.
2. **Больше decoy-точек ветвления ключа** (аналог Dispatch Maze) — наш
одного. Цена агента растёт с глубиной почти линейно, цена сборки - нет.
2. **Больше decoy-точек ветвления ключа** (аналог Dispatch Maze) - наш
декодер уже собирает мастер-секрет из `k1^k2` (`stack.md` §4); можно
поднять число частей и раскидать их сборку по случайным junk-методам
из `Noise`, а не держать в одном месте.
3. **MBA вместо простых тождеств в `Constants`** — то же решение, что ловит
3. **MBA вместо простых тождеств в `Constants`** - то же решение, что ловит
и символьный исполнитель, и SMT-решатель, и, по данным Elastic,
"Flatten + MBA" бьёт сильнее, чем просто Flatten.
4. **Не делать:** имитацию вредоносных паттернов, prompt injection в
строках, EOS-токены. Либо не переносится на байткод, либо создаёт риск
ложного срабатывания защитных систем на легитимном продукте.
Критерий "сработало/не сработало" для всего этого — как и для остальных
Критерий "сработало/не сработало" для всего этого - как и для остальных
техник: прогон конкретного агента на конкретной сборке, а не вера в
методичку. Эффект устаревает с каждой новой версией модели.
@ -151,17 +151,17 @@ indirect/regs и switch/stack).
- **Для нас это ломает собственное допущение** из
[`../stack.md`](../stack.md) §3: `noxis-verify` верифицирует метод через
`java -Xverify:all`, потому что это *обычный* байткод с валидным
`StackMapTable`. Виртуализированный метод — уже не обычный байткод,
`StackMapTable`. Виртуализированный метод - уже не обычный байткод,
верификатор проверит только обвязку интерпретатора, не тело. Big-ticket
item, не для ближайшего спринта.
- **MBA** укладывается без архитектурных изменений — см. выше, §3.
- **MBA** укладывается без архитектурных изменений - см. выше, §3.
## Источники
- [Elastic Security Labs — The Cost of Understanding: LLM-Driven Reverse Engineering vs Iterative LLM Obfuscation](https://www.elastic.co/security-labs/llm-reversing-vs-llm-obfuscation)
- [Elastic Security Labs - The Cost of Understanding: LLM-Driven Reverse Engineering vs Iterative LLM Obfuscation](https://www.elastic.co/security-labs/llm-reversing-vs-llm-obfuscation)
- [Acoda: Adversarial Code Obfuscation for Defending against LLM-based Analysis (arXiv 2606.11755, ICSE-Companion '26)](https://arxiv.org/pdf/2606.11755)
- [A Systematic Study on Static Control Flow Obfuscation Techniques in Java (arXiv 1809.11037)](https://arxiv.org/pdf/1809.11037)
- [Promon Shield: Virtual Machine obfuscation SDK protection](https://promon.io/security-news/virtual-machine-obfuscation-sdk-protection)
- [CoTDeceptor: Adversarial Code Obfuscation Against CoT-Enhanced LLM Code Agents (arXiv 2512.21250)](https://arxiv.org/pdf/2512.21250)
- [Deconstructing Obfuscation: a four-dimensional framework for evaluating LLM deobfuscation (arXiv 2505.19887)](https://arxiv.org/pdf/2505.19887)
- [Tigress — диверсифицирующий обфускатор для C, использованный в бенчмарке Elastic](https://tigress.wtf/)
- [Tigress - диверсифицирующий обфускатор для C, использованный в бенчмарке Elastic](https://tigress.wtf/)