Исследование защиты программы Cool MP3 Splitter
Скриншот программы Cool MP3 Splitter
Программа Cool MP3 Splitter предназначена для вырезания фрагментов нужной длины из MP3-файлов. Особой практической ценности не представляет, потому что есть более мощные и бесплатные аналоги, но в качестве подопытного будет интересна.
Скачиваем дистрибутив, устанавливаем, запускаем. Сразу появляется окно с предложением зарегистрировать программу. На ввод любых левых данных программа никак не реагирует, окно с регистрацией просто закрывается. Разработчики предусмотрели такой вариант и все сделали правильно. Из внешних триальных признаков только две кнопки "Reg" и "BuyNow", в About ничего интересного, в заголовке триальных надписей нет. Тоже не густо для исследования. Поищем хотя бы текст сообщения из наг-скрина. С этим проблем нет, текст лежит в открытом виде.
Триальный текст найден
Теперь выясняем условия его появления. Для этого достаточно посмотреть перекрестные ссылки в дизассемблере.
Code (Assembler) : Убрать нумерацию
- ...
- .text:004047DF add ebx, 1F0h
- .text:004047E5 mov eax, [ebx]
- .text:004047E7 mov edx, [eax]
- .text:004047E9 call dword ptr [edx+14h]
- ; Сравнивается с 0 двойное слово в памяти, на основании результата
- ; этого принимается решение о триальности
- .text:004047EC cmp dword ptr [esi+43Ch], 0
- .text:004047F3 mov ebx, eax
- ; Не равно 0, значит программа зарегистрирована
- .text:004047F5 jnz loc_404884
- .text:004047FB mov [ebp+var_40], 38h
- .text:00404801 mov edx, offset aThisIsA14DayTr
- ; " This is a 14-day trial version of Coo"...
- .text:00404806 lea eax, [ebp+var_14]
- .text:00404809 call sub_4A8660
- .text:0040480E inc [ebp+var_34]
- .text:00404811 mov [ebp+var_40], 2Ch
- .text:00404817 push 40h
- .text:00404819 mov ecx, offset aThisIsA14Day_0
- ; "This is a 14-day trial version."
- .text:0040481E cmp [ebp+var_14], 0
- .text:00404822 jz short loc_404829
- .text:00404824 mov edx, [ebp+var_14]
- .text:00404827 jmp short loc_40482E
- ...
Code (Assembler) : Убрать нумерацию
- ...
- ; Указатели на какие-то две строки
- .text:004045C6 lea edx, [ebp+var_4]
- .text:004045C9 lea eax, [ebp+var_24]
- ; Выполнить функцию сравнения строк
- .text:004045CC call @System@AnsiString@$beql$xqqrrx17
- ; System::AnsiString::operator==(System::AnsiString &)
- .text:004045D1 test al, al
- ; Если она вернула AL=0, то переход
- .text:004045D3 jz short loc_4045E2
- .text:004045D5 mov ecx, [ebp+var_58]
- ; Иначе записать 1 в двойное слово по адресу [reg+43Ch]
- .text:004045D8 mov dword ptr [ecx+43Ch], 1
- .text:004045E2 loc_4045E2:
- ...
Code (Assembler) : Убрать нумерацию
- .text:004043FC push ebp
- .text:004043FD mov ebp, esp
- .text:004043FF add esp, 0FFFFFF8Ch
- .text:00404402 push ebx
- .text:00404403 push esi
- .text:00404404 push edi
- ; Установить собственный обработчик исключений
- .text:00404405 mov [ebp+var_58], eax
- .text:00404408 mov eax, offset stru_4AC238
- .text:0040440D call @__InitExceptBlockLDTC
- .text:00404412 mov [ebp+var_44], 8
- .text:00404418 lea edi, [ebp+var_74]
- ; Это какой-то хитрый ключ реестра. Ради интереса можете потом поискать
- ; его у себя, может быть там есть еще что-нибудь интересное :)
- .text:0040441B mov esi, offset aQplaksjdhfgwoi
- ; "qplaksjdhfgwoieurytmznxbcv"
- .text:00404420 mov ecx, 6
- .text:00404425 mov edx, offset aUser ; "User"
- .text:0040442A rep movsd
- .text:0040442C movsw
- .text:0040442E movsb
- ...
- ...
- .text:00404456 mov eax, [eax+444h]
- ; Прочитать ключ реестра, в котором содержится имя пользователя
- .text:0040445C call @TRegistry@ReadString$qqrx10AnsiString
- ; TRegistry::ReadString(AnsiString)
- .text:00404461 lea edx, [ebp+var_10]
- .text:00404464 mov eax, [ebp+var_58]
- .text:00404467 add eax, 468h
- .text:0040446C call sub_4A87E8
- .text:00404471 dec [ebp+var_38]
- .text:00404474 lea eax, [ebp+var_10]
- .text:00404477 mov edx, 2
- .text:0040447C call sub_4A87B8
- .text:00404481 dec [ebp+var_38]
- .text:00404484 lea eax, [ebp+var_C]
- .text:00404487 mov edx, 2
- .text:0040448C call sub_4A87B8
- .text:00404491 mov [ebp+var_44], 2Ch
- .text:00404497 mov edx, offset aCode ; "Code"
- .text:0040449C lea eax, [ebp+var_14]
- .text:0040449F call sub_4A8660
- .text:004044A4 inc [ebp+var_38]
- .text:004044A7 mov edx, [eax]
- .text:004044A9 xor eax, eax
- .text:004044AB mov [ebp+var_4], eax
- .text:004044AE lea ecx, [ebp+var_4]
- .text:004044B1 inc [ebp+var_38]
- .text:004044B4 mov eax, [ebp+var_58]
- .text:004044B7 mov eax, [eax+444h]
- ; Прочитать ключ реестра, в котором содержится ключ регистрации
- .text:004044BD call @TRegistry@ReadString$qqrx10AnsiString
- ; TRegistry::ReadString(AnsiString)
- .text:004044C2 dec [ebp+var_38]
- .text:004044C5 lea eax, [ebp+var_14]
- .text:004044C8 mov edx, 2
- ...
- ...
- .text:00404509 inc [ebp+var_38]
- .text:0040450C mov eax, [ebp+lpLibFileName]
- ; Получить каталог, из которого запущена программа
- .text:0040450F call @Sysutils@ExtractFilePath$qqrx17
- ; Sysutils::ExtractFilePath(System::AnsiString)
- .text:00404514 lea ecx, [ebp+var_18]
- .text:00404517 push ecx
- ; Дописать к нему строку "key.dll"
- .text:00404518 mov edx, offset aKey_dll ; "key.dll"
- .text:0040451D lea eax, [ebp+var_1C]
- .text:00404520 call sub_4A8660
- ...
- ...
- .text:00404587 push ecx ; lpLibFileName
- ; Попытаться загрузить key.dll из папки программы. В стандартной поставке
- ; этого файла нет, видимо он высылается пользователям после покупки или
- ; его можно скачать из какой-нибудь закрытой зоны сайта. В любом случае
- ; у меня его нет.
- .text:00404588 call LoadLibraryA
- ; Пипец, а если dll нет? Никаких обработчиков ошибок нет. No comments
- .text:0040458D mov ebx, eax
- ; Получить адрес функции "myfun" из загруженной библиотеки
- .text:0040458F push offset aMyfun ; "myfun"
- .text:00404594 push ebx ; hModule
- .text:00404595 call GetProcAddress
- .text:0040459A test eax, eax
- ; Если функции "myfun" не найдено, то программа считается триальной
- .text:0040459C jz short loc_4045F8
- .text:0040459E mov [ebp+var_44], 5Ch
- .text:004045A4 lea edx, [ebp+var_74]
- .text:004045A7 push edx
- .text:004045A8 mov ecx, [ebp+var_58]
- .text:004045AB mov edx, [ecx+468h]
- .text:004045B1 xor ecx, ecx
- .text:004045B3 push edx
- .text:004045B4 lea edx, [ebp+var_24]
- .text:004045B7 mov [ebp+var_24], ecx
- .text:004045BA push edx
- .text:004045BB inc [ebp+var_38]
- ; Вызвать функцию "myfun", передав ей в параметрах имя из реестра
- .text:004045BE call eax
- .text:004045C0 mov [ebp+var_44], 50h
- ; Сравнить полученную строчку с ключом
- .text:004045C6 lea edx, [ebp+var_4]
- .text:004045C9 lea eax, [ebp+var_24]
- .text:004045CC call @System@AnsiString@$beql$xqqrrx17
- ; System::AnsiString::operator==(System::AnsiString &)
- .text:004045D1 test al, al
- .text:004045D3 jz short loc_4045E2
- ; Если они совпали, то программа зарегистрирована
- .text:004045D5 mov ecx, [ebp+var_58]
- .text:004045D8 mov dword ptr [ecx+43Ch], 1
- .text:004045E2 loc_4045E2:
- .text:004045E2 dec [ebp+var_38]
- .text:004045E5 lea eax, [ebp+var_24]
- .text:004045E8 mov edx, 2
- .text:004045ED call sub_4A87B8
- .text:004045F2 mov [ebp+var_44], 20h
- .text:004045F8 loc_4045F8:
- .text:004045F8 push ebx ; hLibModule
- ; Выгрузить библиотеку "key.dll"
- .text:004045F9 call FreeLibrary
- .text:004045FE dec [ebp+var_38]
- .text:00404601 lea eax, [ebp+lpLibFileName]
- .text:00404604 mov edx, 2
- .text:00404609 call sub_4A87B8
- ...
- ...
- .text:00404673 mov edx, [ebp+var_58]
- ; Программа зарегистрирована?
- .text:00404676 cmp dword ptr [edx+43Ch], 1
- .text:0040467D jnz short loc_4046EC
- ; Убрать с формы кнопки "Reg" и "BuyNow"
- .text:0040467F mov eax, [ebp+var_58]
- .text:00404682 xor edx, edx
- .text:00404684 mov eax, [eax+388h]
- .text:0040468A call @Controls@TControl@SetVisible$qqro
- ; Controls::TControl::SetVisible(bool)
- .text:0040468F mov ecx, [ebp+var_58]
- .text:00404692 xor edx, edx
- .text:00404694 mov eax, [ecx+384h]
- .text:0040469A call @Controls@TControl@SetVisible$qqro
- ; Controls::TControl::SetVisible(bool)
- .text:0040469F mov [ebp+var_44], 80h
- .text:004046A5 xor ecx, ecx
- ; Записать куда-то строку, что программа зарегистрирована на кого-то
- .text:004046A7 mov eax, offset aThisProductIsL
- ; "This Product Is Licensed To : "
- .text:004046AC mov [ebp+var_30], ecx
- .text:004046AF lea ecx, [ebp+var_30]
- .text:004046B2 inc [ebp+var_38]
- .text:004046B5 mov edx, [ebp+var_58]
- ...
- ...
- ; Здесь мы окажемся, если программа не зарегистрирована
- .text:004046EC loc_4046EC:
- .text:004046EC mov eax, [ebp+var_58]
- .text:004046EF call sub_4058E8
- ...
Code (Assembler) : Убрать нумерацию
- .text:00404676 cmp dword ptr [edx+43Ch], 1
- .text:0040467D jnz short loc_4046EC
Code (Assembler) : Убрать нумерацию
- ; Записать в [reg+43Ch] единицу
- .text:00404676 inc dword ptr [edx+43Ch]
- .text:0040467C nop
- .text:0040467D nop
- .text:0040467E nop
Программа успешно "зарегистрирована"
Для спортивного интереса можно посмотреть еще и триальный алгоритм программы. Он вызывается по адресу 004046EF, вызываемая процедура расписана и прокомментирована ниже. Для удобства я также сократил некоторые фрагменты:
Code (Assembler) : Убрать нумерацию
- .text:004058E8 push ebp
- .text:004058E9 mov ebp, esp
- .text:004058EB add esp, 0FFFFFE54h
- .text:004058F1 mov eax, offset stru_4AC9D0
- .text:004058F6 push ebx
- .text:004058F7 push esi
- .text:004058F8 push edi
- .text:004058F9 call @__InitExceptBlockLDTC
- .text:004058FE push 104h ; uSize
- .text:00405903 lea edx, [ebp+Buffer]
- .text:00405909 push edx ; lpBuffer
- ; Получить папку, в которую установлена WINDOWS
- .text:0040590A call GetWindowsDirectoryA
- .text:0040590F mov esi, eax
- .text:00405911 lea edi, [esi+1]
- ...
- ...
- .text:00405944 call sub_4A8660
- .text:00405949 inc [ebp+var_60]
- ; Дописать к ней строку "\\Syskernel12.dll"
- .text:0040594C mov edx, offset aSyskernel12_dl
- ; "\\Syskernel12.dll"
- .text:00405951 mov [ebp+var_6C], 14h
- .text:00405957 mov [ebp+var_6C], 20h
- .text:0040595D lea eax, [ebp+var_C]
- ...
- ...
- ; Получить текущую дату и время
- .text:004059BD call @System@TDateTime@CurrentDate$qqrv
- ; System::TDateTime::CurrentDate(void)
- .text:004059C2 fstp [ebp+var_88]
- .text:004059C8 mov [ebp+var_6C], 2Ch
- .text:004059CE xor eax, eax
- ...
- ...
- ; Пропущена куча кода по определению размера файла и чтению его в память
- ...
- ...
- .text:00405A4C mov [ebp+var_6C], 44h
- .text:00405A52 lea eax, [ebp+var_14]
- .text:00405A55 mov edx, ebx
- .text:00405A57 call sub_4A8660
- .text:00405A5C inc [ebp+var_60]
- .text:00405A5F mov edx, offset aEnd ; "-----END-----"
- .text:00405A64 mov [ebp+var_6C], 50h
- .text:00405A6A mov [ebp+var_6C], 5Ch
- .text:00405A70 lea eax, [ebp+var_18]
- .text:00405A73 call sub_4A8660
- .text:00405A78 inc [ebp+var_60]
- ; Сравнить прочитанные данные со строкой "-----END-----"
- .text:00405A7B lea edx, [ebp+var_18]
- .text:00405A7E lea eax, [ebp+var_14]
- .text:00405A81 call @System@AnsiString@$beql$xqqrrx17
- ; System::AnsiString::operator==(System::AnsiString &)
- .text:00405A86 push eax
- .text:00405A87 dec [ebp+var_60]
- .text:00405A8A lea eax, [ebp+var_18]
- .text:00405A8D mov edx, 2
- .text:00405A92 call sub_4A87B8
- .text:00405A97 pop ecx
- .text:00405A98 test cl, cl
- ; Если в файле записана строка "-----END-----", то триальный срок закончился
- .text:00405A9A jz loc_405B2B
- .text:00405AA0 mov [ebp+var_6C], 68h
- .text:00405AA6 mov edx, offset aTheTrialVersio
- ; "The trial version of Cool MP3 Splitter "...
- .text:00405AAB lea eax, [ebp+var_1C]
- .text:00405AAE call sub_4A8660
- .text:00405AB3 inc [ebp+var_60]
- .text:00405AB6 mov edx, [eax]
- ...
- ...
- .text:00405B19 mov edx, off_4BA1AC
- .text:00405B1F mov eax, [edx]
- ; Выйти из программы
- .text:00405B21 call @Forms@TApplication@Terminate$qqrv
- ; Forms::TApplication::Terminate(void)
- .text:00405B26 jmp loc_405D24
- .text:00405B2B ; -------------------------------------------------
- ; Иначе триальный срок продолжается
- .text:00405B2B mov [ebp+var_6C], 98h
- .text:00405B31 lea eax, [ebp+var_28]
- .text:00405B34 mov edx, ebx
- .text:00405B36 call sub_4A8660
- .text:00405B3B inc [ebp+var_60]
- .text:00405B3E lea edx, [ebp+var_28]
- .text:00405B41 lea eax, [ebp+var_90]
- .text:00405B47 mov cl, 2
- ; Получить текущую дату и время в виде строки
- .text:00405B49 call @System@TDateTime
- .text:00405B4E dec [ebp+var_60]
- .text:00405B51 lea eax, [ebp+var_28]
- ...
- ...
- .text:00405C0C mov eax, [ebp+var_80]
- ; Записать в файл
- .text:00405C0F call @Classes@TStream@WriteBuffer$qqrpxvi
- ; Classes::TStream::WriteBuffer(void *,int)
- .text:00405C14 mov ebx, [ebp+var_80]
- .text:00405C17 mov [ebp+var_34], ebx
- .text:00405C1A test ebx, ebx
- ...
Просмотров: 5430 | Комментариев: 5
Метки: исследование защиты
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
Pp
(30.11.2010 в 12:16):
Всё работает сам затупил.
Pp
(30.11.2010 в 10:58):
Ограничение на "60-секундность" фрагмента не снимает.
AyTkACT
(15.04.2010 в 20:20):
"А вообще меня бесят криворукие шароварщики, которые пытаются запрятать регистрацию или триальные счетчики своего говнософта в системные папки, да еще и в файл с именем динамической библиотеки." © ManHunter Подпишусь под каждым словом. Понравилось решение с cmp -> inc. Вообщем как всегда приятно почитать. Спасибо.
Чтец
(13.04.2010 в 17:09):
Как всегда отлично, особенно финальная часть. :)
INC.
(13.04.2010 в 13:53):
Thanks
Добавить комментарий
Заполните форму для добавления комментария