Автор: DiveSlip <diveslip@pochta.ru>
Вот и понеслась череда моих туториалов, уже второй, а не за горами и третий. Опять же этот туториал предназначен для новичков, так как ломаться здесь будет легкий крякми, который взят с сайта www.crackmes.de/view.php?ViewID=1 Называется cyT0m!c's CrackMe #1. Легкость 0 или 1.
Капелька мозгов;
PEiDentifier;
W32Dasm;
Soft-ICE;
Delphi.
Не пугайтесь, если увидели в списке инструментов Delphi. Просто на этом
примере я также хочу показать, как пишутся кейгены.
Исследуем на чем написана программа с помощью PEiDentifier. Как видно программа ничем не запакована и написана на Borland Delphi 2.0. Замечательно! Нам с вами сегодня повезло. Теперь пришло время ознакомиться с самим крякми: в нем есть два поля Name и Serial. Такие защиты называют имя-код, а вот что это означает:
1. На основе имени (Name) программа генерирует правильный регномер, который
хранится в памяти, затем этот правильный номер сравнивается с введенным вами,
а значит в этот момент его можно подсмотреть в отладчике (например, в Soft-ICE).
2. Используются CRC-контрольные суммы. Это сложнее, чем пункт 1. Что это
означает: существует два алгоритма: первый алгоритм анализирует имя и на его
основе создает некую сумму (число); второй же анализирует введенный регномер
и тоже создает сумму (число), затем эти суммы сравниваются. Чистый крэк в
этом случае намного сложнее, чем, так называемый, битхак.
3. Очень редкий случай. Имя никакой роли не играет, а используется лишь
регномер, он либо сравнивается с правильным номером, либо опять же превращается
в сумму, которая сравнивается с эталоном.
Теперь мы представляем, что нас ждет. Третий случай можно откинуть (хотя я и встретился с ним впервые при взломе одого крякми).
* 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.
Вот и все! Второй туториал закончился. Удачи и успехов!
"И да пребудет с вами великий дух Bad-сектора" - цитата Fess'a!
DiveSlip.
Материалы находятся на сайте http://cracklab.narod.ru/doc/