Исследование защиты скринсейвера Zen Light
Скриншот скринсейвера Zen Light
Сегодня будем разбирать защиту скринсейвера Zen Light. Эта заставка показывает очень красивые световые эффекты вроде северного сияния. Цена программы невелика, но это точно не наш метод.
Забираем с офсайта дистрибутив, устанавливаем, открываем настройки экрана. При попытке зарегистрировать скринсейвер левыми данными, появляется вот такое сообщение:
Сообщение о неправильной регистрации
Попытаемся найти какие-нибудь характерные строки. Увы, несмотря на то, что основной файл ничем не упакован, в файле этих строчек нет. Посмотрим ресурсы.
Зашифрованные строки в ресурсах
Вечер перестает быть томным. В строковых ресурсах обнаруживается большое количество неких данных, которые выглядят как зашифрованный текст. Логично предположить, что в коде должна быть процедура для их расшифровки. Отправляем файл в дизассемблер, затем поищем какие-нибудь манипуляции со строкой из ресурсов с наиболее приметным индексом 2018 или 7E2h в шестнадцатеричной системе счисления. Обнаружится следующий код:
Code (Assembler) : Убрать нумерацию
- .text:00408257 push eax ; hWnd
- .text:00408258 call ds:EnableWindow
- ; Индекс строки в ресурсах
- .text:0040825E push 7E2h
- ; Загрузить и расшифровать строку
- .text:00408263 call sub_466D10
- .text:00408268 add esp, 4
- .text:0040826B push eax ; lpString
- .text:0040826C push 437h ; nIDDlgItem
- .text:00408271 mov eax, [ebp+hWnd]
- .text:00408274 push eax ; hDlg
- ; Установить расшифрованный текст куда-то в диалоговое окно
- .text:00408275 call ds:SetDlgItemTextW
- .text:0040827B mov byte_4E9478, 1
Code (Assembler) : Убрать нумерацию
- .text:00466D10 sub_466D10 proc near
- .text:00466D10 push ebp
- .text:00466D11 mov ebp, esp
- .text:00466D13 pop ebp
- .text:00466D14 jmp sub_465ED0
- .text:00466D14 sub_466D10 endp
- ...
- ...
- .text:00465ED0 sub_465ED0 proc near
- .text:00465ED0 push ebp
- .text:00465ED1 mov ebp, esp
- .text:00465ED3 push ecx
- .text:00465ED4 movsx eax, word_4EDFE0
- .text:00465EDB inc eax
- .text:00465EDC and eax, 80000001h
- .text:00465EE1 push esi
- .text:00465EE2 jns short loc_465EE9
- .text:00465EE4 dec eax
- .text:00465EE5 or eax, 0FFFFFFFEh
- .text:00465EE8 inc eax
- .text:00465EE9 loc_465EE9:
- .text:00465EE9 mov word_4EDFE0, ax
- .text:00465EEF cwde
- .text:00465EF0 sub eax, 0
- .text:00465EF3 jz short loc_465F1B
- .text:00465EF5 dec eax
- .text:00465EF6 jnz short loc_465F3E
- .text:00465EF8 movzx ecx, [ebp+arg_0]
- .text:00465EFC mov edx, hInstance
- .text:00465F02 push 0FFh ; cchBufferMax
- .text:00465F07 push offset Buffer ; lpBuffer
- .text:00465F0C push ecx ; uID
- .text:00465F0D push edx ; hInstance
- .text:00465F0E call ds:LoadStringW
- .text:00465F14 mov esi, offset Buffer
- .text:00465F19 jmp short loc_465F41
- .text:00465F1B ; -------------------------------
- .text:00465F1B loc_465F1B:
- .text:00465F1B movzx eax, [ebp+arg_0]
- .text:00465F1F mov ecx, hInstance
- .text:00465F25 push 0FFh ; cchBufferMax
- .text:00465F2A push offset word_4EDBE0 ; lpBuffer
- .text:00465F2F push eax ; uID
- .text:00465F30 push ecx ; hInstance
- .text:00465F31 call ds:LoadStringW
- .text:00465F37 mov esi, offset word_4EDBE0
- .text:00465F3C jmp short loc_465F41
- .text:00465F3E ; -------------------------------
- .text:00465F3E loc_465F3E:
- .text:00465F3E mov esi, [ebp+var_4]
- .text:00465F41 loc_465F41:
- ; Проверка, начинается ли строка с символа "!"
- .text:00465F41 cmp word ptr [esi], 21h
- ; Если нет, то строка считается обычной и расшифровывать ее не надо
- .text:00465F45 jnz short loc_465F9E
- .text:00465F47 push esi ; lpString
- .text:00465F48 call ds:lstrlenW
- .text:00465F4E cmp word ptr [esi+eax*2-2], 7Eh
- .text:00465F54 jnz short loc_465F9E
- .text:00465F56 push edi
- .text:00465F57 lea edi, [esi+2]
- .text:00465F5A mov ecx, edi
- .text:00465F5C cmp word ptr [ecx], 7Eh
- .text:00465F60 jz short loc_465F91
- .text:00465F62 xor esi, esi
- .text:00465F64 push ebx
- ; Цикл расшифровки строки
- .text:00465F65 loc_465F65:
- .text:00465F65 movzx eax, word ptr [ecx]
- .text:00465F68 cmp eax, 30h
- .text:00465F6B jb short loc_465F84
- .text:00465F6D cmp eax, 7Ah
- .text:00465F70 ja short loc_465F84
- .text:00465F72 lea eax, [eax+esi+59h]
- .text:00465F76 cdq
- .text:00465F77 mov ebx, 4Bh
- .text:00465F7C idiv ebx
- .text:00465F7E add edx, 30h
- .text:00465F81 mov [ecx], dx
- .text:00465F84 loc_465F84:
- .text:00465F84 add ecx, 2
- .text:00465F87 add esi, 3
- .text:00465F8A cmp word ptr [ecx], 7Eh
- .text:00465F8E jnz short loc_465F65
- .text:00465F90 pop ebx
- .text:00465F91 loc_465F91:
- .text:00465F91 xor eax, eax
- .text:00465F93 mov [ecx], ax
- ; Вернуть в EAX указатель на расшифрованную строку
- .text:00465F96 mov eax, edi
- .text:00465F98 pop edi
- .text:00465F99 pop esi
- .text:00465F9A mov esp, ebp
- .text:00465F9C pop ebp
- .text:00465F9D retn
- .text:00465F9E ; -------------------------------
- .text:00465F9E loc_465F9E:
- ; Вернуть в EAX указатель на обычную строку
- .text:00465F9E mov eax, esi
- .text:00465FA0 pop esi
- .text:00465FA1 mov esp, ebp
- .text:00465FA3 pop ebp
- .text:00465FA4 retn
- .text:00465FA4 sub_465ED0 endp
Расшифрованная строка
Запускаем отладчик. Несколько раз точка останова сработает на строчках типа "Name", "Number", это, скорее всего, проверяется наличие строчек в реестре. Затем отладчик остановится на строке заголовка незарегистрированной версии. Пошаговой трассировкой доходим до команды RET и следующим шагом попадаем на место вызова:
Code (Assembler) : Убрать нумерацию
- ; Проверить наличие регистрации в реестре
- .text:004608E1 call sub_43C2E0
- .text:004608E6 add esp, 0Ch
- .text:004608E9 test eax, eax
- .text:004608EB jz short loc_460909
- .text:004608ED lea eax, [ebp+var_808]
- .text:004608F3 push eax
- .text:004608F4 lea ecx, [ebp+var_1010]
- .text:004608FA push ecx
- ; Проверить правильность регистрации
- .text:004608FB call sub_43D1F0
- .text:00460900 add esp, 8
- ; Признак зарегистрированности
- .text:00460903 mov [ebp+var_80C], eax
- .text:00460909 loc_460909:
- ; Программа зарегистрирована?
- .text:00460909 cmp [ebp+var_80C], 0
- .text:00460910 jnz short loc_460930
- ; Нет, загрузить в заголовок строку "Unlicensed Copy"
- .text:00460912 push 8Ah
- .text:00460917 call sub_466D10
- .text:0046091C add esp, 4
- .text:0046091F push eax
Сообщение о корректной регистрации
Теперь скринсейвер принимает любые регистрационные данные и сообщает, что теперь это полная версия. Текст в заголовке также меняется на "лицензионный". Теперь пробуем запустить собственно сам скринсейвер.
Сообщение взломанной версии
Через несколько секунд на экране появляется летающая надпись о том, что
Code (Assembler) : Убрать нумерацию
- .text:00414030 sub_414030 proc near
- .text:00414030 push ebp
- .text:00414031 mov ebp, esp
- .text:00414033 sub esp, 9Ch
- .text:00414039 mov eax, dword_4E7FF0
- .text:0041403E xor eax, ebp
- .text:00414040 mov [ebp+var_C], eax
- .text:00414043 push esi
- .text:00414044 push edi
- .text:00414045 mov [ebp+var_98], ecx
- .text:0041404B mov [ebp+var_8C], 1
- .text:00414055 mov eax, dword_4E9838
- .text:0041405A and eax, 1
- .text:0041405D jnz short loc_414086
- .text:0041405F mov ecx, dword_4E9838
- .text:00414065 or ecx, 1
- .text:00414068 mov dword_4E9838, ecx
- .text:0041406E mov edx, [ebp+var_98]
- .text:00414074 fild dword ptr [edx+1974h]
- .text:0041407A fdiv ds:dbl_4ACF18
- .text:00414080 fstp dbl_4E9830
- .text:00414086 loc_414086:
- .text:00414086 mov ecx, 16h
- .text:0041408B mov esi, offset aWinnersNeverPi
- ; "Winners never pirate, and Pirates never"...
- .text:00414090 lea edi, [ebp+chText]
- .text:00414096 rep movsd
- .text:00414098 movsw
- .text:0041409A push offset aArial ; "Arial"
- .text:0041409F push 0 ; iPitchAndFamily
- .text:004140A1 push 0 ; iQuality
- .text:004140A3 push 20h ; iClipPrecision
- .text:004140A5 push 4 ; iOutPrecision
- .text:004140A7 push 1 ; iCharSet
- .text:004140A9 push 0 ; bStrikeOut
- .text:004140AB push 0 ; bUnderline
- .text:004140AD push 1 ; bItalic
- .text:004140AF push 2BCh ; cWeight
- .text:004140B4 push 0 ; cOrientation
- .text:004140B6 push 0 ; cEscapement
- .text:004140B8 push 0 ; cWidth
- .text:004140BA push 16h ; cHeight
- .text:004140BC call ds:CreateFontW
- .text:004140C2 mov [ebp+ho], eax
Code (Assembler) : Убрать нумерацию
- .text:00425325 mov eax, [ebp+var_C]
- ; Проверить какой-то флаг
- .text:00425328 cmp dword ptr [eax+1848h], 2
- ; Строку рисовать не надо
- .text:0042532F jnz short loc_42533F
- .text:00425331 push 1 ; int
- .text:00425333 mov ecx, [ebp+hdc]
- .text:00425336 push ecx ; hdc
- .text:00425337 mov ecx, [ebp+var_C]
- ; Нарисовать строку
- .text:0042533A call sub_414030
Code (Assembler) : Убрать нумерацию
- .text:0042C360 sub_42C360
- .text:0042C360 push ebp
- .text:0042C361 mov ebp, esp
- .text:0042C363 push ecx
- .text:0042C364 mov [ebp+var_4], ecx
- .text:0042C367 mov eax, [ebp+var_4]
- .text:0042C36A mov dword ptr [eax+1848h], 2
- .text:0042C374 mov esp, ebp
- .text:0042C376 pop ebp
- .text:0042C377 retn
- .text:0042C377 sub_42C360 endp
Code (Assembler) : Убрать нумерацию
- .text:0043ED37 cmp [ebp+var_BA0], 464h
- .text:0043ED41 jz short loc_43EDBA
- .text:0043ED43 cmp [ebp+var_BA0], 465h
- .text:0043ED4D jz loc_43F6A8
- .text:0043ED53 jmp loc_43F91F
- .text:0043ED58 ; -------------------------------
- .text:0043ED58 loc_43ED58:
- .text:0043ED58 cmp [ebp+wParam], 2
- .text:0043ED5C jnz short loc_43ED90
- .text:0043ED5E push 2 ; uIDEvent
- .text:0043ED60 mov ecx, [ebp+hWnd]
- .text:0043ED63 push ecx ; hWnd
- .text:0043ED64 call ds:KillTimer
- .text:0043ED6A push 0 ; lpTimerFunc
- .text:0043ED6C push 3A98h ; uElapse
- .text:0043ED71 push 3 ; nIDEvent
- .text:0043ED73 mov edx, [ebp+hWnd]
- .text:0043ED76 push edx ; hWnd
- .text:0043ED77 call ds:SetTimer
- .text:0043ED7D push 0 ; lParam
- .text:0043ED7F push 64h ; wParam
- .text:0043ED81 push 464h ; Msg
- .text:0043ED86 mov eax, [ebp+hWnd]
- .text:0043ED89 push eax ; hWnd
- .text:0043ED8A call ds:PostMessageW
- .text:0043ED90 loc_43ED90:
- .text:0043ED90 cmp [ebp+wParam], 3
- .text:0043ED94 jnz short loc_43EDB5
- .text:0043ED96 push 3 ; uIDEvent
- .text:0043ED98 mov ecx, [ebp+hWnd]
- .text:0043ED9B push ecx ; hWnd
- .text:0043ED9C call ds:KillTimer
- .text:0043EDA2 push 0 ; lParam
- .text:0043EDA4 push 65h ; wParam
- .text:0043EDA6 push 465h ; Msg
- .text:0043EDAB mov edx, [ebp+hWnd]
- .text:0043EDAE push edx ; hWnd
- .text:0043EDAF call ds:PostMessageW
- .text:0043EDB5 loc_43EDB5:
- .text:0043EDB5 jmp loc_43F91F ; default
- .text:0043EDBA ; -------------------------------
- .text:0043EDBA loc_43EDBA:
- .text:0043EDBA mov ecx, dword_4ECCE8
- ; Взвести "пиратский" флаг
- .text:0043EDC0 call sub_42C360
- .text:0043EDC5 jmp loc_43F91F ; default
Сообщение взломанной версии
Уведомление о пиратстве больше не появляется, скринсейвер не вылетает. Однако, через некоторое время поверх цветных узоров начинают появляться предложения метнуться в кассу или вовсе какие-то бредовые надписи типа этой. Эти строки зашифрованы, отлавливаются они под отладчиком точно так же, как описано выше.
Code (Assembler) : Убрать нумерацию
- .text:0040F137 mov ecx, [ebp+var_1C4]
- ; Загрузить флаг
- .text:0040F13D mov edx, [ecx+9E28h]
- .text:0040F143 mov [ebp+var_1C8], edx
- .text:0040F149 cmp [ebp+var_1C8], 10h ; switch 17 cases
- .text:0040F150 ja loc_40F741
- .text:0040F156 mov eax, [ebp+var_1C8]
- .text:0040F15C movzx ecx, ds:byte_40F774[eax]
- .text:0040F163 jmp ds:off_40F760[ecx*4] ; switch jump
- .text:0040F16A loc_40F16A:
- .text:0040F16A mov edx, [ebp+var_1C4]
- ; Загрузить случайное сообщение
- .text:0040F170 mov eax, [edx+9E28h]
- .text:0040F176 add eax, 6Ah
- .text:0040F179 push eax
- ; Расшифровать строку
- .text:0040F17A call sub_466D10
- .text:0040F17F add esp, 4
- .text:0040F182 mov [ebp+var_158], eax
- .text:0040F188 mov [ebp+var_154], 0
Code (Assembler) : Убрать нумерацию
- .text:0040F809 fstp [ebp+var_C]
- .text:0040F80C mov [ebp+var_8], 10h
- ; Проверить аргумент
- .text:0040F813 cmp [ebp+arg_8], 1
- .text:0040F817 jnz loc_40F904
- .text:0040F81D fld ds:flt_4AB890
- .text:0040F823 fstp [ebp+var_C]
- .text:0040F826 mov [ebp+var_8], 0Eh
- ; Загрузить строку и вывести ее на экран
- .text:0040F82D push 7Bh
- .text:0040F82F call sub_466D10
- .text:0040F834 add esp, 4
- .text:0040F837 push eax
- .text:0040F838 push 40h
- .text:0040F83A lea eax, [ebp+String]
- .text:0040F840 push eax
- .text:0040F841 call sub_401CC0
- .text:0040F846 mov ecx, [ebp+var_2FC]
- .text:0040F84C mov edx, [ecx+9F34h]
- .text:0040F852 push edx ; h
- .text:0040F853 mov eax, [ebp+hdc]
- .text:0040F856 push eax ; hdc
- .text:0040F857 call ds:SelectObject
- .text:0040F85D mov [ebp+h], eax
Скринсейвер успешно "зарегистрирован"
Все, больше никаких ограничений нет, никаких надписей не появляется. Цель достигнута, счет 1:0, пираты снова победили. Спасибо автору за красивый скринсейвер и за грамотный подход к защите, получилась очень неплохая разминка для мозгов.
Просмотров: 1328 | Комментариев: 7
Метки: исследование защиты
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(11.06.2020 в 12:15):
Был тут разбор какой-то защиты, там использовалась api CheckSumMappedFile. Еще на TeamViewer встречал проверку целостности по сертификату, там было сделано через api WinVerifyTrust.
0101
(11.06.2020 в 07:12):
pawel97, понятно. Четыре байта нашёл, что за сумма и как "вручную" её подсчитать/исправить - нет. Просто есть одна програмка, которая, похоже, тоже считает CheckSum.. Я думал, может, api характерные есть, по которым искать проверку целостности файла..
pawel97
(10.06.2020 в 23:57):
0101, использовал conditional breakpoints в оле, сначала на оконную процедуру - отловил сообщение wm_timer с аргументом 2 - стало быть это id таймера. Потом на SetTimer с подсмотренным id 2, потом в листинге вышел на инициализацию флагов, из-за которых таймер взводится, и стал разбираться. Да, ещё до дизассемблеров случайно заметил, что к файлу оверлеем дописано 4 байта, подозрительно однако, небось чексумма какая. А разбор подтвердил.
ManHunter
(10.06.2020 в 22:49):
Там чексум берется не из заголовка, а считается самостоятельно.
0101
(10.06.2020 в 21:49):
pawel97, расскажите, как нашли, что в этом месте идёт проверка целостности файла?
Что именно и где подглядываем? При патчиге CheckSum не меняется же..
п.с. есть похожий скринсервер "Gold Lace"
Что именно и где подглядываем? При патчиге CheckSum не меняется же..
п.с. есть похожий скринсервер "Gold Lace"
ManHunter
(10.06.2020 в 11:52):
pawel97, 2:0 в пользу пиратов :)
pawel97
(10.06.2020 в 02:15):
Вся антипиратка из-за проверки целостности файла. Некая контрольная сумма считается и далее с ней происходят всякие колдунства, на основе этого ставятся флаги, взводятся таймеры...
Просто подглядываем в непатченном файле итоговое значение и хардкодим в патченном.
00460D4A |. 8B8D 68FFFFFF mov ecx,ss:[ebp-98]
00460D50 |. 51 push ecx
00460D51 |. 8D8D 74FFFFFF lea ecx,[ebp-8C]
00460D57 |. E8 E4AAFDFF call 0043B840
Первые две команды меняем на push 0000AEC7 и 2 нопа.
Просто подглядываем в непатченном файле итоговое значение и хардкодим в патченном.
00460D4A |. 8B8D 68FFFFFF mov ecx,ss:[ebp-98]
00460D50 |. 51 push ecx
00460D51 |. 8D8D 74FFFFFF lea ecx,[ebp-8C]
00460D57 |. E8 E4AAFDFF call 0043B840
Первые две команды меняем на push 0000AEC7 и 2 нопа.
Добавить комментарий
Заполните форму для добавления комментария