# Writeup: Metamorphic Core Crackme

## **Используемые инструменты**
- **DnSpy**
- **Detect It Easy**
- **UPX**
- **ExtremeDumper**
- **.NET Reactor Slayer**

---

## **Шаг 1. UPX упаковка**

Загружаем файл в **Detect It Easy** и видим, что он упакован **UPX**.  
Попытка распаковать через `upx.exe -d <file>` неудачна — заголовок повреждён.

Открываем бинарь в **Hex Editor** и сравниваем заголовок с эталонным.  
Находим строку `MCVM` вместо `UPX0`. Исправляем байты на **UPX0** и успешно распаковываем через `upx -d`.

---

## **Шаг 2. Native-загрузчик .NET Reactor**

Повторная проверка в **Detect It Easy** показывает: код нативный, обфусцирован **.NET Reactor**.  
Это значит, что бинарь упакован в **native loader**.

Открываем EXE в **DnSpy (32-bit)** и запускаем. На точке **CreateProcess** видно, что процесс распакован и висит в памяти.  
Используем **ExtremeDumper** → находим `task.exe` → делаем дамп. Получаем два файла:  
- **_.dll**  
- **VmHost.exe**

---

## **Шаг 3. Деобфускация .NET Reactor**

Открываем **VmHost.exe** в **DnSpy** или **Detect It Easy** — видим сильную обфускацию.  
Запускаем **.NET Reactor Slayer**, выбираем все опции и запускаем деобфускацию.  

Теперь бинарь читаем и доступен для отладки.

---

## **Шаг 4. Отладка VM**

Открываем **VmHost.exe** в **DnSpy**.  
Ставим брейкпоинт в произвольном месте и смотрим окно **Local Variables**.  

Видим переменные:
- **this.dictionary_***  
- **this.list_***  
- **this.stack_***  
- **this.object_***  

Здесь хранятся строки с именами функций виртуальной машины: **SHA256**, **ExitProcess**, **sha256_hex**, **tohex** и др.  
Значит, вызовы идут через опкод **CALL** в VM.

---

## **Шаг 5. Поиск вызовов функций**

Находим функцию, отвечающую за вызов методов:

```csharp
private void method_3(int int_3, int int_4)
	{
		Class3.Struct0 @struct;
		for (;;)
		{
			IL_177:
			@struct = this.struct0_0[int_3];
			int num = 0; <-------------------------- Тут ставим брейкпоинт
			if (<Module>{3a1b6496-e3bc-4dd9-bc7f-29db217cfc86}.m_b53c11d96ca540509b6b9be7f90e8419 != 0)
			{
				goto IL_F4;
			}
			Class3.Struct1 struct2;
			for (;;)
			{
				IL_123:
				int num2;
				switch (num)
				{
				case 0:
					goto IL_B5;
				case 1:
					goto IL_F4;
				case 2:
					goto IL_177;
				case 3:
				case 4:
					goto IL_A0;
				case 5:
					num2--;
					num = 2;
					if (<Module>{3a1b6496-e3bc-4dd9-bc7f-29db217cfc86}.m_656fb7edff9a4bc3a280e41bdde428a0 != 0)
					{
						goto IL_A0;
					}
					continue;
				case 6:
					goto IL_6B;
				case 7:
					goto IL_D4;
				case 8:
					goto IL_197;
				case 9:
					goto IL_4F;
				case 10:
					return;
				case 11:
					goto IL_44;
				case 12:
					goto IL_62;
				case 13:
					goto IL_47;
				case 14:
					num2 = @struct.int_1 - 1;
					num = 0;
					if (<Module>{3a1b6496-e3bc-4dd9-bc7f-29db217cfc86}.m_9fc4e4f74325466da9db2cc7afcf1357 == 0)
					{
						goto IL_A0;
					}
					continue;
				case 15:
					goto IL_1B4;
				case 16:
					goto IL_30;
				case 17:
					break;
				default:
					goto IL_B5;
				}
				IL_06:
				struct2.object_0[num2] = this.stack_0.Pop();
				num = 5;
				if (<Module>{3a1b6496-e3bc-4dd9-bc7f-29db217cfc86}.m_61742430ed914e4ca05833d60e3db39b == 0)
				{
					break;
				}
				continue;
				IL_A0:
				if (num2 >= 0)
				{
					goto IL_06;
				}
				num = 0;
				if (<Module>{3a1b6496-e3bc-4dd9-bc7f-29db217cfc86}.m_4090cbdba33d4db1ba74b2a9eac02aa1 == 0)
				{
					continue;
				}
				IL_B5:
				this.stack_1.Push(struct2);
				num = 3;
				if (<Module>{3a1b6496-e3bc-4dd9-bc7f-29db217cfc86}.m_fe002c72199d46f0b4a52d0c2bff689e == 0)
				{
					goto Block_4;
				}
			}
			continue;
			IL_30:
			Class3.Struct1 struct3;
			struct2 = struct3;
			num = 14;
			if (<Module>{3a1b6496-e3bc-4dd9-bc7f-29db217cfc86}.m_0425df13f0c045d99d31a6db275fbd9e == 0)
			{
				goto IL_44;
			}
			goto IL_123;
			IL_6B:
			struct3.int_1 = 0;
			num = 3;
			if (<Module>{3a1b6496-e3bc-4dd9-bc7f-29db217cfc86}.m_d940aa626e044e7a9f22554ba2f6bb07 == 0)
			{
				goto IL_30;
			}
			goto IL_123;
			IL_62:
			int num3;
			struct3.int_0 = num3;
			goto IL_6B;
			IL_4F:
			struct3.object_0 = new object[@struct.int_2];
			goto IL_62;
			IL_47:
			struct3 = default(Class3.Struct1);
			goto IL_4F;
			IL_45:
			int num4;
			num3 = num4;
			goto IL_47;
			IL_D4:
			num4 = this.int_0;
			goto IL_45;
			IL_F4:
			if (int_4 != @struct.int_1)
			{
				goto Block_6;
			}
			if (this.stack_1.Count != 0)
			{
				goto IL_D4;
			}
			num = 4;
			if (<Module>{3a1b6496-e3bc-4dd9-bc7f-29db217cfc86}.m_b0871c76b442400c9823273656177106 == 0)
			{
				goto IL_123;
			}
			IL_44:
			num4 = -1;
			goto IL_45;
		}
		Block_4:
		goto IL_1B4;
		Block_6:
		IL_197:
		throw new Exception(Class5.smethod_17(656) + @struct.string_0);
		IL_1B4:
		this.int_0 = @struct.int_0;
	}
```
В локальной переменной **@struct** появляются названия функций, которые будут выполняться.

Здесь видим вызов **exists**, и в **object_0** лежит строка:
```
C:\Users\user\AppData\Local\Temp\.password\metamorphic.core
```
Значит, программа требует этот файл. Создаём его с любыми данными:
```
test1
test2
test3
```

## **Шаг 6. Работа с файлом**

Перезапускаем отладку. Теперь программа не падает, т.к. файл найден.
Следующий вызов — **"read_all_ascii"**. В **object_0** появляются строки из файла.

Затем вызываются **slice** и вспомогательные функции, которые берут первые две строки.
После вызывается **abort**, и программа завершается.

## **Шаг 7. Проверка условий (IF)**

Ищем в коде VM функции условий IF:

```csharp
[CompilerGenerated]
private bool method_21(object object_2, object object_3)
{
    return this.method_8(object_2, object_3); // Equals
}

[CompilerGenerated]
private bool method_22(object object_2, object object_3)
{
    return !this.method_8(object_2, object_3); // Equals
}
```
Ставим брейкпоинты и видим сравнение строк:
```csharp
object_2 = "test1"
object_3 = "MetamorphicVirtualMachine"
```
Значит, первая строка файла должна быть MetamorphicVirtualMachine.

## **Шаг 8. XOR-дешифровка второй строки**

Дальше вызывается функция **xorstr**.
В **object_0** лежит строка **+)8$)/e%+':-zxz}** и ключ **0x48**.

Расшифровка XOR даёт строку: 
```
caplag-mcore2025
```
Эта строка сравнивается со второй строкой файла.

Итого, правильное содержимое файла **metamorphic.core**:
```
Metamorphic Core :: crackme
Привет: DESKTOP-6IRVEAN, user user
OK: заголовки валидны
caplag{6ed9095fc6d38efbdea82031d16150c1b80c16cd641a135f117bdc411dbba68a}
Готово
```

✅ Флаг:
```
caplag{6ed9095fc6d38efbdea82031d16150c1b80c16cd641a135f117bdc411dbba68a}
```
