Всем привет. Сегодня будет разбор довольно занятного CrackMe из этой темы - Вопрос - Один вопрос.
Заинтересовал он меня тем, что человек додумался перенести проверку ключа в x32 программе в x64 код, алгоритм шифрования тоже весёлый, так что давайте разберём по подробнее.
В main программы автор протектит память размером 0x914 с привилегиями выполнения кода:
Мы видим что для успешной проверки ключа функция sub_4015E0 должна вернуть результат != 0.
Переходим в неё и вот что в ней видим:
По инструкции "ret far" и странному асм листингу можно догадаться что остальной код проверки написан для x64 среды.
Не долго думая я понял какую функцию вызывает этот код, её память протектилась в main, и вот как раз в ней и происходит шифрование введённого пароля и сравнение с константой.
Что же делать? Как отладить этот код?
Всё просто, копируем все эти 0x914 байт, открываем любой файл в x64 отладчике, выделяем память (для удобства) и вставляем всё это в неё, на выходе мы получаем оригинальный асм листинг проверки ключа:
Теперь можно выделять память для записи своего ключа и начинать трассировку. Сразу хочу заметить, что код рассчитан на то, что входной параметр будет иметь x32 адрес, потому что до этого момента код выполнялся в x32 контексте, по этому для удобства я заменил в прологе ECX на RCX, в итоге мы ставим RIP на самую первую инструкцию и в RCX суём адрес строки нашего пароля.
Из начала кода мы понимаем, что длинна ключа должна состоять из 16 символов (cmp ecx, 10 на скрине).
Листаем в самый низ и видим как в конце сравниваются 2 QWORD'a (16 байт) с константами, и если всё совпадает нам возвращает результат 5, который не равен 0, как и требуется в процедуре main.
Если у вас нет квантового компьютера для брутфорса 16-ти символов, среди которых есть и цифры и буквы - придётся изучать алгоритм шифрования :(
Я не могу вам сказать что я полностью разобрал этот алгоритм, поскольку:
1) В моей процедуре декрипта есть костыль (на мой взгляд)
2) Конечный результат моего рашифрованного ключа имел 1 не совсем правильно расшифрованный символ.
Но крякми я решил, так что перфекционисты идите на***
Вся шифровка состоит из нескольких этапов:
1) Символы в буфере с введённым паролем меняются в определённом порядке местами и некоторые char'ы инкрементируются (в конечном итоге меняется и сам символ)
2) Далее всё реверсируется (пишется задом наперёд)
3) Полученный результат шифруется по примерно такому алгоритму:
В encrypt_byte учавствуют 2 ключевые инструкции шифрования, а именно:
1) sar eax, cl
2) bts eax, 7
Мой костыльный код для расшифровки char получился таким:
Костыль заключается в проверке cmp eax, 100h, потому что на 1 символ она не сработала и мне пришлось инкрементировать 1 байт вручную.
Теперь сам код расшифровки пароля:
Позиции символов до реверса я понял достаточно просто, я передал в качестве пароля 0123456789ABCDEF и посмотрел что с этим делом произошло после первого этапа шифрования.
На выходе мы получаем следующий результат: IdIGXM12QPQa3ClL
Этот пароль крякмис какого то хрена не принял, и я пошёл смотреть в отладчике что же я сделал не так, и как оказалось моя костыль-функция-декрипта не инкрементировала второй символ, который на самом деле является e
И так, пароль от этого пиздеца: IeIGXM12QPQa3ClL
Всем спасибо кто дочитал это месево до конца, до скорого!
Для тех кто хочет потыкать сие чудо: Crackme.exe
Для параноиков: VirusTotal
Заинтересовал он меня тем, что человек додумался перенести проверку ключа в x32 программе в x64 код, алгоритм шифрования тоже весёлый, так что давайте разберём по подробнее.
В main программы автор протектит память размером 0x914 с привилегиями выполнения кода:
Мы видим что для успешной проверки ключа функция sub_4015E0 должна вернуть результат != 0.
Переходим в неё и вот что в ней видим:
По инструкции "ret far" и странному асм листингу можно догадаться что остальной код проверки написан для x64 среды.
Не долго думая я понял какую функцию вызывает этот код, её память протектилась в main, и вот как раз в ней и происходит шифрование введённого пароля и сравнение с константой.
Что же делать? Как отладить этот код?
Всё просто, копируем все эти 0x914 байт, открываем любой файл в x64 отладчике, выделяем память (для удобства) и вставляем всё это в неё, на выходе мы получаем оригинальный асм листинг проверки ключа:
Теперь можно выделять память для записи своего ключа и начинать трассировку. Сразу хочу заметить, что код рассчитан на то, что входной параметр будет иметь x32 адрес, потому что до этого момента код выполнялся в x32 контексте, по этому для удобства я заменил в прологе ECX на RCX, в итоге мы ставим RIP на самую первую инструкцию и в RCX суём адрес строки нашего пароля.
Из начала кода мы понимаем, что длинна ключа должна состоять из 16 символов (cmp ecx, 10 на скрине).
Листаем в самый низ и видим как в конце сравниваются 2 QWORD'a (16 байт) с константами, и если всё совпадает нам возвращает результат 5, который не равен 0, как и требуется в процедуре main.
Если у вас нет квантового компьютера для брутфорса 16-ти символов, среди которых есть и цифры и буквы - придётся изучать алгоритм шифрования :(
Я не могу вам сказать что я полностью разобрал этот алгоритм, поскольку:
1) В моей процедуре декрипта есть костыль (на мой взгляд)
2) Конечный результат моего рашифрованного ключа имел 1 не совсем правильно расшифрованный символ.
Но крякми я решил, так что перфекционисты идите на***
Вся шифровка состоит из нескольких этапов:
1) Символы в буфере с введённым паролем меняются в определённом порядке местами и некоторые char'ы инкрементируются (в конечном итоге меняется и сам символ)
2) Далее всё реверсируется (пишется задом наперёд)
3) Полученный результат шифруется по примерно такому алгоритму:
C++:
// псевдокод
int key = 0;
for ( int i = 0; i < 16; i++ )
{
buf[i] = encrypt_byte( buf[i], key );
key++;
if ( key == 3 )
key = 0;
}
В encrypt_byte учавствуют 2 ключевые инструкции шифрования, а именно:
1) sar eax, cl
2) bts eax, 7
Мой костыльный код для расшифровки char получился таким:
C++:
.CODE
decrypt_byte PROC PUBLIC
push r10
push r13
mov r13, 1
xor rax, rax
mov al, cl
mov r10d, ecx
and r10d, 80000001h
jge skip
dec r10d
or r10d, -2
inc r10d
skip:
cmp r10d, r13d
jne skip_2
btr eax, 7
skip_2:
mov cl, dl
sal eax, cl
cmp eax, 100h
jb skip_3
inc eax
skip_3:
pop r13
pop r10
ret
decrypt_byte ENDP
END
Костыль заключается в проверке cmp eax, 100h, потому что на 1 символ она не сработала и мне пришлось инкрементировать 1 байт вручную.
Теперь сам код расшифровки пароля:
C++:
int main()
{
//
// Я ЗНАЮ ЧТО МОЖНО БЫЛО ПРОСТО СОЗДАТЬ МАССИВ unsigned char, НЕ НАДО ОБ ЭТОМ ПИСАТЬ, ТАК НАДО БЫЛО ДЛЯ ТЕСТОВ...
//
DWORD_PTR hash_1 = 0xA8B39226E116A432; // Первая часть зашифрованного пароля
DWORD_PTR hash_2 = 0x6C14A6C399A8C78C; // Вторая часть зашифрованного пароля
char* pByte1 = (char*)&hash_1;
char* pByte2 = (char*)&hash_2;
char* enc = (char*)malloc(0x1000);
ZeroMemory( enc, 0x1000 );
memcpy( enc, (void*)&hash_1, 8 );
memcpy( enc + 8, (void*)&hash_2, 8 );
//
// Цикл расшифровки
//
int key = 0;
for ( int i = 0; i < 16; i++ )
{
enc[i] = decrypt_byte( enc[i], key );
key++;
if ( key == 3 )
key = 0;
}
ReverseQword( enc ); // Реверс QWORD'a
//
// Выделяем память для записи конечного результата
//
char* dec = (char*)malloc( 0x1000 );
ZeroMemory( dec, 0x1000 );
//
// Возвращаем на место все символы
//
dec[0] = enc[14];
dec[1] = enc[9];
dec[2] = enc[5];
dec[3] = enc[13];
dec[4] = enc[1];
dec[5] = enc[8];
dec[6] = enc[3];
dec[7] = enc[6];
dec[8] = enc[10];
dec[9] = enc[12];
dec[10] = enc[0];
dec[11] = enc[15];
dec[12] = enc[11];
dec[13] = enc[4];
dec[14] = enc[2];
dec[15] = enc[7];
std::cout << dec << std::endl; // PROFIT?
_getch();
return 0;
}
Позиции символов до реверса я понял достаточно просто, я передал в качестве пароля 0123456789ABCDEF и посмотрел что с этим делом произошло после первого этапа шифрования.
На выходе мы получаем следующий результат: IdIGXM12QPQa3ClL
Этот пароль крякмис какого то хрена не принял, и я пошёл смотреть в отладчике что же я сделал не так, и как оказалось моя костыль-функция-декрипта не инкрементировала второй символ, который на самом деле является e
И так, пароль от этого пиздеца: IeIGXM12QPQa3ClL
Всем спасибо кто дочитал это месево до конца, до скорого!
Для тех кто хочет потыкать сие чудо: Crackme.exe
Для параноиков: VirusTotal
Последнее редактирование: