Взлом простого crackme

😁
Олдфаг
Статус
Оффлайн
Регистрация
27 Ноя 2016
Сообщения
2,091
Реакции[?]
2,025
Поинты[?]
6K

Пожалуйста, зарегистрируйтесь или авторизуйтесь, чтобы увидеть содержимое.


Необходимые инструменты:
Капелька мозгов;
PEiDentifier;
W32Dasm;
Soft-ICE;
Delphi.
Не пугайтесь, если увидели в списке инструментов Delphi. Просто на этом примере я также хочу показать, как пишутся кейгены.

Подготовительные шаги:
Вводим в нужные окошки Name и Serial (желательно неправильные), нажимаем кнопочку Try it, и вылупляется сообщение о том, что мы ни хрена не угадали. Но не спешите закрывать окошко, сначала запомним, что написано в окошке: That isn't, keep on trying... Эта надпись понадобится нам в будущем.

Шаг первый
Исследуем на чем написана программа с помощью PEiDentifier. Как видно программа ничем не запакована и написана на Borland Delphi 2.0. Замечательно! Нам с вами сегодня повезло. Теперь пришло время ознакомиться с самим крякми: в нем есть два поля Name и Serial. Такие защиты называют имя-код, а вот что это означает:
1. На основе имени (Name) программа генерирует правильный регномер, который хранится в памяти, затем этот правильный номер сравнивается с введенным вами, а значит в этот момент его можно подсмотреть в отладчике (например, в Soft-ICE).
2. Используются CRC-контрольные суммы. Это сложнее, чем пункт 1. Что это означает: существует два алгоритма: первый алгоритм анализирует имя и на его основе создает некую сумму (число); второй же анализирует введенный регномер и тоже создает сумму (число), затем эти суммы сравниваются. Чистый крэк в этом случае намного сложнее, чем, так называемый, битхак.
3. Очень редкий случай. Имя никакой роли не играет, а используется лишь регномер, он либо сравнивается с правильным номером, либо опять же превращается в сумму, которая сравнивается с эталоном.
Теперь мы представляем, что нас ждет. Третий случай можно откинуть (хотя я и встретился с ним впервые при взломе одого крякми).


Шаг второй
Загружаем наш крякми в W32Dasm. Ищем ссылки на записанную ранее на жалкий клочок бумаги строку. Об этом было написано в моем первом туториале. Нашли, нажали два раза и оказались здесь:
Код:
 * Possible StringData Ref from Code Obj ->"cyT0m!c's CrackMe #1"
 |
 :00425095 B920514200 mov ecx, 00425120
 
 * Possible StringData Ref from Code Obj ->"That isn't it, keep on trying..."
 |
 :0042509A BA38514200 mov edx, 00425138
 :0042509F A128764200 mov eax, dword ptr [00427628]
 :004250A4 E823CAFFFF call 00421ACC
 :004250A9 EB16 jmp 004250C1
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:00425091(C)
 |
 :004250AB 6A00 push 00000000
 
 * Possible StringData Ref from Code Obj ->"cyT0m!c's CrackMe #1"
 |
 :004250AD B920514200 mov ecx, 00425120
 
 * Possible StringData Ref from Code Obj ->"Hey, you have done it"
 |
 :004250B2 BA5C514200 mov edx, 0042515C
 :004250B7 A128764200 mov eax, dword ptr [00427628]
 :004250BC E80BCAFFFF call 00421ACC
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:004250A9(U)
 |
 :004250C1 3BFE cmp edi, esi
 :004250C3 7526 jne 004250EB
 
 Я сразу впечатал кусок и с вызовом другого окошечка, где нас благодарят
 и хвалят.
 Здесь вроде все понятно, прокрутим немного вверх:
 
 :0042507B 8D55F8 lea edx, dword ptr [ebp-08]
 :0042507E E871D7FDFF call 004027F4
 :00425083 8BF0 mov esi, eax
 :00425085 8B45FC mov eax, dword ptr [ebp-04]
 :00425088 E813010000 call 004251A0
 :0042508D 8BF8 mov edi, eax
 :0042508F 3BFE cmp edi, esi
 :00425091 7418 je 004250AB ;<----------подозрительный переход
 :00425093 6A00 push 00000000
 
 Смотрим, а куда посылает нас этот переход, ух ты, а ведь посылает он нас
 на правельный путь:
 
 :004250AB 6A00 push 00000000
 
 * Possible StringData Ref from Code Obj ->"cyT0m!c's CrackMe #1"
 |
 :004250AD B920514200 mov ecx, 00425120
 
 * Possible StringData Ref from Code Obj ->"Hey, you have done it"
 |
 :004250B2 BA5C514200 mov edx, 0042515C
 :004250B7 A128764200 mov eax, dword ptr [00427628]
 :004250BC E80BCAFFFF call 00421ACC
 
 
 Значит, мы близки и к проверке наших введенных данных. Теперь пора посмотреть на
 куски повнимательнее. Вызывает подозрения следующие команды:
 
 :0042507E E871D7FDFF call 004027F4
 :00425083 8BF0 mov esi, eax ;<------ результат процедуры записывается
 в esi
 :00425085 8B45FC mov eax, dword ptr [ebp-04]
 :00425088 E813010000 call 004251A0
 :0042508D 8BF8 mov edi, eax ;<------ результат процедуры записывается
 в edi
 :0042508F 3BFE cmp edi, esi ;<------- а вот и сравнение двух
 результатов
 :00425091 7418 je 004250AB ; <------- переход к вызову окошка
 с благодарностями.
 
 Ну что ж, пора посмотреть и на сами процедурки call 004251A0 и cal 004027F4
 Сначала первая:
 
 * Referenced by a CALL at Address:
 |:00425088
 |
 :004251A0 53 push ebx
 :004251A1 89C3 mov ebx, eax ;<------ в ebx помещается адрес,
 который находится в eax
 :004251A3 83FB00 cmp ebx, 00000000 ;<--Сравнение адреса с нулем
 :004251A6 7413 je 004251BB ;<--переход если равен
 :004251A8 B801000000 mov eax, 00000001 ;<--подготовка eax для
 дальнейших действий
 :004251AD 31C9 xor ecx, ecx ;<--обнуляем ecx
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:004251B9(U)
 |
 :004251AF 8A0B mov cl, byte ptr [ebx] ;<-- А вот то что нам нужно,
 в cl заносится один байт,
 а именно код ASCII того
 что находится по адресу
 ebx
 :004251B1 80F900 cmp cl, 00 ;<----сравнение кода с нулем
 :004251B4 7405 je 004251BB;<----если равен, то на выход
 :004251B6 F7E1 mul ecx ;<----это эквивалентно eax=eax*ecx,
 теперь понято зачем в eax заносилась
 единица
 :004251B8 43 inc ebx ;<----увеличиваем ebx на 1, то есть
 переходим на адрес следующего
 символа.
 :004251B9 EBF4 jmp 004251AF ;<----повторяем.
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
 |:004251A6(C), :004251B4(C)
 |
 :004251BB 25FFFFFF0F and eax, 0FFFFFFF ;<--последний шаг при вычислении
 контрольной суммы, надо и его
 учесть
 :004251C0 5B pop ebx
 :004251C1 C3 ret ;<----выход.
Ну вот и разобрались с этим алгоритмом. В двух словах: здесь перечисляется все введенные символы одного из полей (name или serial), затем контрольная сумма eax умножается на код текущего символа, то есть сумма равна (в представлении Delphi):
Summa:=ord(name[1])*ord(nam[2])*...*ord(nam[length(nam)]);
Вот и все, хотя нет, я забыл учесть строчку and eax, 0FFFFFFF. Поэтому, окончательный ответ:
Summa:=ord(name[1])*ord(nam[2])*...*ord(nam[length(nam)]);
summa:=Summa and $0FFFFFFFF; правда эта команда не осуществима в Delphi, так как операндами and могут быть только переменные типа Byte, а Summa у нас имеет тип DWord. Но это так отступление.
Перейдем ко второй процедуре:
Громадная попалась, но мы ребята хитрые и поэтому вычленим из этого кусища, кусок поменьше, а остальное пусть гуляет.
Код:
 * Referenced by a CALL at Addresses:
 |:00405472 , :0042507E
 |
 :004027F4 53 push ebx
 :004027F5 56 push esi
 :004027F6 57 push edi
 :004027F7 89C6 mov esi, eax
 :004027F9 50 push eax
 :004027FA 85C0 test eax, eax
 :004027FC 7451 je 0040284F
 :004027FE 31C0 xor eax, eax
 :00402800 31DB xor ebx, ebx
 :00402802 BFCCCCCC0C mov edi, 0CCCCCCC
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:0040280D(C)
 |
 :00402807 8A1E mov bl, byte ptr [esi]
 :00402809 46 inc esi
 :0040280A 80FB20 cmp bl, 20
 :0040280D 74F8 je 00402807
 :0040280F B500 mov ch, 00
 :00402811 80FB2D cmp bl, 2D
 :00402814 7445 je 0040285B
 :00402816 80FB2B cmp bl, 2B
 :00402819 7442 je 0040285D
 :0040281B 80FB24 cmp bl, 24
 :0040281E 7442 je 00402862
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:00402860(U)
 |
 :00402820 84DB test bl, bl
 :00402822 7432 je 00402856
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:0040283C(C)
 |
 :00402824 80EB30 sub bl, 30
 :00402827 80FB09 cmp bl, 09
 :0040282A 772A ja 00402856
 :0040282C 39F8 cmp eax, edi
 :0040282E 7726 ja 00402856
 :00402830 8D0480 lea eax, dword ptr [eax+4*eax]
 :00402833 01C0 add eax, eax
 :00402835 01D8 add eax, ebx
 :00402837 8A1E mov bl, byte ptr [esi]
 :00402839 46 inc esi
 :0040283A 84DB test bl, bl
 :0040283C 75E6 jne 00402824
 :0040283E FECD dec ch
 :00402840 7410 je 00402852
 :00402842 85C0 test eax, eax
 :00402844 7C10 jl 00402856
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
 |:00402854(C), :00402899(U)
 |
 :00402846 59 pop ecx
 :00402847 31F6 xor esi, esi
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:00402859(U)
 |
 :00402849 8932 mov dword ptr [edx], esi
 :0040284B 5F pop edi
 :0040284C 5E pop esi
 :0040284D 5B pop ebx
 :0040284E C3 ret
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
 |:004027FC(C), :0040286C(C)
 |
 :0040284F 46 inc esi
 :00402850 EB04 jmp 00402856
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:00402840(C)
 |
 :00402852 F7D8 neg eax
 :00402854 7EF0 jle 00402846
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
 |:00402822(C), :0040282A(C), :0040282E(C), :00402844(C), :00402850(U)
 |:00402884(C), :0040288B(C)
 |
 :00402856 5B pop ebx
 :00402857 29DE sub esi, ebx
 :00402859 EBEE jmp 00402849
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:00402814(C)
 |
 :0040285B FEC5 inc ch
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:00402819(C)
 |
 :0040285D 8A1E mov bl, byte ptr [esi]
 :0040285F 46 inc esi
 :00402860 EBBE jmp 00402820
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:0040281E(C)
 |
 :00402862 BFFFFFFF0F mov edi, 0FFFFFFF
 :00402867 8A1E mov bl, byte ptr [esi]
 :00402869 46 inc esi
 :0040286A 84DB test bl, bl
 :0040286C 74E1 je 0040284F
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:00402897(C)
 |
 :0040286E 80FB61 cmp bl, 61
 :00402871 7203 jb 00402876
 :00402873 80EB20 sub bl, 20
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:00402871(C)
 |
 :00402876 80EB30 sub bl, 30
 :00402879 80FB09 cmp bl, 09
 :0040287C 760B jbe 00402889
 :0040287E 80EB11 sub bl, 11
 :00402881 80FB05 cmp bl, 05
 :00402884 77D0 ja 00402856
 :00402886 80C30A add bl, 0A
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:0040287C(C)
 |
 :00402889 39F8 cmp eax, edi
 :0040288B 77C9 ja 00402856
 :0040288D C1E004 shl eax, 04
 :00402890 01D8 add eax, ebx
 :00402892 8A1E mov bl, byte ptr [esi]
 :00402894 46 inc esi
 :00402895 84DB test bl, bl
 :00402897 75D5 jne 0040286E
 :00402899 EBAB jmp 00402846
 :0040289B C3 ret
 
 А вот обещанный кусочек:
 
 :004027FE 31C0 xor eax, eax ;<-----здесь начинается подготовка
 к преобразованиям
 :00402800 31DB xor ebx, ebx
 :00402802 BFCCCCCC0C mov edi, 0CCCCCCC ;<-- edi=0CCCCCCC, константа!!!
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:0040280D(C)
 |
 :00402807 8A1E mov bl, byte ptr [esi]; <---опять передача кода ASCII
 в bl. Значит сейчас будет
 второй алгоритм.
 :00402809 46 inc esi <--- увеличиваем адрес на 1
 :0040280A 80FB20 cmp bl, 20 <---Сравниваем код с кодом ' ' - пробел.
 :0040280D 74F8 je 00402807
 :0040280F B500 mov ch, 00
 :00402811 80FB2D cmp bl, 2D <--Сравниваем с '-'
 :00402814 7445 je 0040285B
 :00402816 80FB2B cmp bl, 2B <---Сравниваем с '+'
 :00402819 7442 je 0040285D
 :0040281B 80FB24 cmp bl, 24 <---Сравниваем с '$'
 :0040281E 7442 je 00402862
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:00402860(U)
 |
 :00402820 84DB test bl, bl <---А не равен ли bl 0(нулю)?
 :00402822 7432 je 00402856
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:0040283C(C)
 |
 :00402824 80EB30 sub bl, 30 <----- А вот и пошло преобразование:
 из кода вычитаем 30h, зачем
 спросите вы, а затем, чтобы в bl
 получить истинное значение цифры
 так как код 0-30h, 1-31h и так далее
 :00402827 80FB09 cmp bl, 09 <--сравнение цифры с 9
 :0040282A 772A ja 00402856 <--если больше то переход
 :0040282C 39F8 cmp eax, edi <--сравниваем eax с edi=0CCCCCCC
 :0040282E 7726 ja 00402856 <--выход если больше!
 А вот и сами преобразования:
 :00402830 8D0480 lea eax, dword ptr [eax+4*eax];<--eax:=eax*5
 :00402833 01C0 add eax, eax <--eax:=eax+eax
 :00402835 01D8 add eax, ebx <--eax:=eax+ebx
 :00402837 8A1E mov bl, byte ptr [esi] <--поместить в bl следующий код
 символа!!!
 :00402839 46 inc esi <---увеличение адреса!!!
 Что они означают? Сейчас разберемся. Вся процедура имеет ряд исключений, типа
 если введены не цифры! А мы отбросим все исключения, теперь понятно, что здесь
 производится подсчет суммы нашего введенного регномера. Будем вводить цифры!!!
 
 :0040283A 84DB test bl, bl
 :0040283C 75E6 jne 00402824
 :0040283E FECD dec ch
 :00402840 7410 je 00402852
 :00402842 85C0 test eax, eax
 :00402844 7C10 jl 00402856
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
 |:00402854(C), :00402899(U)
 |
 :00402846 59 pop ecx
 :00402847 31F6 xor esi, esi
 
 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
 |:00402859(U)
 |
 :00402849 8932 mov dword ptr [edx], esi
 :0040284B 5F pop edi
 :0040284C 5E pop esi
 :0040284D 5B pop ebx
 :0040284E C3 ret
 
 Лично я взял кусок бумаги и прогнал число 123, по следующему алгоритму:
 
 xor eax, eax
 xor ebx, ebx
 mov edi, 0CCCCCCC
 mov bl, byte ptr [esi]
 sub bl, 30
 cmp eax, edi
 ja 00402856
 lea eax, dword ptr [eax+4*eax]
 add eax, eax
 add eax, ebx
 mov bl, byte ptr [esi]inc esi
 test bl, bl
 jne 00402824
Эта вытяжка из того что описано выше. Прогнал я число 123 и что я вижу, сумма моя (eax) равна 123, то есть алгоритм-то никаких преобразований с регномером, составленного из цифр, не делает!!! Сумма равна числу равному регномеру!!!!
А теперь сядьте и подумайте, что нам это дает! Подумали, теперь подумайте еще и вас осенит, что правельный регномер равен первой сумме (помните первую процедуру). Вот и все!!!
НЕ та уж и сложно!!!! А вот и кейген на Delphi, создайте в форме два объекта Edit, и кнопку Button, нажмите два раза на кнопку и запишите эту процедуру:
Код:
 procedure TForm1.Button1Click(Sender: TObject);
 label M1, M2;
 var nam:String; Key:Dword; i:Byte; M:Array [1..12] of Byte;
 begin
 nam:=Edit1.text;
 for i:=1 to length(nam)
 do M[i]:=ord(nam[i]);
 M[length(nam)+1]:=0;
 asm
 pusha
 xor ecx, ecx
 lea ebx, M[0]
 mov eax, 01
 M1: mov cl, byte ptr [ebx]
 cmp cl, 00
 je M2
 mul ecx
 inc ebx
 jmp M1
 M2: and eax, $0fffffff
 mov key, eax
 popa
 end;
 Edit2.text:=inttostr(key);
 end;
 
 end.
Вот и все!
 
Сверху Снизу