Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Альтернативный Метод написания КейГена на примере Professional Minesweeper v1.2 Shareware version

Оформил: DeeCo
Автор: http://www.cracklab.narod.ru

Инструменты:
1) SoftIce
2) С++
3) Знание С++&&Asm (и опыт работы :)

На примере этой простой (в плане защиты) программки я покажу, как просто научится писать КейГены. Конечно же эта статья не распространяется на защиты, в которых быстрый перебор невозможен, однако...
Моё мнение таково: в каждом отдельно взятом случае своя ситуация, по-своему неповторимая и интересная. Так что применять какие-то алгоритмы или правила в данных ситуациях просто глупо. Однако, из таких вот пустяшных туториалов можно почерпнуть много общих идей, при помощи которых позже вы сможете сами анализировать защитный механизм и сами писать КейГен.
Итак, в данной статье мы:

- Найдём процедуру проверки РН (рег.номер).
- Проанализируем все действия этой процедуры
- Напишем на С++ КейГен, который будет емулировать эту процедуру.
- Лирические отступления.

(1) Локализовываем защиту.

Итак, найдя окно регистрации, введём в него "vallkor" и "123321" нажмём "ОК" и увидим MessageBox с инфой о том, что РН неправильный.
Вываливаем Айс и ставим бряк:
bpx MessageBoxA do "p ret" и выйдя из айса, опять нажмём "ОК", при этом окажемся тут:
015F:00404FE3    PUSH      0041EDC8                      
015F:00404FE8    PUSH      65                            
015F:00404FEA    PUSH      EBX
015F:00404FEB    CALL      USER32!GetDlgItemTextA	<--Считали имя
015F:00404FF0    PUSH      0A
015F:00404FF2    PUSH      0041EE18
015F:00404FF7    PUSH      66
015F:00404FF9    PUSH      EBX
015F:00404FFA    CALL      USER32!GetDlgItemTextA	<--Считали РН
015F:00404FFF    PUSH      0041EE18			<--сохранили адрес РН
015F:00405004    PUSH      0041EDC8			<--сохранили адрес имени
015F:00405009    CALL      00405657			<--!!!Процедура проверки
015F:0040500E    ADD       ESP,08
015F:00405011    MOV       [0041EE22],AL
015F:00405016    TEST      AL,AL
015F:00405018    JZ        0040502B			<--Если её результаты плохи - прыжок на плохое сообщение
015F:0040501A    MOV       BYTE PTR [0041C074],00
015F:00405021    PUSH      00
015F:00405023    PUSH      EBX
015F:00405024    CALL      USER32!EndDialog		<--иначе - закроем окно
015F:00405029    JMP       0040503E
015F:0040502B    PUSH      30
015F:0040502D    PUSH      DWORD PTR [0041C078]
015F:00405033    PUSH      0041EF13
015F:00405038    PUSH      EBX
015F:00405039    CALL      USER32!MessageBoxA
Как вы уже поняли, процедура считывания Имени и РН да и сам вызов Функции проверки РН на валидность очень прозрачны и сразу можно догадаться, что CALL 00405657 - вызов необходимой нам процедуры.
Теперь необходимо понять логику этой процедуры...

(2) Анализ защитного механизма.

Зайдём в функцию, которую мы обнаружили и увидим:
015F:00405657    PUSH      EBP                           
015F:00405658    MOV       EBP,ESP                       
015F:0040565A    ADD       ESP,-1C
015F:0040565D    PUSH      EBX
015F:0040565E    PUSH      ESI                            
015F:0040565F    PUSH      EDI                            
015F:00405660    MOV       EBX,[EBP+0C]                   
015F:00405663    MOV       ECX,[EBP+08]                   
015F:00405666    XOR       EAX,EAX                        
015F:00405668    MOVSX     EDX,BYTE PTR [EAX+EBX]        <--Загружаем первую цифру РН 
015F:0040566C    ADD       EDX,-30			 <--Переводим её из символьного в цифровой вид
015F:0040566F    MOV       [EAX*4+EBP-1C],EDX            <--сохраняем по адресу EBP-1C 
015F:00405673    CMP       DWORD PTR [EAX*4+EBP-1C],00   <--сравниваем, цифра ли это 
015F:00405678    JL        00405681                      <--если 
015F:0040567A    CMP       DWORD PTR [EAX*4+EBP-1C],09   <-- нет
015F:0040567F    JLE       004056A4                      <--  то выходим из функции проверки
015F:00405681    PUSH      0041C42D                       
015F:00405686    PUSH      ECX                            
015F:00405687    CALL      00414780                       
015F:0040568C    ADD       ESP,08                         
015F:0040568F    PUSH      0041C435                       
015F:00405694    PUSH      EBX                            
015F:00405695    CALL      00414780                       
015F:0040569A    ADD       ESP,08                         
015F:0040569D    XOR       EAX,EAX
015F:0040569F    JMP       00405749                       
015F:004056A4    INC       EAX                            
015F:004056A5    CMP       EAX,07                        <--Символов, судя по счётчику, должно быть 7
015F:004056A8    JL        00405668 
В этом куске кода программа проверяет, чтобы РН состоял только из Цифр и чтобы их было ровно 7.

Смотрим дальше:
015F:004056AA    IMUL      EAX,[EBP-0C],000003E8          
015F:004056B1    IMUL      EDX,[EBP-14],64                
015F:004056B5    ADD       EAX,EDX                        
015F:004056B7    MOV       EDX,[EBP-04]                   
015F:004056BA    ADD       EDX,EDX                        
015F:004056BC    LEA       EDX,[EDX*4+EDX]                
015F:004056BF    ADD       EAX,EDX                        
015F:004056C1    ADD       EAX,[EBP-1C]                   
015F:004056C4    IMUL      ESI,EAX,0D                    
015F:004056C7    MOV       EAX,ESI                   
015F:004056C9    MOV       ESI,000000C5                  
015F:004056CE    CDQ                                     
015F:004056CF    IDIV      ESI
015F:004056D1    MOV       ESI,EDX
===================
015F:004056D3    XOR       EDI,EDI                        
015F:004056D5    XOR       EAX,EAX                        
015F:004056D7    CMP       BYTE PTR [EAX+ECX],00          
015F:004056DB    JZ        004056E9                       
015F:004056DD    MOVSX     EDX,BYTE PTR [EAX+ECX]         
015F:004056E1    ADD       EDI,EDX                        
015F:004056E3    INC       EAX
015F:004056E4    CMP       EAX,50                         
015F:004056E7    JL        004056D7  
=====================                     
015F:004056E9    MOV       EAX,ESI                        
015F:004056EB    PUSH      ECX                            
015F:004056EC    MOV       ECX,0000000A                   
015F:004056F1    CDQ                                      
015F:004056F2    IDIV      ECX                            
015F:004056F4    POP       ECX                            
015F:004056F5    ADD       EAX,EDI                        
015F:004056F7    MOV       EDI,00000064                   
015F:004056FC    CDQ                                      
015F:004056FD    IDIV      EDI                            
015F:004056FF    MOV       EDI,EDX                        
015F:00405701    MOV       EAX,ESI
015F:00405703    MOV       ESI,0000000A                   
015F:00405708    CDQ                                      
015F:00405709    IDIV      ESI                            
015F:0040570B    MOV       EAX,EDI                        
015F:0040570D    ADD       EAX,EAX                        
015F:0040570F    LEA       EAX,[EAX*4+EAX]                
015F:00405712    ADD       EDX,EAX                        
015F:00405714    MOV       ESI,EDX                        
015F:00405716    IMUL      EAX,[EBP-10],64                
015F:0040571A    MOV       EDX,[EBP-18]                   
015F:0040571D    ADD       EDX,EDX                        
015F:0040571F    LEA       EDX,[EDX*4+EDX]                
015F:00405722    ADD       EAX,EDX                       
015F:00405724    ADD       EAX,[EBP-08]              
015F:00405727    CMP       EAX,ESI                       
015F:00405729    JZ        00405749                      
Здесь можно было бы начать анализировать алгоритм, но я решил поступить оригинально, я выяснил, что скрывается под [EBP-xx]:
[EBP-04]=7цифра РН
[EBP-08]=6цифра РН
[EBP-0C]=5цифра РН
[EBP-10]=4цифра РН
[EBP-14]=3цифра РН
[EBP-18]=2цифра РН
[EBP-1С]=1цифра РН
Потом я выяснил, что делается в этом цикле:
015F:004056D3    XOR       EDI,EDI                        
015F:004056D5    XOR       EAX,EAX                        
015F:004056D7    CMP       BYTE PTR [EAX+ECX],00          
015F:004056DB    JZ        004056E9                       
015F:004056DD    MOVSX     EDX,BYTE PTR [EAX+ECX]         
015F:004056E1    ADD       EDI,EDX                        
015F:004056E3    INC       EAX
015F:004056E4    CMP       EAX,50                         
015F:004056E7    JL        004056D7 
А именно сумируются все сиволы введённого имени и результат записывается в EDI

Заня это я решил не упрощать задачу, а просто "эмулировать" процедуру проверки. Т.е. я взял C++ и там создал переменные
unsigned long	eax,ebx,edx,ecx,edi,esi,SUM;
Т.е. все необходимые регистры процессора и переменная SUM, в которой будет хранится сумма всех символов введённого имени.

Завёл переменную
char	number[7]="\0\0\0\0\0\0\0";
это будет число, которое мы будем наращивать в цикле и смотреть, является ли оно правильным РН или нет.

Вобщем сделал все так, как в программе (Люди, которые мало-мальски знакомы с программированием поймут меня, а люди, которые нет - им в писании кейгена места нету).

(3) Сам КейГен.
#include <iostream.h>
#include <conio.h>
void inc(char *number) //Функция увеличения на 1 нашего перебираемого числа { int i; number[6]++; if (number[6]>9) { for (i=6;i>=0;i--) { number[i]=0; number[i-1]++; if (number[i-1]<10) break; } } } void main() { char name[50]; //наше имя char number[7]="\0\0\0\0\0\0\0"; //перебираемое число unsigned long eax,ebx,edx,ecx,edi,esi,SUM,i,passcount=0; int k,l; cout<<"KeyGen For Professional Minesweeper v1.2 Shareware version \n"; cout<<"cracked by vallkor (vallkor@chat.ru) or http://vallkor/chat.ru \n\n"; cout<<"Enter your name:"; cin>>name; SUM=0; for (i=0;name[i]!=0;i++) //в этом цикле считаем сумму всех букв имени SUM=SUM+name[i]; for (i=0;i<9999999;i++) //главный цикл перебора //в котором мы переписываем всю логическую //часть функции проверки //преобразовывая ассемблерный код в C++ { eax=number[4]*0x3E8; edx=number[2]*0x64; eax+=edx; edx=10*number[6]; eax+=edx; eax+=number[0]; esi=eax*0x0D; eax=esi; esi=0xC5; edx=eax%esi; eax=eax/esi; esi=edx; edi=SUM; eax=esi; ecx=0x0A; edx=eax%ecx; eax=eax/ecx; eax+=edi; edi=0x64; edx=eax%edi; eax=eax/edi; edi=edx; eax=esi; esi=0x0A; edx=eax%esi; eax=eax/esi; eax=edi*10; edx+=eax; esi=edx; eax=number[3]*0x64; edx=number[1]*10; eax+=edx; eax+=number[5]; if (eax==esi) { for(k=0;k<7;k++) cout<<number[k]+0; cout<<"\n"; passcount++; } inc(number); //наращиваем перебираемое число и на следующее прохождение } cout<<"\n Password's Found:"<<passcount; //вывели количество найденых правильных РН для нашего имени getch(); } (4)
Как оказалось, для любого имени всегда можно найти 10000 правильных РН.
Т.е. даже простой чудак, перебрав сотню-другую паролей смог бы найти правильный для себя, но в том-то и прикол, что простой чувак не знает об этом.
Вообще приведеный метод анализа и построения перебора без детального разбора алгоритма хорош только в таких вот "слабых" защитах, где время, затраченное на перебор в 100 раз меньше, чем нужно для детального анализа.
К тому же если оптимизировать цикл (а там много чего можно оптимизировать, т.к. он просто сконвертирован в С++ из ассемблерного вида безо всякого изменения), то все пароли переберутся ещё в 10-ки раз быстрее.
Вобщем задача выполнена, программа закейгенена без особых усилий. А остальное уже флейм, а значит позвольте мне откланяться и идти переписывать конспекты закошенных по вине написания этого туториала лекций :)

Извращался, исследовал и наваял туториал:
vallkor //PTDS
E-mail: vallkor@chat.ru
Page : http://vallkor.chat.ru

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