
Скриншот скринсейвера Incredible Mars 3D
Под сегодняшнюю принудительную раздачу халявы у нас попадает скринсейвер
Incredible Mars 3D. На экране крутится планета Марс, причем как в ее нынешнем высушенном состоянии, так и ее гипотетическая версия, когда планета была покрыта водой и всякой зеленью. Не самый великий шедевр скринсейверостроения, конечно, но в качестве подопытного вполне сгодится.
Скачиваем
дистрибутив, устанавливаем. Основной файл скринсейвера
Incredible Mars 3D.scr устанавливается в папку с Windows, поэтому искать его надо именно там. Файл ничем не упакован, поэтому сразу отправляем его в дизассемблер. Дальше открываем системные настройки скринсейверов, выбираем Incredible Mars 3D и открываем его свойства.

Настройки скринсейвера и окно регистрации
На ввод неправильного серийного номера программа реагирует сообщением "The key is incorrect", значит надо поискать его в файле:

Нехорошая строка найдена
Строчка найдена, и там же рядом, по всей видимости, строка сообщения об успешной регистрации. Теперь поищем в дизассемблере ссылки на эти строчки и условия, при которых они появляются.
...
.text:00422E48 mov eax, ds:dword_44788C
.text:00422E4D mov [esp+1ECh+var_1DC], 0
.text:00422E55 mov [esp+1ECh+var_1E0], offset sub_422110
.text:00422E5D mov [esp+1ECh+var_1E4], ebp
.text:00422E61 mov [esp+1ECh+var_1E8], 68h
.text:00422E69 mov [esp+1ECh+var_1EC], eax
.text:00422E6C mov ebx, 1
.text:00422E71 call DialogBoxParamA
.text:00422E76 sub esp, 14h
.text:00422E79 test eax, eax
.text:00422E7B jz loc_422248
; Если байт по адресу 430158 равен нулю, то введенный серийник правильный
.text:00422E81 cmp byte_430158, 0
.text:00422E88 jz loc_422FE2
; Иначе вывести сообщение о неправильном серийном номере
.text:00422E8E mov [esp+1ECh+var_1E0], 10h
.text:00422E96 mov [esp+1ECh+var_1E4], offset aError
; "Error"
.text:00422E9E mov [esp+1ECh+var_1E8], offset aTheKeyIsIncorr
; "The key is incorrect."
.text:00422EA6 mov [esp+1ECh+var_1EC], ebp
.text:00422EA9 call MessageBoxA
; ---------------
; часть кода пропущена
; ---------------
.text:00422FE2 loc_422FE2:
; Сообщение об успешной регистрации
.text:00422FE2 call sub_401B40
.text:00422FE7 lea esi, [esp+1ECh+hWnd]
.text:00422FEB mov eax, ds:dword_44788C
.text:00422FF0 mov [esp+1ECh+var_1E4], esi
.text:00422FF4 mov [esp+1ECh+var_1E0], 104h
.text:00422FFC mov [esp+1ECh+var_1E8], 9C41h
.text:00423004 mov [esp+1ECh+var_1EC], eax
.text:00423007 call LoadStringA
.text:0042300C sub esp, 10h
.text:0042300F mov [esp+1ECh+var_1EC], ebp
.text:00423012 mov [esp+1ECh+var_1E0], 40h
.text:0042301A mov [esp+1ECh+var_1E4], offset aSuccess
; "Success"
.text:00423022 mov [esp+1ECh+var_1E8], offset aRegistrationCo
; "Registration completed."
.text:0042302A call MessageBoxA
...
Значит флажок условия триальности хранится в байте по адресу 0430158h. Если он равен нулю, то программа зарегистрирована, и если равен 1, то программа работает в триальном режиме. Все не как у людей, все нормальные разработчики взводят флажок зарегистрированности, а тут сбрасывают флажок триальности. Ладно, космические фантазии аффтаров нам не очень интересны, нас больше интересует где же этот флажок инициализируется и изменяется. Посмотрим в сегменте данных:
Code (Assembler) : Убрать нумерацию
.data:00430158 byte_430158 db 1
Еще более странно. Значение флажка триальности вбито прямо в данные и по умолчанию сразу же равно "незарегистрировано". Смоторим перекрестные ссылки на этот несчастный байт:

Перекрестные ссылки на байт
Все очевидно. Первые две ссылки - инициализация флажка в режим триальности и в режим зарегистрированной программы, а дальше только куча проверок. Нас интересует вторая ссылка, там флажок сбрасывается и программа переходит в зарегистрированный режим. Надо посмотреть, где и как это делается. Вся инициализация и проверка оформлены в виде отдельной процедуры. Для наглядности лучше ее пройти под отладчиком.
.text:00401920 push ebp
.text:00401921 push edi
.text:00401922 push esi
.text:00401923 push ebx
.text:00401924 sub esp, 8Ch
; Начальная инициализация - программа по умолчанию не зарегистрирована
.text:0040192A mov byte_430158, 1
; Если вообще никакого серийного номера нет, то сразу на выход
.text:00401931 cmp ds:byte_4476E0, 0
.text:00401938 jz loc_401B2E
; Перевести серийник в верхний регистр
.text:0040193E lea edi, [esp+9Ch+var_3C]
.text:00401942 mov ecx, 8
.text:00401947 xor eax, eax
.text:00401949 xor esi, esi
.text:0040194B rep stosd
.text:0040194D xor ebx, ebx
.text:0040194F mov edi, ds:__mb_cur_max
.text:00401955 mov ebp, ds:_pctype
.text:0040195B jmp short loc_401982
.text:0040195B ; -----------------------------------
.text:0040195D align 10h
.text:00401960 loc_401960:
.text:00401960 mov edx, [ebp+0]
.text:00401963 movzx eax, word ptr [edx+eax*2]
.text:00401967 and eax, 80h
.text:0040196C loc_40196C:
.text:0040196C test eax, eax
.text:0040196E jz short loc_40197C
.text:00401970 movzx eax, ds:byte_4476E0[ebx]
.text:00401977 mov [esp+esi+9Ch+var_3C], al
.text:0040197B inc esi
.text:0040197C loc_40197C:
.text:0040197C inc ebx
.text:0040197D cmp ebx, 20h
.text:00401980 jz short loc_4019A0
.text:00401982 loc_401982:
.text:00401982 movsx eax, ds:byte_4476E0[ebx]
.text:00401989 cmp dword ptr [edi], 1
.text:0040198C jz short loc_401960
.text:0040198E mov [esp+9Ch+var_98], 80h
.text:00401996 mov [esp+9Ch+var_9C], eax
.text:00401999 call _isctype
.text:0040199E jmp short loc_40196C
.text:004019A0 ; --------------------------------------------
.text:004019A0 loc_4019A0:
; А вот тут начинаются манипуляции с серийным номером
.text:004019A0 lea esi, [esp+9Ch+var_7C]
.text:004019A4 xor eax, eax
.text:004019A6 movzx edx, [esp+9Ch+var_3C]
.text:004019AB lea edi, [esp+9Ch+var_5C]
.text:004019AF mov ecx, 8
.text:004019B4 rep stosd
.text:004019B6 mov [esp+9Ch+var_54], dl
.text:004019BA lea edi, [esp+9Ch+var_7C]
.text:004019BE movzx edx, [esp+9Ch+var_3B]
.text:004019C3 mov cl, 8
.text:004019C5 mov [esp+9Ch+var_55], dl
.text:004019C9 movzx edx, [esp+9Ch+var_3A]
.text:004019CE mov [esp+9Ch+var_53], dl
.text:004019D2 movzx edx, [esp+9Ch+var_39]
.text:004019D7 mov [esp+9Ch+var_56], dl
.text:004019DB movzx edx, [esp+9Ch+var_38]
.text:004019E0 mov [esp+9Ch+var_52], dl
.text:004019E4 movzx edx, [esp+9Ch+var_37]
.text:004019E9 mov [esp+9Ch+var_57], dl
.text:004019ED movzx edx, [esp+9Ch+var_36]
.text:004019F2 mov [esp+9Ch+var_51], dl
.text:004019F6 movzx edx, [esp+9Ch+var_35]
.text:004019FB mov [esp+9Ch+var_58], dl
.text:004019FF movzx edx, [esp+9Ch+var_34]
.text:00401A04 mov [esp+9Ch+var_50], dl
.text:00401A08 movzx edx, [esp+9Ch+var_33]
.text:00401A0D mov [esp+9Ch+var_59], dl
.text:00401A11 movzx edx, [esp+9Ch+var_32]
.text:00401A16 mov [esp+9Ch+var_4F], dl
.text:00401A1A movzx edx, [esp+9Ch+var_31]
.text:00401A1F mov [esp+9Ch+var_5A], dl
.text:00401A23 movzx edx, [esp+9Ch+var_30]
.text:00401A28 mov [esp+9Ch+var_4E], dl
.text:00401A2C movzx edx, [esp+9Ch+var_2F]
.text:00401A31 mov [esp+9Ch+var_5B], dl
.text:00401A35 movzx edx, [esp+9Ch+var_2E]
.text:00401A3A mov [esp+9Ch+var_4D], dl
.text:00401A3E movzx edx, [esp+9Ch+var_2D]
.text:00401A43 mov [esp+9Ch+var_5C], dl
.text:00401A47 rep stosd
.text:00401A49 lea eax, [esp+9Ch+var_5C]
.text:00401A4D mov [esp+9Ch+var_9C], esi
.text:00401A50 mov edi, 0A3F3h
.text:00401A55 mov [esp+9Ch+var_94], 8
.text:00401A5D mov [esp+9Ch+var_98], eax
.text:00401A61 call strncpy
.text:00401A66 mov [esp+9Ch+var_9C], esi
.text:00401A69 mov [esp+9Ch+var_94], 10h
.text:00401A71 mov [esp+9Ch+var_98], 0
.text:00401A79 call strtoul
.text:00401A7E mov [esp+9Ch+var_9C], esi
.text:00401A81 mov ebx, eax
.text:00401A83 mov [esp+9Ch+var_94], 8
; Собрать 8 нечетных символов серийника в обратном порядке, преобразовать
; их в HEX-число и поXORить результат с 05AEB7C3h
.text:00401A8B xor ebx, 5AEB7C3h
.text:00401A91 lea eax, [esp+9Ch+var_54]
.text:00401A95 mov [esp+9Ch+var_98], eax
.text:00401A99 call strncpy
.text:00401A9E mov [esp+9Ch+var_9C], esi
.text:00401AA1 mov [esp+9Ch+var_94], 10h
.text:00401AA9 mov [esp+9Ch+var_98], 0
.text:00401AB1 call strtoul
.text:00401AB6 mov [esp+9Ch+var_94], 0A3F3h
.text:00401ABE mov esi, eax
.text:00401AC0 mov [esp+9Ch+var_90], 0
; Собрать 8 четных символов серийника в HEX-число и поXORить полученный
; результат с 14AB7B4Ah
.text:00401AC8 xor esi, 14AB7B4Ah
; Теперь в ESI проверочное значение
.text:00401ACE mov [esp+9Ch+var_9C], ebx
.text:00401AD1 mov [esp+9Ch+var_98], 0
.text:00401AD9 call sub_42EB10
.text:00401ADE mov [esp+9Ch+var_94], 0A3F3h
.text:00401AE6 imul ecx, edx, 0A3F3h
.text:00401AEC mov [esp+9Ch+var_90], 0
.text:00401AF4 mul edi
.text:00401AF6 mov [esp+9Ch+var_9C], ebx
.text:00401AF9 add edx, ecx
.text:00401AFB mov [esp+9Ch+var_84], eax
.text:00401AFF mov [esp+9Ch+var_80], edx
.text:00401B03 mov [esp+9Ch+var_98], 0
.text:00401B0B call sub_42EC34
.text:00401B10 add eax, [esp+9Ch+var_84]
.text:00401B14 mov edi, eax
.text:00401B16 add edi, 7B1h
.text:00401B1C mul edi
.text:00401B1E add eax, 7CBh
; Сравнить вычисленное значение EAX с сохраненным ESI
.text:00401B23 cmp esi, eax
.text:00401B25 jnz short loc_401B2E
; Регистрационный номер правильный
.text:00401B27 mov byte_430158, 0
.text:00401B2E loc_401B2E:
...
В коде я прокомментировал ключевые моменты, но повторю их словами. Из серийного номера выбираются нечетные символы в обратном порядке, начиная с 15-го. То есть длина серийника должна быть 16 символов. Затем из него же выбираются все четные символы по порядку, начиная с нулевого. Для наглядности из введенного серийника 192A3B4C5D6E7F80 получится две строки "0FEDCBA" и "12345678". Дальше эти строки переводятся в шестнадцатеричные числа, первое XORится с константой 05AEB7C3h, а второе с константой 14AB7B4Ah. Полученный из второго преобразования результат является проверочным значением для серийника. А на основании первого числа, путем различных преобразований, получается тестовое число. Если оно равно проверочному, то серийный номер правильный и в байт 0430158h заносится ноль.
Первый вариант решения проблемы регистрации - патч. Достаточно заменить команду инициализации mov byte_430158, 1 на mov byte_430158, 0, а затем поменять с 1 на 0 начальное значение байта в файле. После этого скринсейвер переходит в полноценный режим работы. Это быстро, эффективно, но неспортивно.

Программа успешно "зарегистрирована"
Второй вариант - серийный номер. Разбирать все алгоритмы преобразований мне лениво, а вот подобрать какой-нибудь правильный серийный номер вполне реально. Вспомним алгоритм проверки. На последнем этапе у нас есть тестовое число, которое должно быть равно проверочному.
Тестовое число = (четные символы) XOR 14AB7B4Ah
Уравнение младших классов. Тестовое число мы узнаем при проверке, команда XOR обратимая и при ее применении с тем же параметром 14AB7B4Ah мы получим некое число, которое является четными символами серийного номера. То есть подбор сводится к тому, чтобы на основании введенных нечетных символов в конце проверки узнать правильные четные символы. Берем серийник "192A3B4C5D6E7F80", под отладчиком пытаемся зарегистрировать им программу. Тестовое число в регистре EAX получается "8BDBD25D":

Сравнение тестового и контрольного чисел
Осталась простейшая двоичная математика: 8BDBD25D xor 14AB7B4A = 9F70A917, это и есть искомые символы серийного номера, которые должны быть на четных позициях. Записываем их в серийный номер, получается строка "99FA7B0CAD9E1F70". Попробуем зарегистрировать скринсейвер с его помощью.

Программа успешно зарегистрирована
Вот теперь все красиво. Серийный номер принят, скринсейвер работает в полном режиме. Можете бесплатно приобщаться к красотам космоса.