Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Написание кейгена к Ad Muncher 4.3d
Автор: Fess

Три способа, которыми советские программисты достают программное обеспечение: воровство, грабеж, и обмен награбленным.

Target: Ad Muncher v4.3d

Tools:

  • Some brains
  • Soft-Ice v3.4
  • Win32Dasm 8.93
  • UPX (или другой unpack'ер UPX)
  • Delphi

Вступление

Что делает прога? Удаляет баннеры с Web-страниц закачиваеммых из Инета.

Что побудило меня сломать эту прогу? Да вообщем-то сущий пустяк, было нечего делать. Включил комп. Взял один из последних дисков журнала Hard&Soft за 4.2002. Установил первую попавшуюся на глаза прогу и тут оно началось...

Начало

Первое с чего начинается любой взлом это определить какую защиту имеет программа. В основном это имя-код, код, ключ-файл (отсортированы в порядке встречаемости в природе). Запускаем прогу, заходим в About, нажимаем нужную кнопочку и видим Name/Code, что и следовало ожидать. Все понятно.

Активные действия

Пишем любые имя и код. Нажимаем зарегистрировать. А нам фигу на чем-то очень похожим на MessageBox. Ладно лезем в Soft-Ice ставим бряк на GetWindowsTextA должна же как-то параметры брать? Опять жмем на кнопочку... Что не можете нажать? Функция говорите постоянно вызывается ну пойдем от обратного. Ставим бряк на MessageBoxA. Опять жмем. Вываливаемся в айсе. И видим это место.


0057C5E3 030D7FD04000            add ecx, dword ptr [0040D07F]
0057C5E9 6A00                    push 00000000
0057C5EB 51                      push ecx
0057C5EC 50                      push eax
0057C5ED FF7508                  push [ebp+08]
0057C5F0 6A5C                    push 0000005C
0057C5F2 E819AAFFFF              call 00407010
0057C5F7 EB2E                    jmp 0040C627
0057C5F9 663D6B00                cmp ax, 006B
0057C5FD 7513                    jne 0040C612

А почему сегмент кода в начинается с 5, а не 4. Наверное мы в какой-то библиотеке программы. Посмотрим... Да точно есть у нее библиотека. AdMunch.dll. Попробуем подсунуть ее Win32Dasm'у - не хочет. Значит запакована чем-то. Попробуем определить чем запакована на глазок. Запускаем любую смотрелку, смотрим, а там в начале написано UPX 1.2. Ну все понятно. Распаковываем. Я использовал сам паковщик UPX с ключем -d. Вот так-то лучше. Он в отличие от AsPack'a не портит заголовок файла. Так что можно его смело кидать в Win32Dasm. Дизассемблировался хорошо. Смотрим ссылки на строки. А там ничего связанного с регистрацией. Жаль. Можно посмотреть и в AdMuncher.exe, но и там тоже нет. Значит строки зашифрованы. (К сведению: по-моему глубокому мнению прога написана на асме отсюда и такой маленький размер).

Погоревали и ладно. Мы же знаем откуда вызывался MessageBox c 0057C5F2. Посмотрим - НЕТ! Аааа догадываемся мы, просто файл дизасмен со смещения 400000, а в память грузится с 570000. Тогда все ясно идем к точке 40C5F2. Да точно тотже call. Посмотрим кто его пользует. Мотаем вверх и видим


* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0040C4D4(U), :0040C4E7(C), :0040C529(C)

Значит возможен вызов из этих точек. Снова лезем в программу, ставим бряки на все три точки, запускаем регистрацию и вываливаемся в 57C4E7. Поосмотримся вокруг нее...


:0040C4D9 BF7B484600     mov edi, 0046487B
:0040C4DE B02D           mov al, 2D          <- Символ "-"
:0040C4E0 B914000000     mov ecx, 00000014   <- 14 символов
:0040C4E5 F2             repnz
:0040C4E6 AE             scasb
:0040C4E7 0F85E6000000   jne 0040C5D3        <- если не найден MessageBox
:0040C4ED C647FF00       mov [edi-01], 00    <- Найден, заменяем его 0

Из фрагмента кода становится понятно, что где-то в 14 символах ищется символ "-" (минус). Яснее ясного, что в коде. Ведь имя же может быть любым сказал я себе и написал в пароле один минус. Мой пароль был таков 11-22334455. Да, еще не забудьте поставить бряк на 57C4D9 (bpx 57C4D9).

Генерация ключа

Вываливаемся вна этом фрагменте и начинаем трассировать по шагам F8. Доходим до процедуры


:0057C4FC E8B1070000      call 0040CCB2

И заходим в нее. И видим следующий текст. Я написал комментарии и надеюсь из них все станет ясно.


:0057CCB2 33DB      xor ebx, ebx           <- Обнуление регистров
:0057CCB4 33C0      xor eax, eax           <- перед
:0057CCB6 33D2      xor edx, edx           <- использованием
:0057CCB8 8A1E      mov bl, byte ptr [esi] <-Первый байт кода (ESI указывает на код)
:0057CCBA 46        inc esi                <-Увеличиваем указатель
:0057CCBB F7E7      mul edi                <-Умножаем eax на edi (edi=10h)
:0057CCBD 83EB30    sub ebx, 00000030      <-Получаем реальное цисло из кода
:0057CCC0 83FB09    cmp ebx, 00000009      <-Если число больше 9 (в пароле
:0057CCC3 7603      jbe 0057CCC8           <- это цифры). Переходим
:0057CCC5 83EB07    sub ebx, 00000007      <-Если буквы - вычитаем еще 7
:0057CCC8 03C3      add eax, ebx           <-Добавляем к eax ebx
:0057CCCA 8A1E      mov bl, byte ptr [esi] <-Второй байт кода
:0057CCCC 83D200    adc edx, 00000000      <-Бессмыслица
:0057CCCF 80FB00    cmp bl, 00             <-Если 2-й байт 0, то
:0057CCD2 7426      je 0057CCFA            <-выход из процедуры
:0057CCD4 85D2      test edx, edx          <-Если edx<>0, то снова
:0057CCD6 75E2      jne 0057CCBA           <-то снова
:0057CCD8 8BC8      mov ecx, eax           <- ecx=eax
:0057CCDA 8BC2      mov eax, edx           <-eax=edx=0
:0057CCDC 46        inc esi                <-Увелич.указаеля на код
:0057CCDD F7E7      mul edi                <-Умножаем eax на edi (edi=10h)
:0057CCDF 91        xchg eax,ecx           <-Меняем местами ecx и eax
:0057CCE0 33D2      xor edx, edx           <-edx=0
:0057CCE2 F7E7      mul edi                <-Умножаем eax на edi (edi=10h)
:0057CCE4 83EB30    sub ebx, 00000030      <-Получаем реальное цисло из кода
:0057CCE7 83FB09    cmp ebx, 00000009      <-Если число больше 9 (в пароле
:0057CCEA 7603      jbe 0057CCEF           <- это цифры). Переходим
:0057CCEC 83EB07    sub ebx, 00000007      <-Если буквы - вычитаем еще 7
:0057CCEF 03C3      add eax, ebx           <- eax=eax+ebx
:0057CCF1 8A1E      mov bl, byte ptr [esi] <-Следующие байты кода
:0057CCF3 13D1      adc edx, ecx           <-edx=edx+ecx
:0057CCF5 80FB00    cmp bl, 00             <-Если еще есть символы в коде
:0057CCF8 75DE      jne 0057CCD8           <-То повторяем
:0057CCFA C3        ret                    <-Иначе выход из процедуры

Общий смылс этого куска кода: вычисление контрольного числа 1 куска кода (до знака минус). И сводится к такому куску кода


edx=0,eax=0
метка:
bl=один байт из адреса кода
если bl=00 (все символы кончились) то метка 3
eax=eax*10h
bl=bl-30
если это число (т.е. bl<=9),то метка2
иначе bl=bl-7
метка2:
иди к метке

Вот такая штука получается. Вышли из процедуры и видим такой код:


:0057C501 8BD8        mov ebx, eax       <-Результат процедуры поместили в ebx
:0057C503 BE7B474600  mov esi, 0046477B  <-Указатель на имя
:0057C508 33C9        xor ecx, ecx       <-Обнуляем
:0057C50A 33C0        xor eax, eax       <-регистры
:0057C50C AC          lodsb              <-Берем 1 байт кода и указатель
                                         < увеличиваем на 1 (eax=байт,esi=esi+1)
:0057C50D 03C8        add ecx, eax       <- ecx=ecx+eax
:0057C50F C1C908      ror ecx, 08        <- Сдвиг вправо на 8 байт
:0057C512 03CB        add ecx, ebx       <- ecx=ecx+ebx(результат предыдущей
                                         < процедуры)
:0057C514 3C00        cmp al, 00         < Символы кончились?
:0057C516 75F4        jne 0040C50C       < Нет. Следующий символ
:0057C518 F7D1        not ecx            < Да. Инверсия ecx.

Здесь даже выделять ничего не надо в кейген чистый код. Нашли еще один результат (в ecx). Смотрим дальше


:0057C51A 5E             pop esi
:0057C51B 51             push ecx          <-Сохраняется результат 2-го подсчета
:0057C51C BF10000000     mov edi, 00000010
:0057C521 E88C070000     call 0040CCB2     <-Вызываем еще раз функцию подсчета
                                           < аналогична первой за исколючением
                                           < считается 2-я часть кода
:0057C526 59             pop ecx
:0057C527 3BC8           cmp ecx, eax      <-Сравнение результата 2-го подсчета
                                           < и последней процедуры.
:0057C529 0F85A4000000   jne 0040C5D3

Смотрите результат первой процедуры влияет только на вторую процедуру, где подсчитывается контрольная сумма имени. Значит ее можно взять статической. Например, если код начинается так 1-, то добавка в ebx при подсчете будет 1. (Можете взять что-то другое). Кстати, если понаблюдать, то можно заметить, что все имя преобразуется в нижний регистр.

Написание кейгена

За неимением времени C++, я пока выучить не озаботился, по-этому, кейген будет на Delphi. Если прочитаете статью Dr.Golova, как писать маленькие проги на Делфи, можете переделать его. Но я сляпал просто.

1) Создаем форму и два компонента Edit с именами Code и Name
2) Одну кнопку.

Нажимаем два раза на кнопку и начинаем писать кейген. (Если немножко присмотреться, то обратная к третьей процедуре будет генерация из результата второй ее hex-вида) Вот моя процедура, я ее прокоментрирую и разобраться не составит труда:


procedure TForm1.Button1Click(Sender: TObject);
Var
  B:Byte;               // Щетчик цикла
  Col: Dword;           // Переменная для хранения котрольной суммы
  Name_: String;        // Переменная для хранения имени
begin
   COl:=0;                       //Обнуление переменной контрольной суммы
   Name_:=LowerCase(Name.Text);  //Все буквы в имени строчные (Помните)
   For B:=1 To Length(Name_)+1 Do  //
   Begin                           //
     Col := Col + Ord(Name_[B]);   //
     asm                           // Цикл генерации котрольной суммы
     mov ecx, Col                  // из имени
     ror ecx, 08                   //
     add ecx, 1                    //
     mov Col, Ecx                  //
     end;                          //
   End;                            //
   Col:= not Col;               // Инвертирование контрольной суммы

  Code.Text := '1-'+IntToHex(Col,8); //Вывод на экран полученного ключа
end;

Вот и все! Дело сделано. 3 часа парился писал это. Ломал немного дольше, спасибо автору за сие творение. Как вы видите писать кейгены не так сложно как кажется. Хотя иногда понять смысл формулы пропущенной через хреновый компилятор бывает не легко.

Удачи в Reversing Engeneering!

З.Ы. Товарищи по оружию будьте снисходительны не выкладывайте кряки на всеобщее использование. Товарищи программисты не надейтесть на совесть крякеров - делайте защиту лучше!

Все ругательства отправлять в null
Все остальное на lomovskih@yandex.ru

P.S. Запомните все материалы публикуются только в учебных целях и автор за их использование ответственности не несет!!

P.P.S. Возможно имеют место опечатки, заранее извините!

With best wishes Fess

И да пребудет с вами великий дух bad-сектора.

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay