Олдфаг
-
Автор темы
- #1
Пожалуйста, зарегистрируйтесь или авторизуйтесь, чтобы увидеть содержимое.
Необходимые инструменты:
Капелька мозгов;
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 ;<----выход.
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
А теперь сядьте и подумайте, что нам это дает! Подумали, теперь подумайте еще и вас осенит, что правельный регномер равен первой сумме (помните первую процедуру). Вот и все!!!
НЕ та уж и сложно!!!! А вот и кейген на 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.