# Crackme writeup
Задание выглядит следующим образом и является приложением windows forms (ну или wpf), написанным на c#.

Заметим, что весь проект скомпилирован в один исполняемый файл.
Откроем его в любом изветном декомпиляторе (я использовал dotpeek).
Заметим несколько файлов:

`MainWindow.cs` содержит обработчики элементов управления формы, и несколько проверочных и вспомогательных функций: `ConvertHexStringToByteArray`, `OnlyHexInString`. Но в целом - ничего интересного.
Введенная почта проверяется на наличие "@", код должен содержать только hex-символы. Если введенные данные корректны, они проверяются в `Dllloader.CheckSerial(text, byteArray)`.
Заметим, что перед этим вызывается Dllloader.LoadDll().
Нужная dll'ка декодируется из base64:
`public byte[] GetDLL() => Convert.FromBase64String(Resources.base64);`
Затем выделяем под нее память и кладем по адресу указателя:
```
IntPtr num = Marshal.AllocHGlobal(dll.Length);
Marshal.Copy(dll, 0, num, dll.Length);
```
Затем, в `LoadDll()` присваиваем в делегат типа:
```
IntPtr ptr = this.LoadFromMem();
this.Func = (Loader.d713235dbd24f7d3) Marshal.GetDelegateForFunctionPointer(ptr, typeof (Loader.d713235dbd24f7d3));
```
Дальше, в `CheckSerial(string email, byte[] serial`, где проверяется введенная почта и код, мы сначала копируем их в память:
```
byte[] bytes = Encoding.UTF8.GetBytes(email);
IntPtr num1 = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, num1, bytes.Length);
IntPtr num2 = Marshal.AllocHGlobal(serial.Length);
Marshal.Copy(serial, 0, num2, serial.Length);
```
А затем вызываем делегат, передавая указатель на почту, длину почты, а также указатель на код.
`bool flag = this.Func(num1, email.Length, num2);`
Достанем dll-ку из base64:
```
>>> from base64 import b64decode
>>> open('kek.dll', 'wb').write(b64decode(open('CrackMe2018.Res.base64.txt', 'r').read()))
75264
```
Посмотрим на нее <s>ida</s> и декомпилируем нужную функцию `d713235dbd24f7d3`:

Поменяем типы аргументов и переименуем переменные:

Перепишем на плюсы для того, чтобы понять, что вообще происходит:
```
#include <iostream>
#include <string>
using namespace std;
#define __PAIR64__(high, low) (((uint64_t) (high) << 32) | (uint32_t)(low))
#define byte unsigned char
bool check(byte *mailPtr, int mailLength, byte *codePtr)
{
byte *cur; // ecx
int offset; // eax
int ersk; // esi
int kasp; // edi
int offset1; // ebx
unsigned int v8; // edi
int v9; // kr00_4
cur = codePtr;
offset = 0;
ersk = 0x4552534B; // ERSK
kasp = 0x4B415350; // KASP
printf("vars - %08x %08x\n", *(uint32_t *)cur, *((uint32_t *)cur + 1));
printf("code - %08x %08x\n\n", ersk, kasp);
while ( codePtr[offset] )
{
if ( ++offset >= 10 )
{
offset1 = 0;
if ( mailLength > 0 )
{
do
{
v9 = mailPtr[offset1] + ersk;
v8 = (mailPtr[offset1++] + __PAIR64__(kasp, ersk)) >> 32;
kasp = __PAIR64__(v8, v9) >> 28;
ersk = 16 * v9;
}
while ( offset1 < mailLength );
cur = codePtr;
}
*cur ^= cur[9];
printf("vars - %08x %08x\n", *(uint32_t *)cur, *((uint32_t *)cur + 1));
printf("code - %08x %08x\n", ersk, kasp);
return *(uint32_t *)cur == ersk && *((uint32_t *)cur + 1) == kasp;
}
}
return 0;
}
int main()
{
byte mail[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
byte code[10] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10};
int mailLength = 10;
cout << check(mail, mailLength, code);
}
```
Заметим, что переменные `kasp` и `ersk` как-либо меняются в зависимости от почты. Первый байт кода ксорится с последним. Затем, первые 4 байта кода сравниваются с `ersk`, а последние - с `kasp`. Заблекбоксим алгоритм, подобрав код для почты `kek@kek.kek`.
Переменные превратятся в:
```
ersk = 1be4a5d0
kasp = 253bcbf7
```
Подберем последний байт ключа так, чтобы при ксоре с первым он давал d4.
Получим код - ` byte code[10] = {0xb0, 0xa5, 0xe4, 0x1b, 0xf7, 0xcb, 0x3b, 0x25, 0x09, 0x60};`
Ответ для почты: `kek@kek.com` - `b0a5e41bf7cb3b250960`
