
Скриншот программы DeblurMyImage
Программа
DeblurMyImage позволяет усилить резкость фотографий при ошибочном выборе точки фокусировки или при съемке в движении. Чудесного и кристально четкого результата, конечно, ожидать не стоит, но немного улучшить качество снимков для печати или публикации вполне можно. Особенно, когда это единственный кадр, а переснять нет никакой возможности. Главная шутка юмора в том, что DeblurMyImage по сути - всего лишь графическая надстройка над бесплатной библиотекой для работы с изображениями
FreeImage. Так какого хрена мы должны выкладывать за эту надстройку почти 15 евро? Тем более, что со стороны аффтара DeblurMyImage были допущены грубейшие нарушения
лицензии и условий использования библиотеки FreeImage.
Скачиваем дистрибутив, устанавливаем, запускаем. Сразу же после запуска нас встречает вот такое окно с предложением купить, зарегистрироваться или продолжить в триальном режиме. Про покупку мы уже все решили, это нас не интересует. При попытке зарегистрироваться внезапно оказывается, что для регистрации требуется какой-то файл с лицензией, формат которого мы не знаем и которого у нас нет. Значит, остается пока это все пропустить.

Окно регистрации
Дальше нас оповещают, что незарегистрированная версия DeblurMyImage не будет сохранять отредактированные файлы. Это, собственно, и есть главное ограничение программы. И действительно, файл можно отредактировать, посмотреть что получилось, но при попытке сохранения вместо него появляется вот такое сообщение:

Сообщение незарегистрированной программы
Это уже более удобная зацепка для анализа защиты, поищем текст сообщения в исполняемом файле. Он ничем не упакован, поэтому найдется строка в юникоде:

Строка сообщения найдена
Переходим к дизассемблеру. Посмотрим, где и как эта строка используется.
.text:0042187B push ecx ; lpFileName
.text:0042187C push 400h ; nSize
.text:00421881 push offset word_4A481C
; lpReturnedString
.text:00421886 push offset aCannotSaveTheI
; "Cannot save the image due to unregister"...
.text:0042188B push offset aString72 ; "string72"
.text:00421890 lea edx, [esp+830h+Dst]
.text:00421894 push edx ; lpAppName
.text:00421895 call esi ; GetPrivateProfileStringW
Если посмотреть документацию по функции GetPrivateProfileString, то станет ясно, что тут программа пытается загрузить строку из внешнего языкового файла, а, если такого не найдено, то по умолчанию используется искомая строка. И word_4A481C - это указатель на участок памяти, куда эта строка будет записана для дальнейшего использования. Значит теперь посмотрим на него:
.data:004A481C ; const WCHAR word_4A481C
.data:004A481C word_4A481C dw ?
; DATA XREF: sub_40DDB0+2276
; sub_420F10+971
Всего две перекрестные ссылки. Одна, как мы уже выяснили, это инициализация строки. Очевидно, что оставшаяся - место, где эта строка используется, то есть выводится на экран в виде сообщения. Так и есть:
; Вызвать какую-то функцию проверки
.text:00410011 call sub_40D170
; Если она вернула AL=0, то вывести сообщение на экран, иначе перейти
; к сохранению файла
.text:00410016 test al, al
.text:00410018 mov byte_4C5734, al
.text:0041001D jnz short loc_41004B
.text:0041001F push 0 ; uType
.text:00410021 push offset word_49481C ; lpCaption
; Вот она наша строка сообщения
.text:00410026 push offset word_4A481C ; lpText
.text:0041002B push edi ; hWnd
; А вот и функция показа сообщения
.text:0041002C call ds:MessageBoxW
.text:00410032 xor eax, eax
.text:00410034 pop edi
.text:00410035 pop esi
.text:00410036 pop ebx
.text:00410037 mov ecx, [esp+3DF4h+var_4]
.text:0041003E xor ecx, esp
.text:00410040 call sub_425146
.text:00410045 mov esp, ebp
.text:00410047 pop ebp
.text:00410048 retn 10h
.text:0041004B ; ------------------------------------------
.text:0041004B loc_41004B:
.text:0041004B push 800h ; size_t
.text:00410050 lea eax, [esp+3E04h+Dst]
.text:00410057 push 0 ; int
.text:00410059 push eax ; void *
...
Дальше и ходить никуда не надо, все и так ясно. Вызывается функция проверки, по ее результату или выводится на экран сообщение о невозможности сохранения по причине незарегистрированности или выполняется переход на сохранения файла. Осталось посмотреть на функицю проверки по адресу 40D170:
.text:0040D170 push ecx
.text:0040D171 push ebx
.text:0040D172 push 1
.text:0040D174 call sub_40C980
.text:0040D179 push 0
.text:0040D17B mov bl, al
.text:0040D17D call sub_40C980
.text:0040D182 add esp, 8
.text:0040D185 mov [esp+8+var_1], al
.text:0040D189 call sub_40D030
.text:0040D18E test bl, bl
.text:0040D190 pop ebx
.text:0040D191 jnz short loc_40D19A
.text:0040D193 cmp [esp+4+var_1], 0
.text:0040D198 jz short loc_40D1A5
.text:0040D19A loc_40D19A:
.text:0040D19A test al, al
.text:0040D19C jnz short loc_40D1A5
.text:0040D19E mov eax, 1
.text:0040D1A3 pop ecx
.text:0040D1A4 retn
.text:0040D1A5 ; ---------------------------------------------------
.text:0040D1A5 loc_40D1A5:
.text:0040D1A5 xor eax, eax
.text:0040D1A7 pop ecx
.text:0040D1A8 retn
Если посмотреть все перекрестные ссылки на нее, то видно, что все они так или иначе связаны с триальным статусом. Это показ сообщений, открытие первого диалогового окна с регистрацией, активация пунктов меню. И везде используется регистр AL. Значит просто пропатчим эту функцию, чтобы она всегда возвращала AL=1. Это уже хорошо знакомая нам пара команд MOV AL,1 и RET, которые надо записать по адресу
0040D170. Сохраняем изменения, запускаем. Окно с предложением регистрации пропало, а при сохранении отредактированного файла вместо сообщения об ошибке появляется диалог выбора имени сохраняемого файла.
Все, защиту мы обошли, справедливость восстановлена. А на месте разработчиков различных бесплатных компонентов я бы запрещал их использование в составе коммерческих продуктов. Ибо нефиг пытаться нарубить капусты на чужом энтузиазме.