скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Альтернативный Метод написания КейГена на примере 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






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook