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:
parent
2172206e83
commit
139aea9a8f
96 changed files with 1053 additions and 831 deletions
|
|
@ -25,18 +25,18 @@ cd ref/evo/reverse
|
|||
|---|---|---|---|
|
||||
| `mod/jar/META-INF/evo/native/win-x64/evoguard.dll` | 51 321 | 18 | своя маленькая JNI-библиотека (MinGW, PE32+) |
|
||||
| `mod/evojdk_x/bin/java.dll` | 111 104 | 6 | стандартный `java.dll` из JDK **+ новая функция** |
|
||||
| `mod/evojdk_x/bin/server/jvm.dll` | 15 463 936 | — | **патченный HotSpot** + новый экспорт |
|
||||
| `mod/evojdk_x/bin/server/jvm.dll` | 15 463 936 | - | **патченный HotSpot** + новый экспорт |
|
||||
|
||||
Отдельно (Java, не натив): `mod/evojdk_x/classes/java/lang/EvoShare.class`,
|
||||
исходник — `mod/evoshare/sources/java/lang/EvoShare.java` (JADX), вызывающий код —
|
||||
исходник - `mod/evoshare/sources/java/lang/EvoShare.java` (JADX), вызывающий код -
|
||||
`mod/agent-src/sources/evo/agent/key/NativeJvmShare.java`.
|
||||
|
||||
---
|
||||
|
||||
## 1. evoguard.dll — JNI-«нативный шейр»
|
||||
## 1. evoguard.dll - JNI-«нативный шейр»
|
||||
|
||||
Экспорт ровно один (плюс mingw-рантайм `_CRT_INIT`, `DllMainCRTStartup`, `atexit`,
|
||||
`sha256_block/update/final`, `env_flags_debug`, `hmac_sha256.constprop.0`) — **`JNI_OnLoad` нет**,
|
||||
`sha256_block/update/final`, `env_flags_debug`, `hmac_sha256.constprop.0`) - **`JNI_OnLoad` нет**,
|
||||
`nm`-имена не вычищены (COFF-символы остались).
|
||||
|
||||
### `Java_evo_systems_security_natives_NativeGuard_nativeShare(JNIEnv*, jclass, jstring hwid)` → `byte[32]`
|
||||
|
|
@ -67,7 +67,7 @@ return res;
|
|||
d42394b1dbe62f2d466fe6f8c4ee3cf5ce46949a032627a9ba00e8c5ef533bd2
|
||||
```
|
||||
|
||||
Итог: `NativeGuard.share(hwid) = HMAC-SHA256(key_eg, hwid_utf8)`, при детекте отладки —
|
||||
Итог: `NativeGuard.share(hwid) = HMAC-SHA256(key_eg, hwid_utf8)`, при детекте отладки -
|
||||
`XOR 0xA5` (не ошибка, а **молчаливо неверный шейр**).
|
||||
|
||||
### Платформенный паритет (`evoguard`)
|
||||
|
|
@ -83,9 +83,9 @@ d42394b1dbe62f2d466fe6f8c4ee3cf5ce46949a032627a9ba00e8c5ef533bd2
|
|||
ровно `d42394b1dbe62f2d466fe6f8c4ee3cf5ce46949a032627a9ba00e8c5ef533bd2`; следующие 32
|
||||
байта = тот же ключ с XOR `0x5c`. SHA-IV рядом (`0x180003090` = `67e6096a…`).
|
||||
Во всех сборках есть строки `IsDebuggerPresent`, `jdwp`, `agentlib`, `JAVA_TOOL_OPTIONS`,
|
||||
`_JAVA_OPTIONS` — логика антиотладки одинаковая. **Linux-сборки натива нет вообще**.
|
||||
`_JAVA_OPTIONS` - логика антиотладки одинаковая. **Linux-сборки натива нет вообще**.
|
||||
|
||||
Потребитель: `alm.java` (KeyAssembler) — шейр идёт в 4-of-4 сборку Шамира:
|
||||
Потребитель: `alm.java` (KeyAssembler) - шейр идёт в 4-of-4 сборку Шамира:
|
||||
|
||||
```java
|
||||
byte[] nativeShare = NativeGuard.share(hwid);
|
||||
|
|
@ -97,7 +97,7 @@ ama.combine(new int[]{share.x(), share.xIntegrity(), share.xNative(), share.xHwi
|
|||
|
||||
---
|
||||
|
||||
## 2. java.dll — `java.lang.EvoShare.computeRaw` (патченный JDK)
|
||||
## 2. java.dll - `java.lang.EvoShare.computeRaw` (патченный JDK)
|
||||
|
||||
Экспорт: `Java_java_lang_EvoShare_computeRaw` (@ `0x180001ca4`, 286 байт) +
|
||||
импорт `JVM_EvoForeignAgentPresent` **из `jvm.dll`** (см. §3).
|
||||
|
|
@ -121,9 +121,9 @@ longlong Java_java_lang_EvoShare_computeRaw(JNIEnv* env, jclass cls, jbyteArray
|
|||
|
||||
`FUN_180001e4c(input, len, out32)`:
|
||||
|
||||
1. `FUN_180001f1c(&DAT_180010e90, 0x20, input, len, out32)` — **стандартный HMAC-SHA256**
|
||||
1. `FUN_180001f1c(&DAT_180010e90, 0x20, input, len, out32)` - **стандартный HMAC-SHA256**
|
||||
(внутри явные SHA-IV `0x6a09e667…`, копирование ключа в ipad/opad);
|
||||
2. затем антиотладка, при срабатывании — `out32 ^= 0xA5…`:
|
||||
2. затем антиотладка, при срабатывании - `out32 ^= 0xA5…`:
|
||||
- `IsDebuggerPresent()`;
|
||||
- `CheckRemoteDebuggerPresent()`;
|
||||
- загружены модули `jdwp.dll`, `dt_socket.dll`, `dt_shmem.dll` (транспорты JDWP);
|
||||
|
|
@ -135,16 +135,16 @@ longlong Java_java_lang_EvoShare_computeRaw(JNIEnv* env, jclass cls, jbyteArray
|
|||
70ea2f4857869bdc8606a29a6c8cdcedbe92c18bef1aad106024db104a8f25d1
|
||||
```
|
||||
|
||||
Ключи `evoguard.dll` и `java.dll` **разные** — это два независимых шейра.
|
||||
Ключи `evoguard.dll` и `java.dll` **разные** - это два независимых шейра.
|
||||
|
||||
Константы-маски: `0xA5` — антиотладка (натив), `0x3C` — «чужой агент» из HotSpot,
|
||||
`0x5A` — Java-уровень (см. §4).
|
||||
Константы-маски: `0xA5` - антиотладка (натив), `0x3C` - «чужой агент» из HotSpot,
|
||||
`0x5A` - Java-уровень (см. §4).
|
||||
|
||||
---
|
||||
|
||||
## 3. jvm.dll — экспорт `JVM_EvoForeignAgentPresent`
|
||||
## 3. jvm.dll - экспорт `JVM_EvoForeignAgentPresent`
|
||||
|
||||
`mod/evojdk_x/bin/server/jvm.dll` (15,4 МБ) — тот же HotSpot, что и в `java.dll`, но с
|
||||
`mod/evojdk_x/bin/server/jvm.dll` (15,4 МБ) - тот же HotSpot, что и в `java.dll`, но с
|
||||
добавленным экспортом (в таблице экспортов: `[4281] +base[4282] 10b9 JVM_EvoForeignAgentPresent`).
|
||||
`java.dll` импортирует его и вызывает в `computeRaw`.
|
||||
|
||||
|
|
@ -171,7 +171,7 @@ undefined1 JVM_EvoForeignAgentPresent(void) {
|
|||
|
||||
### Откуда берётся список и что такое `+0x53`
|
||||
|
||||
- `DAT_180e2c160` — **глава глобального связного списка JVMTI-агентов** (stock-механизм HotSpot,
|
||||
- `DAT_180e2c160` - **глава глобального связного списка JVMTI-агентов** (stock-механизм HotSpot,
|
||||
`+0x20` = `next`, `+0x58` размер элемента). Доказательство: рядом лежит функция с литералом
|
||||
`"Disabled all JVMTI agents during -XX:AOTMode=create"`, которая делает `DAT_180e2c160 = 0`;
|
||||
push-функция `FUN_1805f40e0(name, path, is_absolute)` аллоцирует `0x58` байт, инициализирует
|
||||
|
|
@ -196,8 +196,8 @@ JVMTI-списке есть **хотя бы один не-instrument агент*
|
|||
|
||||
Вызов из `EvoShare.computeRaw` → `XOR 0x3C` на HMAC-шейре (см. §2, §5).
|
||||
|
||||
Также в `jvm_focus.c` — две функции, содержащие доступы к полю `0x53`
|
||||
(`FUN_180268640`, `FUN_1804a7920`), — это чтения в фильтрах классов/загрузчика, к
|
||||
Также в `jvm_focus.c` - две функции, содержащие доступы к полю `0x53`
|
||||
(`FUN_180268640`, `FUN_1804a7920`), - это чтения в фильтрах классов/загрузчика, к
|
||||
логике агентов отношения не имеют. Прямой записи `mov byte [reg+0x53]` в программе нет:
|
||||
поле инициализируется в конструкторе широкой записью через `strcmp`-результат.
|
||||
|
||||
|
|
@ -210,12 +210,12 @@ public static byte[] compute(byte[] input) {
|
|||
byte[] out = computeRaw(input); // натив, §2
|
||||
if (out != null && out.length == 32 && agentMismatch())
|
||||
for (int i = 0; i < out.length; i++)
|
||||
out[i] = (byte) (out[i] ^ 90); // 0x5A — третий уровень порчи
|
||||
out[i] = (byte) (out[i] ^ 90); // 0x5A - третий уровень порчи
|
||||
return out;
|
||||
}
|
||||
```
|
||||
|
||||
`agentMismatch()` — сканирует `VM.getRuntimeArguments()`:
|
||||
`agentMismatch()` - сканирует `VM.getRuntimeArguments()`:
|
||||
|
||||
- аргумент `-agentpath:` или `-agentlib:` (любой нативный агент, в т.ч. `-agentlib:jdwp`) → `true`;
|
||||
- `-javaagent:<jar>[=opts]`: берёт путь jar, читает файл, считает SHA-256 и сравнивает с
|
||||
|
|
@ -230,11 +230,11 @@ AGENT_CORRUPT_MASK = 90 // 0x5A
|
|||
EXPECTED_AGENT_SHA256 = 374ace507bd0cc2d0ce6dbbe07e50ddb3173ff819e92470271e2bf0fccff2038
|
||||
```
|
||||
|
||||
Проверено: `sha256sum mod/evo-agent.jar` = `374ace50…` — **пиннинг на свой агент
|
||||
Проверено: `sha256sum mod/evo-agent.jar` = `374ace50…` - **пиннинг на свой агент
|
||||
`evo-agent.jar`**. То есть JVM должен быть запущен либо вообще без `-javaagent`,
|
||||
либо именно с их агентом; чужой агент тихо ломает шейр.
|
||||
|
||||
Потребитель — `evo.agent.key.NativeJvmShare` (в `evo-agent.jar`):
|
||||
Потребитель - `evo.agent.key.NativeJvmShare` (в `evo-agent.jar`):
|
||||
|
||||
```java
|
||||
Method compute = Class.forName("java.lang.EvoShare").getMethod("compute", byte[].class);
|
||||
|
|
@ -242,7 +242,7 @@ byte[] share = (byte[]) compute.invoke(null, hwid.getBytes(UTF_8));
|
|||
// ClassNotFoundException → KeyProviderException("not running on the EvoVis custom JVM")
|
||||
```
|
||||
|
||||
То есть **payload-ключ требует их кастомного JDK** — на стоковом `java` шейр получить нельзя.
|
||||
То есть **payload-ключ требует их кастомного JDK** - на стоковом `java` шейр получить нельзя.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -255,19 +255,19 @@ byte[] share = (byte[]) compute.invoke(null, hwid.getBytes(UTF_8));
|
|||
| натив JDK | `EvoShare.computeRaw` | `JVM_EvoForeignAgentPresent() == 1`: есть `-agentlib:`/`-agentpath:` агент либо ≥2 `-javaagent:` | `XOR 0x3C` |
|
||||
| Java JDK | `EvoShare.compute` | `-agentpath:`/`-agentlib:` или `-javaagent:` с чужим sha256 | `XOR 0x5A` |
|
||||
|
||||
Во всех случаях результат остаётся валидным `byte[32]` — ошибка не сигнализируется,
|
||||
Во всех случаях результат остаётся валидным `byte[32]` - ошибка не сигнализируется,
|
||||
ключ просто собирается неверно (в `alm.java` дальше идёт `combine` Шамира и `alz.wipe`).
|
||||
|
||||
Две разные схемы HMAC (разные ключи) дают два разных шейра:
|
||||
|
||||
- `NativeGuard.share(hwid)` — в сборку ключа мода (`alm.java`, 4-of-4 Шамир);
|
||||
- `EvoShare.compute(hwid)` — в цепочку ключа payload'а агента (`NativeJvmShare`, `EvoAgent`).
|
||||
- `NativeGuard.share(hwid)` - в сборку ключа мода (`alm.java`, 4-of-4 Шамир);
|
||||
- `EvoShare.compute(hwid)` - в цепочку ключа payload'а агента (`NativeJvmShare`, `EvoAgent`).
|
||||
|
||||
---
|
||||
|
||||
## 6. `evovis-audiotap` (macOS, Swift)
|
||||
|
||||
`mod/jar/assets/evolution/native/macos/evovis-audiotap` — **Mach-O universal (x86_64 + arm64),
|
||||
`mod/jar/assets/evolution/native/macos/evovis-audiotap` - **Mach-O universal (x86_64 + arm64),
|
||||
174 848 байт, исполняемый файл** (`_main` + `__mh_execute_header`, не dylib).
|
||||
|
||||
Факты по `strings` / `r2`:
|
||||
|
|
@ -275,17 +275,17 @@ byte[] share = (byte[]) compute.invoke(null, hwid.getBytes(UTF_8));
|
|||
- написан на **Swift** (секции `__swift5_typeref/capture/entry/reflstr/fieldmd/types`),
|
||||
класс `main.AudioTap` (`_TtC4main8AudioTap`);
|
||||
- зависимости: **ScreenCaptureKit**, CoreMedia, AVFoundation, CoreAudio (swiftCoreAudio),
|
||||
Foundation, IOKit — то есть захват системного звука/видеопотока через ScreenCaptureKit;
|
||||
Foundation, IOKit - то есть захват системного звука/видеопотока через ScreenCaptureKit;
|
||||
- делегат `stream(_:didOutputSampleBuffer:of:)` (`SCStream` + `CMSampleBuffer`) и
|
||||
`stream(_:didStopWithError:)`; поля `output: NSFileHandle`, `mono: [Float]`;
|
||||
- логи/метки `evovis.audiotap.audio`, `evovis.audiotap.screen`;
|
||||
- **сетевых строк нет** (ни `http`, ни сокетов, ни доменов) — данные пишутся в
|
||||
- **сетевых строк нет** (ни `http`, ни сокетов, ни доменов) - данные пишутся в
|
||||
`NSFileHandle` (pipe/stdout), то есть процесс-потребитель мод запускает локально.
|
||||
|
||||
Вывод: это локальный helper для тапа аудио (и экрана) на macOS, результат уходит пайпом
|
||||
в родительский процесс; в аудио/сеть наружу ничего не отдаётся.
|
||||
|
||||
Декомпиляция Ghidra для него не запускалась (предмет — Swift + macOS-фреймворки, на
|
||||
Декомпиляция Ghidra для него не запускалась (предмет - Swift + macOS-фреймворки, на
|
||||
уровне строк/символов картина уже ясна); запускается по необходимости:
|
||||
|
||||
```bash
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue