Как дампят софты под майн, какие дамперы испльзуют и обходят защиту
## Общая схема дампа из RAM
JVM загружает классы в heap. Задача — перехватить их **до** того как они будут трансформированы/зашифрованы обратно, или поймать в момент загрузки.
### 1. Java Agent — основной метод
Агент подключается через `-javaagent` или Attach API и получает доступ к `Instrumentation`:
```java
public class DumpAgent {
public static void agentmain(String args, Instrumentation inst) {
inst.addTransformer((loader, className, classBeingRedefined,
protectionDomain, classfileBuffer) -> {
// classfileBuffer — байткод ПОСЛЕ расшифровки, ДО выполнения
saveClass(className, classfileBuffer);
return null; // не модифицируем
}, true);
// Форсируем retransform уже загруженных классов
for (Class<?> c : inst.getAllLoadedClasses()) {
if (inst.isModifiableClass(c)) {
try { inst.retransformClasses(c); } catch (Exception ignored) {}
}
}
}
}
```
Attach к уже запущенной JVM:
```java
VirtualMachine vm = VirtualMachine.attach(pid);
vm.loadAgent("dump-agent.jar", outputDir);
vm.detach();
```
---
## Обход конкретных защит
### Custom ClassLoader + шифрование классов
Самая частая защита на кастомных клиентах. Классы хранятся зашифрованными, расшифровываются в рантайме кастомным `ClassLoader`.
**Обход:** агент срабатывает **после** расшифровки — `classfileBuffer` уже содержит чистый байткод. Это фундаментальное ограничение JVM, которое нельзя обойти без модификации самой JVM.
### Custom JVM (форк HotSpot и т.п.)
Некоторые клиенты шипят модифицированную JVM которая:
- Отключает Attach API
- Фильтрует агенты по подписи
- Патчит `Instrumentation`
**Варианты обхода:**
**а) Запуск через стандартную JVM с подменой classpath** — если лаунчер это позволяет
**б) JVMTI агент (нативный, `.so`/`.dll`)** — работает на уровне ниже Java, сложнее заблокировать:
```c
// JVMTI callback на загрузку класса
void JNICALL ClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv *env,
jclass class_being_redefined, jobject loader,
const char *name, jobject protection_domain,
jint class_data_len, const unsigned char *class_data,
jint *new_class_data_len, unsigned char **new_class_data)
{
// class_data — чистый байткод
dump_to_file(name, class_data, class_data_len);
}
```
JVMTI подключается через `-agentpath:dump.so` или через нативный Attach.
**в) Дамп через `/proc/[pid]/mem`** (Linux) — чтение памяти процесса напрямую и поиск паттернов `CAFEBABE` (magic bytes `.class` файлов):
```python
import re, os
def dump_classes(pid):
maps = open(f"/proc/{pid}/maps").readlines()
mem = open(f"/proc/{pid}/mem", "rb")
for line in maps:
if "heap" in line or "anon" in line:
start, end = [int(x, 16) for x in line.split()[0].split("-")]
mem.seek(start)
data = mem.read(end - start)
# Ищем magic bytes .class
for m in re.finditer(b'\xca\xfe\xba\xbe', data):
save_candidate(data[m.start():])
```
### String шифрование / flow obfuscation
После дампа классы могут быть обфусцированы. Инструменты:
- **Deobfuscator** (java-deobfuscator) — умеет снимать Allatori, Zelix, Stringer, DashO
- **Threadtear** — GUI, набор трансформеров для деобфускации
- **Recaf** — ручной анализ байткода
---
## Практический порядок действий
1. Запустить клиент
2. Найти PID (`jps -l`)
3. Подцепить агент через Attach API → получить дамп классов
4. Если Attach заблокирован → JVMTI через `-agentpath` при старте или `/proc/mem`
5. Прогнать дамп через Vineflower
6. Если обфусцирован → Threadtear/deobfuscator
7. Применить маппинги если нужны читаемые имена Minecraft
Небольшая заметка: практически во всех кастомных jvm на клиентах всяких выпилены javaagent, jvmti.