Решение ksydfius - Simple Ciph3r

Сложность: 1 - Very easy, for newbies
Платформа: Windows
Язык: Unspecified/other
Дано: Simple Ciph3r

Это очень простой шифр.
Взломай его :)
- - - - - - - - -
Оригинальный текст для MD5 очень короткий, для перебора хватит 4-х символов

Решение (автор randomdude)
опубликовано 10.10.2012

Часть 1: Расшифровка exe-файла

Во-первых, выясняем, что SimpleCiph3r.exe - .NET-сборка, поэтому мы запускаем Reflector или SimpleAssemblyExplorer (SAE) и находим код функции button1_Click, выполняющая основную часть работы.

// SimpleCiph3r.Form1
private void button1_Click(object sender, EventArgs e)
{
   string text = this.textBox1.Text;
   // получить MD5-хэш «Unlock Code» 
   byte[] hash = this.getHash(text);
   // берем массив байт из секции ресурсов SimpleCiph3r
   byte[] array = Resources.SimpleCiph3r;
   // расшифровка ресурса, ключом является MD5-хэш «Unlock Code» 
   array = this.crypt(array, hash);
   try
   {
 
       // расшифрованный ресурс – это выполняемый файл, его мы и запускаем на выполнение
       FileStream fileStream = new FileStream("SimpleCiph3r_.exe", FileMode.Create, FileAccess.Write);
       fileStream.Write(array, 0, array.Length);
       fileStream.Close();
       new Process
       {
           StartInfo = 
           {
               FileName = "SimpleCiph3r_.exe"
           }
       }.Start();
   }
   catch (Exception var_5_69)
   {
       MessageBox.Show("Invalid unlock key", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
       File.Delete("SimpleCiph3r_.exe");
   }
   base.Close();
}
 
// получить MD5-хэш «Unlock Code» 
private byte[] getHash(string text)
{
   MD5 mD = MD5.Create();
   byte[] bytes = Encoding.ASCII.GetBytes(text);
   return mD.ComputeHash(bytes);
}
 
// расшифровка ресурса, ключом является MD5-хэш «Unlock Code» 
private byte[] crypt(byte[] data, byte[] key)
{
   for (int i = 0; i < data.Length; i++)
   {
       int expr_0D_cp_1 = i;
       data[expr_0D_cp_1] ^= key[i % key.Length];
   }
   return data;
}


Все просто. В поле ввода указываем некий текст, на основании которого создаем хэш MD5 (функция getHash) и байт за байтом XORим его с потоком зашифрованных данных (функция crypt). Такой способ шифрования очень напоминает режим электронной кодовой книги (ECB).

"Наиболее простой способ шифрования (или расшифровки) последовательности сегментов сообщения – обрабатывать их по отдельности. В это случае сегмент сообщения становится блоком. Поскольку это простой и естественный способ напоминает присваивание кодовых слов, взятых из шифровальной книги, он получил название «электронная кодовая книга (ECB)» (в русскоязычной литературе метод называется режимом простой замены). Режим ECB определяется следующим образом.

Шифрование в режиме ECB: Ci ← E(Pi), i=1,2,…,m; C - шифротекст, P - исходный текст
Расшифровка в режиме ECB: Pi ← E(Ci), i=1,2,…,m; C - шифротекст, P - исходный текст

Режим ECB является детерменированным, т.е. если сегменты P1,P2,…Pm шифруются дважды с помощью одного и того же ключа, то результирующие блоки зашифрованного текста будут одинаковыми. В приложениях данные, как правило, содержат информацию, которую легко угадать. … Как правило, детерминированные шифры применять не рекомендуется, и по этой причине режим ECB не следует использовать в большинстве приложений".

Мао В. Современная криптография (Под редакцией Клюшиной Д.А. — М. : Издательский дом Вильямс, 2005) стр.276-277

Итак, известная слабость ECB в том, что у двух одинаковых блоков будет одинаковый зашифрованный текст, и этот факт нам сейчас пригодится.
Отметим также, что файл, который нужно расшифровать это исполняемый exe-файл, имеющий PE-заголовок. Давайте посмотрим на него поближе:

image00.jpg

Хм, кажется, присутствует повторяющийся шаблон (выделено красным)! Т.к. мы находимся в режиме ECB, то мы знаем, что одинаковые блоки зашифрованных данных будут иметь одинаковое исходное значение. Предположим, что их значение при расшифровке будет NULL.

Тогда:
crypt(md5(key)) == 0xB1F4F9A523E36FD969F4573E25AF4540
которое означает:
0x00000000000000000000000000000000 xor md5(key) = 0xB1F4F9A523E36FD969F4573E25AF4540

Зная, что A xor 0 = A, мы получим:
md5(key) = 0xB1F4F9A523E36FD969F4573E25AF4540

Это все, что нужно для того, чтобы расшифровать остальные данные - просто XOR’им это значение с каждым 128-битным блоком в файле, в результате получим рабочий исполняемый файл SimpleCiph3r_.exe в той же папке, где лежит сам crackme SimpleCiph3r.exe (дотошные читатели могут поискать оригинальный ключ перебором. Как и говорил автор crackme, ключ простой - "cool").

Часть 2. Ищем ключ в SimpleCiph3r_.exe

Загрузив исполняемый файл в IDA, замечаем множество вызовов функций с префиксом "__vba". Похоже на то, что этот exe-файл содержит VisualBasic-код. Великолепно. Для повышения читабельности кода мы применяем скрипт vb.idc. Этот скрипт создает четыре точки входа (комбинация клавиш Ctrl-E в IDA): OPubOI1Event1 00404730, OPubOI1Event2 00404880, OPubOI1Event3 00401FD0, OPubOI1Event4 00404BD0.

image01.jpg

Запускаем SimpleCiph3r_.exe в отладчике IDA. Но, что это? Ошибка?!

image02.jpg

Это должно быть сработал какой-то антиотладочный прием. В окне IDA "Строки" (Shift-F12) находим строку "IsDebuggerPresent" (самый популярный трюк). Для обхода этой антиотладочной фишки в IDA я использую (и рекомендую) плагин IDAStealth. Перед отладкой настраиваем плагин (Edit -> Plugins -> IDAStealth): выставить флажки "Enable stealth when debugger starts" и "IsDebuggerPresent (Patch PEB/BeingDebugged)".

Ставим точки останова на все OPubOI1Event 1-4 и снова запускаем отладку. Если срабатывает точка останова, то мы пропускаем ее. Нам необходимо событие по нажатию кнопки "Check". Вводим данные, жмем кнопку, и мы попали на OPriOI1Event3.

Псевдокод OPriOI1Event3


Шифр Вижинера (16-й век, Рим)
k = C R Y P T O C R Y P T O C R Y P T
(+ mod 26)
m = W H A T A N I C E D A Y T O D A Y
c = Z Z Z J U C L U D T U N W G C Q S !

В этом шифре используется ключ - слово. Для алфавита A-Z поставим соответствие чисел от 1 до 26. В нашем примере - это слово "crypto", длиной 6 символов. И чтобы зашифровать сообщение, мы сообщение пишем под ключом. В нашем случае под сообщением - "whatanicedaytoday" (what a nice day today) - мы повторяем ключ столько раз, сколько необходимо для покрытия всего сообщении - "cryptocryptocrypt". А само шифрование заключается в том, чтобы сложить символы сообщения и ключа по модулю 26. Только для примера, смотрите, если вы добавите к Y букву А, то получим Z. А если T + A, то получим U. И так делаем для всех символов. И помните, что сложение идет по модулю 26. Т.е. после Z вы вернетесь к А. Так работает шифр Вижинера. А расшифрование такое же легкое. Для расшифровки вы бы написали зашифрованный текст над ключом. Повторяем ключ по всей длине шифротекста, а затем вычитаем символы ключа из символов шифротекста, чтобы получить оригинальный текст.

Отрывок из лекции "История криптографии" Дэна Боне

Нам надо найти ключ. Делаем так (рассматриваем строки без пробелов и знаков препинания):



Итак, пароль "greatness". Давайте введем пароль и посмотрим, что мы получим.
:)

.. Подождите, но снова ошибка. Что это дает?!
Оказывается, пароль не проходит проверку по длине. Длина должна быть больше 10, а длина нашего пароля – 9. Но т.к. пароль повторяется, мы можем просто указать "greatnessgreatness" или всю строку "greatnessgreatnessgreatnessg", и пароль принят!

image03.jpg

Пока не указано иное, содержимое этой страницы распространяется по лицензии Creative Commons Attribution-ShareAlike 3.0 License