Цель взлома:
#c4n crackme #3 - http://c4nprojects.cjb.net
Инструментарий
SoftIce v4.xx
IDA v4.xx
мозги 🙂
ВСТУПЛЕНИЕ
Этот крякмис является очередным из серии
заданий для новичков проекта Cracking 4 Newbies - IRC (EFNet)
at #cracking4newbies. Задание таково: объяснить, как
генерируется серийный номер и написать
кейген.
ИССЛЕДОВАНИЕ
Итак, рассмотрим этот простой кpякмис,
сделанный специально для начинающих.
Запустим его и посмотрим: программа просит
ввести Имя/Name и Код/Registration. Введем что-либо,
например cr0AKer и 99999999 и нажмем ОК. Wrong number -
говорит нам она. То есть "неправильно
набран номер" 🙂 Жмем снова ОК, и мы снова в
стартовой позиции.
Ладно, запустим SoftIce (Ctrl-D) и поставим бpяк:
bpx getdlgitemtexta
Почему этот? Ну, просто потому, что getwindowtexta
не срабатывает 😉 Жмем 'Ok' снова и выпадаем в
отладчик, F12 для выхода в вызывающую
процедуру:
.......
.......
0040107B push 32h ; max длина имени 49 символов:
0040107D push edx ; 31h+1
0040107E push 3E8h
00401083 push ebx
Вот как pаз наша пpоцедypа:
>00401084 call ds:GetDlgItemTextA ; Считываем имя
А попадаем мы вот сюда:
>0040108A test eax, eax ; Проверяем, введено ли хоть
что-то.
0040108C jnz short loc_4010AA ; Введено
0040108E push eax
Если бы мы не ввели имя, то перешли бы на
нижеследующий код, где y нас спросили бы, а
есть ли y нас, вообще, имя-то? ;))
0040108F push offset aNoName ; "No name!"
00401094 push offset aDonTYouHaveANa ; "Don't you have a name?!"
00401099 push ebx
0040109A call ds:MessageBoxA
Поскольку имя введено, то мы переходим на
следующий участок кода:
004010AA loc_4010AA: ; CODE XREF: sub_401000+8Cj
Вот сюда:
>004010AA xor esi, esi ; esi=0
004010AC push edi
004010AD xor edx, edx ; edi=0
Здесь производится суммирование hex -
значений символов имени и вычисляется его
длина:
004010AF Name: ; CODE XREF: sub_401000+C6j
004010AF movsx eax, byte ptr [ebp+edx-40h] ; символ имени
004010B4 add esi, eax ; суммируем символы имени
004010B6 lea edi, [ebp+var_40] ; edi=offset 'cr0AKer'
004010B9 or ecx, 0FFFFFFFFh
004010BC xor eax, eax
004010BE inc edx ; N символов
004010BF repne scasb ; ищем конец строки 'cr0AKer'
004010C1 not ecx
004010C3 dec ecx ; N символов
004010C4 cmp edx, ecx ; Все ли символы?
004010C6 jbe short Name ; Не все
004010C8 mov [ebp+arg_0], esi ; Сохраним сумму
В нашем случае сумма равна esi=268h
Далее производится операция сдвига над
посчитанной суммой:
004010CB shl [ebp+arg_0], 7 ;
В нашем случае получим shl 268h, 7h = 13400h
Теперь производится считывание введенного
нами кода:
004010CF lea ecx, [ebp+var_C] ; подготовка к
004010D2 push 0Ah ; считыванию
004010D4 push ecx ; введенного
004010D5 push 3E9h ; кода (max= 0Ah - 1 символов)
004010DA push ebx
004010DB call ds:GetDlgItemTextA ; считываем
004010E1 test eax, eax ; Есть код?
004010E3 pop edi
004010E4 jnz short loc_401102 ; Есть
Если бы программа не обнаружила бы
введенный код, то она бы перешла на
следующий участок кода и сказала бы все, что
она дyмает. 😉
004010E6 push eax
004010E7 push offset aNoSerial ; "No serial!"
004010EC push offset aNoSerialNumber ; "No serial number entered!"
004010F1 push ebx
004010F2 call ds:MessageBoxA
Но поскольку мы, не будь дураки, ввели код,
то нас препроводят в другое место, а именно
сюда:
00401102 loc_401102: ; CODE XREF: sub_401000+E4j
Что же мы здесь видим? А видим мы следующее:
наш введенный код преобразовывается при
помощи стандартной С'шной функции _atoi
(ASCII-->int) в целое число, которое
возвращается в eax:
00401102 lea edx, [ebp+var_C] ; edx=offset '99999999'
00401105 push edx
00401106 call _atoi ; 'Code' ASCII --> int
В eax сейчас преобразованный код, который в
нашем случае равен 05F5E0FFh.
А далее мы подходим к финальной части -
сравнению. Hо сначала еще надо подготовить
одно из сравниваемых значений:
Берется значение, полученное после
операции сдвига над суммой hex-значений
символов нашего имени и складывается с
самой суммой, которое y нас до сих пор
хранится в esi. Во как. 🙂
0040110B mov ecx, [ebp+arg_0] ; преобразованное 'cr0AKer'
0040110E add esp, 4
00401111 add ecx, esi ; складываем
В нашем случае оно будет равно ecx = 13668h
Вот и все! Теперь сравниваем и видим, что,
естественно, значения не совпадают 😉
00401113 cmp ecx, eax ; Сравнение
00401115 push 0
00401117 jnz short loc_401134 ; Если равно, то Ок!
00401119 push offset aGood ; "Good!"
0040111E push offset aCongratulation ; "Congratulations!!"
00401123 push ebx
00401124 call ds:MessageBoxA
В нашем слyчае 13668h, конечно, не равно 05F5E0FFh.
ПИШЕМ КЕЙГЕН
Итак, приступим к решению. Что мы знаем?
Что из символов введенного имени
высчитывается определенное число, которое
равно результату сдвига влево hex-сyммы
каждого hex-значения символа нашего имени.
Что это определенное число должно быть
равно значению, полученному путем
преобразования введенного кода из ASCII-стpоки
в целое.
Вот и все! Все, что нам нужно - это взять
наше имя, произвести над ним операцию,
описанную в пункте 1) и преобразовать его из
_целого_в_ASCII_. Таким образом мы и получим код,
соответствующий нашему имени. В общем, на
нормальном 🙂 языке это называется pевеpснyть
(от слова reverse) алгоритм.
Тепеpь мы можем написать кейген, как того
от нас требует задание. Если вы хотите
взглянуть на исходник моего, то можете
посмотреть его в этом архиве,
который заодно включает и сам туториал.
Претензии по качеству кода не принимаются (может
и кривовато, но работает ;)):
ПРИВЕТСТВИЯ
FIDO echo Ru.hacker.Dummy и всем ее обитателям
Всем хакерам в мире 🙂