Хотели порешать эти ваши реверс-инжиниринги на CTF? Да ещё и на C++ с Windows Forms? К тому же чтобы он был не сложным и в райтапе были объяснения? Тогда вам сюда ?
Ссылка на задание (файлы): нажми на меня :)
В архиве будет лежать cross_cr.exe
. Флаг должен быть формата CODEBY{FLAG}
.
План статьи
-
Статический анализ файла
-
Detect It Easy
Карта памяти
Энтропия
Строки
Импорты
Анализ заголовков
Виртуализация
Ресурсы
Resource Hacker
Hex-редактор
-
Динамический анализ
-
Анализ в IDA
Поиск сообщения о неправильном флаге
Анализ функции и начало сбора флага
Составляем "тело флага"
1. Статический анализ файла
1.1 Detect It Easy
Начнём анализ исполняемого файла с помощью утилиты Detect It Easy (далее DIE).
DIE не обнаружил никаких упаковщиков/протекторов и прочих средств защиты. Сам таск свежий, если верить отметки времени. Кроме того он с графическим интерфейсом.
Посмотрим информацию о файле через продвинутый анализ.
Карта памяти
В карте памяти аномалий не выявлено.
Энтропия
Файл кажется сжатым, но если смотреть по секциям, то наиболее сжатый по энтропии - это секция ресурсов (.rsrc
). Пока что просто запомним это. Возможно, далее нам это пригодится.
Строки
Поиск по строкам дал нам возможные ориентиры, на места в коде с выводом сообщение о флаге и о проигрыше. Также названия некоторых функций и пока что непонятное MASTER_OF_CODEBY
.
Импорты
Из импортов чего-то "слишком криминального" не выявлено. Но есть IsDebuggerPresent
.
Примечание: Хоть функция IsDebuggerPresent
и есть в программе, но она не используется в основном коде для изменения поведения таска во время отладки. Поэтому нам будет проще.
Анализ заголовков
По заголовкам всё выглядит нормально для EXE-файла.
Виртуализация
Бывает полезно посмотреть таким образом на файл. По графическому представлению видно, что ресурсы занимают больше всего места в этом исполняемом файле.
Ресурсы
Теперь понятно, откуда такой размер. Это PNG-файл. Посмотрим более детально в Resource Hacker.
1.2 Resource Hacker
Это иконка. Если смотреть на неё в Hexdump'е (Binary View) ничего странного не видно.
1.3 Hex-редактор
Иногда в конце исполняемых файлов некоторых тасков что-то можно найти. Но тут ничего странного нет.
2. Динамический анализ
Динамический анализ в Autoruns, Process Monitor, Process Hacker и Wireshark не показал ничего необычного.
3. Анализ в IDA
Примечание: далее будет использована IDA Freeware 8.4. Её хватит с головой :)
Но вы также можете использовать Ghidra, Cutter (Radare2) или любой другой отладчик, который вам нравится. Код в декомпиляторах будет +- похожим.
3.1 Поиск сообщения о неправильном флаге
Попробовав запустить файл и ввести что-то в поля для ввода, мы наблюдаем вызов функции MessageBox
с текстом о неправильном флаге.
Запустим IDA и попробуем найти код, что вызывает этот MessageBox
. Перейдём в окно Strings (Shift + F12).
Русские символы по умолчанию не отображаются. Для этого нужно нажать правую кнопку мыши (далее ПКМ), затем Setup
и выбрать такие пункты меню.
Опция Unicode нужна, чтобы нормально отображались символы из 16-битных кодировок в стандарте Unicode. Строки в 16-битной кодировке активно используются в WinAPI.
Галочку со Strict ASCII мы убираем для поиска русских символов (в том числе для опции Unicode). Но для нормального отображения нам нужно зайти ещё сюда.
Затем тут выставляем cp1251
. Это стандартная 8-битная кодировка для русских символов в Windows.
Далее нажимаем Rebuild и видим русские символы.
Первых символов не видно, но понять можно. Жмём на "хх..." (от Эхх...).
Нужно представить эти байты как строку. Жмём ALT+A, затем сюда.
Байты представились как текст в кодировке UTF-16LE (UTF-16 Little Endian). Наводим курсор на название строки (aE
) И нажимаем X, чтобы IDA показала нам, где используется эта строка. Жмём левой кнопкой мыши (далее ЛКМ) на пункт меню со скрина.
Видим 2 блока, ведущих к MessageBoxW
. Обычно это блоки о неправильном/правильном флаге. Переменные unk
на скриншотах - это русские строки. Их тоже можно представить в нормальном виде.
Через клавишу N можно поменять название ноды графа, наведя курсор на его первую инструкцию. Кроме этого можно задать цвет через ПКМ или специальную иконку.
Более того мы сразу попали на функцию проверки ввода.
3.2 Анализ функции и начало сбора флага
Составим краткий "скелет" функции через декомпилятор - клавиша F5. В IDA Freeware онлайн-декомпилятор, поэтому иногда он может быть недоступен. Учтите это, если будете пробовать сами.
Через клавишу N можно менять названия объектов.
Убедиться, что эта функция используется для проверки нашего ввода можно, если навести курсор на её название и нажать X. А далее посмотреть, где вызывается.
Эту функцию можно назвать так. Она обрабатывает сообщения от GUI-окна. По коду 0x111 (WM_COMMAND) вызывается наша функция проверки ввода.
На check_input
и сосредоточим внимание. Вывод декомпилятора:
int check_input()
{
__int64 v0; // rdi
__int64 v1; // rax
__int64 v2; // rsi
WCHAR *v3; // rbx
int v4; // esi
int v5; // ebx
int v6; // ebp
int result; // eax
int v8; // edx
UINT v9; // r9d
const WCHAR *v10; // r8
const WCHAR *v11; // rdx
WCHAR String1[256]; // [rsp+20h] [rbp-418h] BYREF
WCHAR String[256]; // [rsp+220h] [rbp-218h] BYREF
memset(String, 0, sizeof(String));
GetWindowTextW(hWnd, String, 256);
memset(String1, 0, sizeof(String1));
GetWindowTextW(qword_140005670, String1, 256);
v0 = -1LL;
v1 = -1LL;
do
++v1;
while ( String1[v1] );
if ( v1 == 27 && !wcsncmp(String1, L"CODEBY{", 7uLL) && String1[26] == 125 )
{
v2 = 11LL;
v3 = &String1[13];
while ( *(v3 - 2) == 45 && iswdigit(*(v3 - 1)) && iswdigit(*v3) && iswdigit(v3[1]) && iswdigit(v3[2]) )
{
v2 += 5LL;
v3 += 5;
if ( v2 > 24 )
{
v4 = wtoi(&String1[7]);
v5 = wtoi(&String1[12]);
v6 = wtoi(&String1[17]);
result = wtoi(&String1[22]);
v8 = result;
while ( String[v0 + 1] == aMasterOfCodeby[v0 + 1] )
{
v0 += 2LL;
if ( v0 == 17 )
{
if ( (((v4 ^ 0xDFAF7) + 22098798) ^ 0x23B97B) == 24947582 && (((v5 ^ 0x378) + 1361) ^ 0xB84C) == 40468 )
{
result = (v6 - 9283) ^ 0xA808;
if ( result == -47487 && (((v8 ^ 0xFD836) - 13112) ^ 0xBC3F) == 988548 )
{
v9 = 0;
v10 = L"Флаг!!!";
v11 = L"Флаг, поздравляю! :)";
return MessageBoxW(0LL, v11, v10, v9);
}
}
return result;
}
result = String[v0];
if ( result != aMasterOfCodeby[v0] )
goto fail;
}
break;
}
}
}
fail:
v9 = 16;
v10 = L"Эхх...";
v11 = L"Ну, почти...";
return MessageBoxW(0LL, v11, v10, v9);
}
Самые первые инструкции получают данные из полей с вводом имени и флага:
memset(String, 0, sizeof(String));
GetWindowTextW(hWnd, String, 256);
memset(String1, 0, sizeof(String1));
GetWindowTextW(qword_140005670, String1, 256);
В дизассемблированном виде код выглядит так (вывод декомпилятора полезно проверять на листинге дизассемблера):
Сразу дадим нормальные имена, чтобы повысить читаемость. Через отладку можно понять, где будет что.
В данном случае в поле имени было введено "qwe".
Следующая часть кода выполняет подсчёт длины строки с флагом:
v0 = -1LL;
v1 = -1LL;
do
++v1;
while ( flag[v1] );
if ( v1 == 27...
Листинг дизассемблера:
Флаг должен быть длиною в 27 символов.
Следующий кусок кода:
Первое условие из выделения со скрина проверяет, что начальные 7 символов флага - это CODEBY{
. Второе условие из выделения проверяет, что код символа с индексом 26 (по счёту 27-ой) равен 125. В таблице ASCII это символ }
.
Листинг дизассемблера:
Таким образом сейчас у нас есть следующее представление флага:
Следующий блок кода:
Листинг дизассемблера:
Данный блок кода проверяет часть флага между фигурными скобками {}. Она должна быть такого вида:
-XXXX-XXXX-XXXX
Где X
- это цифра в 10-ой системе.
Немного странно, что в этой проверке упущена часть перед первым -
, это мы поймём далее.
Затем идёт такой блок:
Он преобразует строку с числом между символами -
в число.
Листинг дизассемблера:
Следующий блок кода сравнивает введённое имя со строкой MASTER_OF_CODEBY
. Это будет правильным именем для дальнейшего решения таска.
Листинг дизассемблера:
3.3 Составляем "тело флага"
Посмотрим на следующий блок:
Он выполняет шифрование чисел через простые операции и сравнивает их с нужными.
Листинг дизассемблера:
Перепишем это в более понятном виде:
first_decimal ^= 0xDFAF7;
first_decimal += 0x151336E;
first_decimal ^= 0x23B97B;
// first_decimal должен быть 0x17CAB7E
second_decimal ^= 0x378;
second_decimal += 0x551;
second_decimal ^= 0xB84C;
// second_decimal должен быть 0x9E14
third_decimal -= 0x2443;
third_decimal ^= 0xA808;
// third_decimal должен быть -47487 (0xFFFF4681)
fourth_decimal ^= 0xFD836;
fourth_decimal -= 0x3338;
fourth_decimal ^= 0xBC3F;
// fourth_decimal должен быть 0xF1584
Для удобства я не использовал копии чисел, как показал декомпилятор.
Теперь останется просто применить противоположные операции в обратном порядке к нужным значениям.
Для операции + обратная операция -.
Для операции - обратная операция +.
Для операции xor обратная операция xor.
first_decimal = 0x17CAB7E;
first_decimal ^= 0x23B97B;
first_decimal -= 0x151336E;
first_decimal ^= 0xDFAF7;
// (((0x17CAB7E ^ 0x23B97B) - 0x151336E) ^ 0xDFAF7) = 9312
second_decimal = 0x9E14;
second_decimal ^= 0xB84C;
second_decimal -= 0x551;
second_decimal ^= 0x378;
// (((0x9E14 ^ 0xB84C ) - 0x551 ) ^ 0x378) = 8831
third_decimal = 0xFFFF4681;
third_decimal ^= 0xA808;
third_decimal += 0x2443;
// ((0xFFFF4681 ^ 0xA808) + 0x2443) = 4294972108
fourth_decimal = 0xF1584;
fourth_decimal ^= 0xBC3F;
fourth_decimal += 0x3338;
fourth_decimal ^= 0xFD836;
// (((0xF1584 ^ 0xBC3F) + 0x3338) ^ 0xFD836) = 1221
Итого наши части флага:
9312-8831-4294972108-1221
Общий флаг:
CODEBY{9312-8831-4294972108-1221}
Третья часть очень выделяется. И проверку не пройдёт. Если посмотреть листинг дизассемблера для функции шифрования, то данные там хранятся в 4-байтовых регистрах.
Представим число 4294972108 в 16-ом виде:
0x1000012CC
0x01 00 00 12 CC (по байтам)
Это 5-байтовое число. А регистры 4-байтовые. 5-байтовое число в 4-байтовый регистр никак не влезает. Поэтому в процессе шифрования первые лишние байты просто отсекаются. В данном случае это байт 0x01 в самом начале:
0x00 00 12 CC (по байтам)
Получили 0x000012CC. Незначащие байты (0x0000) можно убрать и получить 16-ое число. Это 16-ое число переводим в 10-е. Далее оформляем флаг и сдаём.
Делиться открытым флагом не буду, дальше вы сами добьёте таск! :)
Спасибо за прочтение статьи! ?