# Crackme writeup Задание выглядит следующим образом и является приложением windows forms (ну или wpf), написанным на c#. ![](https://i.imgur.com/TczX6FC.png) Заметим, что весь проект скомпилирован в один исполняемый файл. Откроем его в любом изветном декомпиляторе (я использовал dotpeek). Заметим несколько файлов: ![](https://i.imgur.com/aULkHpt.png) `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`: ![](https://i.imgur.com/8Rp6tta.png) Поменяем типы аргументов и переименуем переменные: ![](https://i.imgur.com/ftcI0Y2.png) Перепишем на плюсы для того, чтобы понять, что вообще происходит: ``` #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` ![](https://i.imgur.com/grJTqMQ.png)