R0 CREW

Crackme #00

Доброго времени суток!

Данная мини-статья расскажет о том, как пройти «Crackme #00» от [KSDR], который заключающегося в обнаружении «валидного кода» или другими словами «серийного кода».

Я не буду объяснять, почему я сделал то-то или то-то, так как это не полноценная статья. Если вам, что-то будет не понятно, то постарайтесь додумать сами, другими словами «реверсируйте себя» иначе никакого толка от обычного исполнения инструкций - не будет.

И так приступим. Для начала просто запустим подопытную программу. В нашем случае это «r0 Crew_crackme.exe»:

Рисунок 0.

Видим два поля. Первое отвечает за «ввод строки», второе сообщает о правильности или неправильности введенной строки. На этом ознакомление с программой закончим и закроем ее.

Далее, загрузим подопытную программу в OllyDbg. Видим следующее окно:

Рисунок 1.

Опираясь на данное окно можно сказать, что программа, возможно, чем-то упакована… Нажимаем «Ок» и видим следующее:

Рисунок 2.

Оставляем все как есть и попытаемся определить, чем упакована программа, так как это позволит произвести более быстрый взлом. Для определения упаковщика я использовал PEID, который сказал, что мы имеем дело с ASPack 2.12.

Окей =) Зная чем упакована программа мы можем использовать определенную последовательность действий, которая позволит комфортно и быстро добраться до «Точки Входа», а там уже сможем обнаружить участок кода, который отвечает за манипуляции с «вводимой строкой».

Возвращаемся к OllyDbg:

Рисунок 3.

Видим, что находимся за тридевять земель, в тридесятом царстве – не понятном государстве, от нашей цели. Что же – поправим ситуацию. Давайте зайдем в «Memory Map», и поставим бряк на секцию кода по адресу «00401000»:

Рисунок 4.

Далее жмем «Run» и вуаля - обнаружим очень знакомые инструкции ASPack =)

Рисунок 5.

Теперь пройдем на «OEP». Для этого опустимся на одну инструкцию в низ, по адресу «0047E002»:

Рисунок 6.

Перейдем к регистру «ESP» (0012FFA4):

Рисунок 7.

И зайдем в «Dump» (Follow in Dump):

Рисунок 8.

Рисунок 9.

Далее поставим Hardware-бряк, на доступ, на «двойное слово», т.е. на первые четыре байта:

Рисунок 10.

После этого не забудем зайти в «Memory Map» и снять ранее поставленный бряк, да бы не отвлекал лишними срабатываниями:

Рисунок 11.

Затем жмем «Run», после чего должен сработать Breakpoint и мы окажемся тут:

Рисунок 12.

Далее проходим все по «F7» и после прохода по «RETN» окажемся тут:

Рисунок 13.

Затем вызываем «Analyse code» (Ctrl + A) и видим тело подопытной программы:

Рисунок 14.

Далее посмотрим на то какие «строки» мы сможем обнаружить. Для этого делаем «Search for –> All referenced text string»:

Рисунок 15.

Рисунок 16.

Как видим там присутствуют:

Text string=ASCII “Access denied!” и Text string=ASCII “The code is accepted”.

Первая как мы видели, запуская программу в первый раз, появляется всякий раз когда «серийный код» введен не правильно, вторая же - появится, как только мы введем «правильный код».

Клацнем на одну из строчек и посмотрим где мы окажемся:

Рисунок 17.

Примечание: Тут, красным отмечены строки “Access denied!” и “The code is accepted”, синим отмечен алгоритм дешифрации, а зеленым отмечен алгоритм проверки строк.

Как оказалось мы попали в самое логово данной программы. Прокрутив немного в верх замечаем алгоритм «дешифровки строки», после которого следует сравнение полученной строки с «REVERSE4YOU.ORG’». Собственно идти нам уже ни куда не нужно, а нужно только проанализировать, то каким образом получить легитимную строку. Собственно этим и займемся.

Начнем с адреса «004010A1», в котором производится проверка, на то что передаваемая строка не нулевая.

По адресу «00401023» производится проверка на то, что количество передаваемых символов в строке равно 16.

В последующих инструкциях начиная с «0040102С» определяется место расположение введенных данных, а так же определяется цикл с 8-ю обходами.

В теле цикла каждые два байта меняются местами и происходит это 8-сем раз, т.е. пока не переберем всю строку «8 проходов цикла * по 2 байта за проход = 16 байт». Для тех кто не понял совершается такое действие, например:

У нас есть исходная строка:

0 1 2 3 4 5 6 7 8 9 A B C D E F

После про работки выше указанного цикла, наша строка преобразуется в такую:

1 0 3 2 5 4 7 6 9 8 B A D C F E

Далее следует еще один цикл, с 16-ю обходами. В этом цикле происходит отнимание от каждого байта строки числа «0D». Затем над полученным числом производится операция «XOR, с Еденицей». После чего байт сохраняется и цикл переходит к следующему байту строки и так до конца.

Чуть ниже мы видим, что полученная таким образом строка, находящаяся по адресу «00406151», сравнивается со строкой «REVERSE4YOU.ORG’», отсюда получается, что мы должны используя знания полученные при анализе «алгоритма дешифровки», зашифровать строку «REVERSE4YOU.ORG’», так что бы она прошла проверку.

Ну что же, попробуем зашифровать…

Для начала возьмем строку «REVERSE4YOU.ORG’» и проксорим каждый байт с единицей:

  [B]Исходная строка: [/B]
  [B]String: [/B]REVERSE4YOU.ORG’
  [B]HEX:[/B] 52 45 56 45 52 53 45 34 59 4f 55 2e 4f 52 47 27
   
  [B]После преобразования:[/B] 
  [B]XOR(HEX,1):[/B] 53 44 57 44 53 52 44 35 58 4e 54 2f 4e 53 46 26

Далее добавим к каждому байту «0D»:

  [B]Исходная:[/B]
  [B]HEX:[/B] 53 44 57 44 53 52 44 35 58 4e 54 2f 4e 53 46 26
   
  [B]После преобразования:[/B]
  [B]HEX:[/B] 60 51 64 51 60 5f 51 42 65 5b 61 3c 5b 60 53 33

Далее «разделим строку», по 2 байта, в паре:

  [B]Исходная:[/B]
  [B]HEX:[/B] 60 51 64 51 60 5f 51 42 65 5b 61 3c 5b 60 53 33
   
  После преобразования:
  [B]HEX:[/B] | 60 51 | 64 51 | 60 5f | 51 42 | 65 5b | 61 3c | 5b 60 | 53 33 |

Далее «расставим пары» в обратном порядке, т.е., например, у нас есть такие пары:

 | 1a 1b | 2a 2b | 3a 3b | 4a 4b | 5a 5b | 6a 6b | 7a 7b | 8a 8b | 

Согласно выше сказанному, мы должны разместить их таким образом:

 | 8a 8b | 7a 7b | 6a 6b | 5a 5b | 4a 4b | 3a 3b | 2a 2b | 1a 1b | 

Произведем это же над нашей строкой:

  [B]Исходная:[/B]
  [B]HEX:[/B] | 60 51 | 64 51 | 60 5f | 51 42 | 65 5b | 61 3c | 5b 60 | 53 33 |
   
  [B]После преобразования:[/B]
  [B]HEX:[/B] | 53 33 | 5b 60 | 61 3c | 65 5b | 51 42 | 60 5f | 64 51 | 60 51 |

Переведя «HEX в ASCII» получим такую строку:

String: S3[a<e[QB_dQ`Q

Запускаем программу, вставляем полученную строку и видим сообщение, которое говорит нам, что мы справились с заданием =)

Рисунок 18.

Успехов!

Prosper-H © r0 Crew

Какая-то хорошая антиотладка в программе имеется или у меня дебаггер по другой причине так бешенно вылетает? (W7SP1+Olly 1.01+HideOD Plugin со всеми включенными опциями). За Алго отдельное спасибо. Простенько, но со вкусом. Просветляет :slight_smile:

Семерка не всегда адекватно ведет себя с подобным.

В рамках подготовки по плюсам я написал свой скромный код :slight_smile:

Offtop
#include <iostream>

using namespace std;

int main()
{
	char src[] = "REVERSE4YOU.ORG'";
	for(int i=0; i<int(strlen(src)); i++)
	{
		*(src+i)^=1;
		*(src+i)+=13;		
	}
	for(int i=0; i<int(strlen(src)); i+=2)
	{
		char tmp = *(src+i);
		*(src+i) = *(src+i+1);
		*(src+i+1) = tmp;
	}
	for(int i = 0, j = 15; i<8; i+=2, j-=2)
	{
		char tmp = *(src+i);
		*(src+i) = *(src+j);
		*(src+j) = tmp;
		tmp = *(src+i+1);
		*(src+i+1) = *(src+j-1);
		*(src+j-1) = tmp;
	}
	cout << "Final string: " << src << endl;
	return 0;
}

Если речь идет о плюсах, то может быть есть смысл использовать плюсовые же строки? Как нибудь так:

Offtop
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
	string src("REVERSE4YOU.ORG'");
	if (src.length()%2 == 1) {
		return 0;
	}
	for(string::iterator i = src.begin(); i != src.end(); ++i) 
	{
		*i ^= 1;
		*i += 13;       
	}
	for(string::iterator i = src.begin(); i != src.end(); i+=2) 
	{
		iter_swap(i, (i+1));
	}
	for(string::iterator i = src.begin(); i != src.end(); i+=2) 
	{
		string::reverse_iterator j = src.rbegin() + distance(src.begin(), i);
		iter_swap(i, j);
	}
	cout << "Final string: " << src << endl;
        return 0;
}

Избыточный код. Читается плохо. Так-то лучше будет.

Offtop
#include <iostream>

using namespace std;

int main()
{
	char src[] = "REVERSE4YOU.ORG'";

	for (int i = 0; i < sizeof(src) - 1; i++)
	{
		src[i] ^= 1;
		src[i] += 13;
	}

	for (int i = 0; i < sizeof(src) - 1; i += 2)
		swap(src[i], src[i + 1]);

	for (int i = 0, j = 15; i < 8; i += 2, j -= 2)
	{
		swap(src[i], src[j]);
		swap(src[i + 1], src[j - 1]);
	}
	
	cout << src << endl;
	
	return 0;
}

Собственно соглашусь с сенсеем, избыточный код получился :slight_smile: