Исследование защиты программы Becky! Internet Mail


Скриншот программы Becky! Internet Mail

В Японии умеют делать не только качественную электронику, но еще и очень интересные программы. Например, Becky! Internet Mail, серьезная альтернатива для других популярных почтовых клиентов. В ней есть много функций, которые до сих пор не реализованы у конкурентов: поддержка нескольких профилей для формирования писем, штатная портативная установка на сменные носители, отсроченная отправка писем, сравнительно небольшой размер, и другие прелести. Огорчает только цена в 40 вечнозеленых рублей и 30 дней пробного периода.

Качаем дистрибутив, устанавливаем или распаковываем, запускаем. После первоначальных настроек нам предлагают зарегистрироваться, вводим любые левые данные и получаем вполне ожидаемый отлуп "Illegal Passcode". Осмотр пациента показывает, что главный исполняемый файл ничем не упакован, а "нехорошая" строка находится в ресурсах под номером 60469:


"Нехорошая" строка в ресурсах

Там же рядом находятся строки сообщения об успешной регистрации, но они нам пока не нужны. Теперь перейдем непосредственно к кодокопанию. Загоняем исполняемый файл в дизассемблер и, когда он закончит анализ, поищем шестнадцатеричное значение идентификатора ресура с нужной строкой.
...
.text:005B8CE5 loc_5B8CE5:
.text:005B8CE5                 push    0FFFFFFFFh      ; int
.text:005B8CE7                 push    0               ; uType
; Идентификатор нашей строки из ресурсов
.text:005B8CE9                 push    0EC35h          ; int = 60469 
; Показать окно с сообщением
.text:005B8CEE                 call    ?AfxMessageBox@@YGHIII@Z
...

Тут все предельно ясно. Посмотрим откуда передается управление на этот фрагмент кода:
...
.text:005B8B91                 mov     [edi], eax
; Вызвать функцию проверки веденных регистрационных данных
.text:005B8B93                 call    sub_425660
.text:005B8B98                 test    eax, eax
; Если она вернула не 0, то серийник неправильный
.text:005B8B9A                 jnz     loc_5B8CE5
; Регистрация прошла успешно, сохранить данные регистрации
.text:005B8BA0                 mov     edi, [esp+34h+var_14]
.text:005B8BA4                 mov     ecx, [edi+78h]
.text:005B8BA7                 add     edi, 78h
.text:005B8BAA                 push    ecx             ; lpData
.text:005B8BAB                 push    offset aUser    ; "User"
.text:005B8BB0                 push    offset aLicense ; "License"
.text:005B8BB5                 mov     ecx, offset unk_687990
.text:005B8BBA                 call    sub_426D20
.text:005B8BBF                 push    esi             ; lpData
.text:005B8BC0                 push    offset aCode    ; "Code"
.text:005B8BC5                 push    offset aLicense ; "License"
.text:005B8BCA                 mov     ecx, offset unk_687990
.text:005B8BCF                 call    sub_426D20
.text:005B8BD4                 mov     edx, [ebp+0]
.text:005B8BD7                 push    edx             ; lpData
.text:005B8BD8                 push    offset aEmail_0 ; "EMail"
.text:005B8BDD                 push    offset aLicense ; "License"
.text:005B8BE2                 mov     ecx, offset unk_687990
...

Можно просто пропатчить функцию проверки серийного номера, чтобы она постоянно возвращала 0, а можно попробовать разобрать алгоритм генерации ключа. Лучше всего это делать под отладчиком, введя длинный серийный номер с неповторяющимися символами.


Вводим неправильные регистрационные данные

Первая стадия проверки:
; Длина серийника должна быть 12h (18 в десятичной системе) символов.
; При этом надо учитывать, что к серийнику перед проверкой дописывается
; в начало строка "RBK-" и общая длина считается с ней. Значит длина нашего
; серийника равна 14 сисволам
.text:0042566B                 cmp     dword ptr [edi-0Ch], 12h
.text:0042566F                 jnz     loc_425800
; В регистр AL заносится код символа "-"
.text:00425675                 mov     al, 2Dh
; 4-й, 9-й и 14-й символы в серийнике должны быть "-". Теперь мы знаем формат
; правильного серийного номера: RBK-XXXX-XXXX-XXXX. Напомню, "RBK-" дописывается
; автоматически, а нам надо ввести только строку XXXX-XXXX-XXXX
.text:00425677                 cmp     [edi+3], al
.text:0042567A                 jnz     loc_425800
.text:00425680                 cmp     [edi+8], al
.text:00425683                 jnz     loc_425800
.text:00425689                 cmp     [edi+0Dh], al
.text:0042568C                 jnz     loc_425800

Дальше строка разбивается на составляющие блоки по 4 символа:
.text:00425692                 push    esi
.text:00425693                 push    3               ; MaxCount
.text:00425695                 lea     eax, [esp+20h+var_4]
.text:00425699                 push    eax             ; int
.text:0042569A                 lea     ecx, [esp+24h+arg_0]
.text:0042569E                 call    sub_407180
.text:004256A3                 push    4               ; MaxCount
.text:004256A5                 push    4               ; int
.text:004256A7                 lea     ecx, [esp+24h+var_10]
.text:004256AB                 push    ecx             ; int
.text:004256AC                 lea     ecx, [esp+28h+arg_0]
.text:004256B0                 call    sub_407240
.text:004256B5                 push    4               ; MaxCount
.text:004256B7                 push    9               ; int
.text:004256B9                 lea     edx, [esp+24h+var_8]
.text:004256BD                 push    edx             ; int
.text:004256BE                 lea     ecx, [esp+28h+arg_0]
.text:004256C2                 call    sub_407240
.text:004256C7                 push    4               ; MaxCount
.text:004256C9                 push    0Eh             ; int
.text:004256CB                 lea     eax, [esp+24h+var_C]
.text:004256CF                 push    eax             ; int
.text:004256D0                 lea     ecx, [esp+28h+arg_0]
.text:004256D4                 call    sub_407240
; Тут выполняется сравнение первого блока со строкой "RBK". Зачем это
; сделано - непонятно, ведь она все равно дописывается автоматически
.text:004256D9                 push    offset aRbk_0   ; unsigned __int8 *
.text:004256DE                 lea     ecx, [esp+20h+var_4]
.text:004256E2                 push    ecx             ; int
.text:004256E3                 call    sub_409A30
.text:004256E8                 test    al, al
.text:004256EA                 jz      loc_4257D4

Следующая проверка:
; Взять 3-й и 4-й символы из первого блока и перевести их в число
.text:004256F0                 push    2               ; MaxCount
.text:004256F2                 lea     edx, [esp+20h+arg_0]
.text:004256F6                 push    edx             ; int
.text:004256F7                 lea     ecx, [esp+24h+var_10]
.text:004256FB                 call    sub_403670
.text:00425700                 mov     eax, [eax]
.text:00425702                 push    eax             ; char *
.text:00425703                 call    j__atol
.text:00425708                 add     esp, 4
.text:0042570B                 lea     ecx, [esp+1Ch+arg_0]
; Сохранить вычисленное значение в регистре ESI
.text:0042570F                 mov     esi, eax
.text:00425711                 call    sub_401B30
.text:00425716                 mov     eax, [esp+1Ch+var_10]
.text:0042571A                 push    eax             ; char *
.text:0042571B                 call    j__atol
.text:00425720                 add     esp, 4
; Первые две цифры не должны быть нулями
.text:00425723                 test    eax, eax
.text:00425725                 jz      loc_4257D4
.text:0042572B                 add     esi, 0FFFFFFFFh
; Число больше 12? Если да, то серинийк неправильный. Выяснено первое правило
; генерации серийного номера: число из 3-й и 4-й цифр в десятичной записи
; должно быть не более 12
.text:0042572E                 cmp     esi, 0Bh
.text:00425731                 ja      loc_4257D4
; Сравнить второй блок серийника со строкой "3437". Если не равно, то ошибка.
; Вычислено второе правило генерации серийника: второй блок всегда равен "3437"
.text:00425737                 push    offset a3437    ; "3437"
.text:0042573C                 lea     ecx, [esp+20h+var_8]
.text:00425740                 push    ecx             ; int
.text:00425741                 call    sub_409A30
.text:00425746                 test    al, al
.text:00425748                 jz      loc_4257D4

И последняя проверка. Тут лучше сперва посмотреть дизассемблерный листинг, а потом пройтись в отладчике:
; Проверяется третий блок. Второй символ блока должен быть цифрой >0
.text:00425762                 movsx   edx, byte ptr [esi+1]
.text:00425766                 push    edx             ; C
.text:00425767                 call    _isdigit
.text:0042576C                 add     esp, 4
.text:0042576F                 test    eax, eax
.text:00425771                 jz      short loc_4257D4
.text:00425773                 cmp     dword ptr [esi-0Ch], 2
.text:00425777                 jge     short loc_425783
.text:00425779                 push    80070057h
.text:0042577E                 call    nullsub_1
.text:00425783
.text:00425783 loc_425783:
; Третий символ блока должен быть цифрой >0
.text:00425783                 movsx   eax, byte ptr [esi+2]
.text:00425787                 push    eax             ; C
.text:00425788                 call    _isdigit
.text:0042578D                 add     esp, 4
.text:00425790                 test    eax, eax
.text:00425792                 jz      short loc_4257D4
.text:00425794                 cmp     dword ptr [esi-0Ch], 3
.text:00425798                 jge     short loc_4257A4
.text:0042579A                 push    80070057h
.text:0042579F                 call    nullsub_1
.text:004257A4
.text:004257A4 loc_4257A4:
; Четвертый символ блока должен быть цифрой >0
.text:004257A4                 movsx   ecx, byte ptr [esi+3]
.text:004257A8                 push    ecx             ; C
.text:004257A9                 call    _isdigit
.text:004257AE                 add     esp, 4
.text:004257B1                 test    eax, eax
.text:004257B3                 jz      short loc_4257D4
.text:004257B5                 cmp     [esi-0Ch], ebx
.text:004257B8                 jge     short loc_4257C4
.text:004257BA                 push    80070057h
.text:004257BF                 call    nullsub_1
.text:004257C4
.text:004257C4 loc_4257C4:
; Первый символ блока должен быть буквой латинского алфавита.
.text:004257C4                 movsx   edx, byte ptr [esi]
.text:004257C7                 push    edx             ; C
.text:004257C8                 call    _isalpha
.text:004257CD                 add     esp, 4
.text:004257D0                 test    eax, eax
.text:004257D2                 jnz     short loc_4257D9

Теперь у нас есть все данные для генерации серийного номера. Первый блок: первые две цифры любые, но больше нуля, вторые две цифры - двузначное число не больше 12. Второй блок - всегда 3437. Третий блок: первый символ - буква латинского алфавита, потом три любые цифры больше нуля. Блоки разделены символом "-". Под эту схему подходит, например, такой серийник: 1101-3437-A111. Вводим его, причем имя и адрес E-mail могут быть любыми, и программа благодарит нас за успешную регистрацию.


Программа успешно зарегистрирована

Рабочий кейген для Becky! Internet Mail можете написать сами, после разбора алгоритма ничего сложного тут уже нет.
Теги:
защита, взлом
Добавлено: 21 Сентября 2013 01:15:37 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...