Blog. Just Blog

Исследование защиты программы Visual Watermark

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Темная сторона Силы | Автор: ManHunter
Скриншот программы Visual Watermark
Скриншот программы Visual Watermark

Программа Visual Watermark предназначена для нанесения текстовых и графических водяных знаков или подписей на цифровые фотографии. Программа очень проста в использовании, имеет простой и дружественный интерфейс, поддерживает все популярные графические форматы, имеет функцию пакетной обработки изображений. Все это можно сделать совершенно бесплатно в любых графических программах, а можно выложить денежку и сделать в программе Visual Watermark. А можно и не выкладывать ничего, и точно так же сделать в программе Visual Watermark. Последний вариант мне нравится больше.

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

Сообщение о неправильной регистрации
Сообщение о неправильной регистрации

Нужная строка легко находится в файле:

Строка сообщения найдена
Строка сообщения найдена

Дальше в дизассемблере по перекрестным ссылкам находим код, где она используется и смотрим условия ее появления:
  1. .text:00457360 sub_457360      proc near
  2. .text:00457360 arg_0           = dword ptr  8
  3. .text:00457360                 push    ebp
  4. .text:00457361                 mov     ebp, esp
  5. .text:00457363                 mov     eax, [ebp+arg_0]
  6. .text:00457366                 inc     dword ptr [eax+30Ch]
  7. .text:0045736C                 mov     edx, [ebp+arg_0]
  8. .text:0045736F                 cmp     dword ptr [edx+30Ch], 3
  9. .text:00457376                 jge     short loc_457394
  10. .text:00457378                 push    10h             ; uType
  11. .text:0045737A                 push    offset aRegistration_1
  12. ; "Registration"
  13. .text:0045737F                 push    offset aTheKeyYouEnter
  14. ; "The key you entered is not valid. Pleas"...
  15. .text:00457384                 mov     eax, [ebp+arg_0]
  16. .text:00457387                 call    @Controls@TWinControl@GetHandle$qqrv
  17. .text:0045738C                 push    eax             ; hWnd
  18. .text:0045738D                 call    MessageBoxA
  19. .text:00457392                 pop     ebp
  20. .text:00457393                 retn
Вывод сообщения о неправильной регистрации оформлен в виде отдельной процедуры, которая, в свою очередь, вызывается отсюда:
  1. ; Вызвать процедуру проверки
  2. .text:004572B5                 call    sub_42920C
  3. .text:004572BA                 pop     ecx
  4. ; ПоXORить результат функции с константой 35h
  5. .text:004572BB                 xor     eax, 35h
  6. ; Сохранить результат в ячейку памяти
  7. .text:004572BE                 mov     [ebp+var_44], eax
  8. .text:004572C1                 mov     [ebp+var_28], 14h
  9. ; Сравнить результат с числом 24h
  10. .text:004572C7                 cmp     [ebp+var_44], 24h
  11. ; Если не равно, то показать сообщение о неправильной регистрации
  12. .text:004572CB                 jz      short loc_4572D8
  13. .text:004572CD                 push    [ebp+var_3C]
  14. ; Сообщение о неправильной регистрации
  15. .text:004572D0                 call    sub_457360
Вызывается процедура проверки по адресу 0042920C, если она вернула значение, которое после XORирования с константой 35h дает значение 24h, то регистрация правильная. Несложной математикой получаем нужное значение - 11h. Сама же функция проверки достаточно громоздкая, но тут нам на помощь приходит замечательный плагин для дизассемблера IDA - Hex-Rays. Он позволяет перевести ассемблерный листинг в C-подобный псевдокод. Скомпилировать его, конечно, не получится, но зато псевдокод в разы облегчает анализ файла. Вот что получается после преобразования функции проверки в псевдокод:
  1. int __cdecl sub_42920C(char a1)
  2. {
  3.   __InitExceptBlockLDTC();
  4.   System__AnsiString__AnsiString(&a1, &a1);
  5.   v12 2;
  6.   v11 8;
  7.   v9 sub_42919C(a1);
  8.   if ( sub_429350(&unk_6D71F8, &unk_6E0E38, &v9) > (unsigned int)&unk_6E0E34
  9.     || (v8 sub_42919C(a1),
  10.         v2 __rwstd____RW_array(&unk_732B08),
  11.         v3 unknown_libname_34(&unk_732B08),
  12.         v4 sub_429350(v3v2, &v8),
  13.         v4 != __rwstd____RW_array(&unk_732B08))
  14.     || System__AnsiString__Length(&a1v1v7) != 19
  15.     || *(_BYTE *)System__AnsiString__operator__(&a15) != 45
  16.     || *(_BYTE *)System__AnsiString__operator__(&a110) != 45
  17.     || *(_BYTE *)System__AnsiString__operator__(&a115) != 45 )
  18.     v10 6681;
  19.   else
  20.     v10 17;
  21.   v6 v10;
  22.   --v12;
  23.   sub_6C8040(&a12);
  24.   return v6;
  25. }
Все как на ладони. Тут и проверка на длину серийника - 19 символов, и проверка на наличие разделителей (символов с кодами 45 в десятичной системе, это символы "-") в позициях 5, 10 и 15. А также еще одна дополнительная проверка, к которой мы вернемся чуть позже. А пока мы выяснили формат и длину серийного номера, это должно быть что-то типа "ABCD-EFGH-JKLM-NOPQ". Теперь нас интересуют два вызова - это функция sub_42919C и sub_429350. Первая представляет собой последовательность вызовов:
  1. .text:0042919C sub_42919C      proc near
  2. .text:0042919C                 push    ebp
  3. .text:0042919D                 mov     ebp, esp
  4. .text:0042919F                 add     esp, 0FFFFFFD4h
  5. .text:004291A2                 mov     eax, offset stru_6E0F50
  6. .text:004291A7                 call    @__InitExceptBlockLDTC
  7. .text:004291AC                 mov     [ebp+var_8], 1
  8. .text:004291B3                 lea     edx, [ebp+arg_0]
  9. .text:004291B6                 lea     eax, [ebp+arg_0]
  10. .text:004291B9                 call    @System@AnsiString@AnsiString
  11. .text:004291BE                 inc     [ebp+var_8]
  12. .text:004291C1                 mov     [ebp+var_14], 8
  13. .text:004291C7                 lea     eax, [ebp+arg_0]
  14. .text:004291CA                 call    @System@AnsiString@c_str$xqqrv
  15. .text:004291CF                 push    eax
  16. .text:004291D0                 lea     edx, [ebp+var_2C]
  17. .text:004291D3                 push    edx
  18. .text:004291D4                 call    sub_4291FC
  19. .text:004291D9                 add     esp, 8
  20. .text:004291DC                 push    eax
  21. .text:004291DD                 dec     [ebp+var_8]
  22. .text:004291E0                 lea     eax, [ebp+arg_0]
  23. .text:004291E3                 mov     edx, 2
  24. .text:004291E8                 call    sub_6C8040
  25. .text:004291ED                 pop     eax
  26. .text:004291EE                 mov     edx, [ebp+var_24]
  27. .text:004291F1                 mov     large fs:0, edx
  28. .text:004291F8                 mov     esp, ebp
  29. .text:004291FA                 pop     ebp
  30. .text:004291FB                 retn
  31. .text:004291FB sub_42919C      endp
  32.  
  33. ;-------------------------------------------------------
  34.  
  35. .text:004291FC sub_4291FC      proc near
  36. .text:004291FC                 push    ebp
  37. .text:004291FD                 mov     ebp, esp
  38. .text:004291FF                 push    [ebp+arg_4]
  39. .text:00429202                 call    sub_429FC0
  40. .text:00429207                 pop     ecx
  41. .text:00429208                 pop     ebp
  42. .text:00429209                 retn
  43. .text:00429209 sub_4291FC      endp
  44.  
  45. ;-------------------------------------------------------
  46.  
  47. .text:00429FC0                 push    ebp
  48. .text:00429FC1                 mov     ebp, esp
  49. .text:00429FC3                 push    ecx
  50. .text:00429FC4                 xor     eax, eax
  51. .text:00429FC6                 mov     [ebp+var_4], eax
  52. .text:00429FC9                 jmp     short loc_429FDF
  53. .text:00429FCB ; ---------------------------------
  54. .text:00429FCB loc_429FCB:
  55. .text:00429FCB                 mov     edx, [ebp+var_4]
  56. .text:00429FCE                 lea     edx, [edx+edx*4]
  57. .text:00429FD1                 mov     ecx, [ebp+arg_0]
  58. .text:00429FD4                 movsx   eax, byte ptr [ecx]
  59. .text:00429FD7                 add     edx, eax
  60. .text:00429FD9                 mov     [ebp+var_4], edx
  61. .text:00429FDC                 inc     [ebp+arg_0]
  62. .text:00429FDF loc_429FDF:
  63. .text:00429FDF                 mov     edx, [ebp+arg_0]
  64. .text:00429FE2                 cmp     byte ptr [edx], 0
  65. .text:00429FE5                 jnz     short loc_429FCB
  66. .text:00429FE7                 mov     eax, [ebp+var_4]
  67. .text:00429FEA                 pop     ecx
  68. .text:00429FEB                 pop     ebp
  69. .text:00429FEC                 retn
При помощи того же плагина Hex-Rays этот код сворачивается в абсолютно понятный псевдокод:
  1. int __cdecl sub_429FC0(int a1)
  2. {
  3.   int v2// [sp+0h] [bp-4h]@1
  4.  
  5.   v2 0;
  6.   while ( *(_BYTE *)a1 )
  7.     v2 = *(_BYTE *)a1++ + v2;
  8.   return v2;
  9. }
Под отладчиком эта процедура становится еще понятнее, в ней считается некая контрольная сумма от всего серийного номера, включая разделители. Алгоритм заключается в последовательном сложении и умножении на 5 всех символов строки серийного номера. На выходе получаем контрольное значение. Вторая функция - это проверка контрольного значения. Я приведу только ее часть, остальное лучше смотреть под отладчиком.
  1. .text:00429350 sub_429350      proc near
  2. .text:00429350                 push    ebp
  3. .text:00429351                 mov     ebp, esp
  4. .text:00429353                 add     esp, 0FFFFFFF8h
  5. .text:00429356                 lea     eax, [ebp+var_8]
  6. .text:00429359                 push    eax
  7. .text:0042935A                 push    [ebp+arg_8]
  8. .text:0042935D                 push    [ebp+arg_4]
  9. .text:00429360                 push    [ebp+arg_0]
  10. .text:00429363                 call    sub_429370
  11. .text:00429368                 add     esp, 10h
  12. .text:0042936B                 pop     ecx
  13. .text:0042936C                 pop     ecx
  14. .text:0042936D                 pop     ebp
  15. .text:0042936E                 retn
  16. .text:0042936E sub_429350      endp
  17.  
  18. ;-------------------------------------------------------
  19.  
  20. .text:00429370 sub_429370      proc near
  21. .text:00429370                 push    ebp
  22. .text:00429371                 mov     ebp, esp
  23. .text:00429373                 push    ecx
  24. .text:00429374                 mov     eax, [ebp+arg_4]
  25. .text:00429377                 sub     eax, [ebp+arg_0]
  26. .text:0042937A                 test    eax, eax
  27. .text:0042937C                 jns     short loc_429381
  28. .text:0042937E                 add     eax, 3
  29. .text:00429381 loc_429381:
  30. .text:00429381                 sar     eax, 2
  31. .text:00429384                 sar     eax, 2
  32. .text:00429387                 mov     [ebp+var_4], eax
  33. .text:0042938A                 cmp     [ebp+var_4], 0
  34. .text:0042938E                 jle     short loc_4293F1
  35. .text:00429390 loc_429390:
  36. .text:00429390                 mov     edx, [ebp+arg_0]
  37. .text:00429393                 mov     ecx, [edx]
  38. .text:00429395                 mov     eax, [ebp+arg_8]
  39. .text:00429398                 cmp     ecx, [eax]
  40. .text:0042939A                 jnz     short loc_4293A2
  41. .text:0042939C                 mov     eax, [ebp+arg_0]
  42. .text:0042939F                 pop     ecx
  43. .text:004293A0                 pop     ebp
  44. .text:004293A1                 retn
  45. .text:004293A2 ; ------------------------------------------
  46. .text:004293A2 loc_4293A2:
  47. .text:004293A2                 add     [ebp+arg_0], 4
  48. .text:004293A6                 mov     edx, [ebp+arg_0]
  49. .text:004293A9                 mov     ecx, [edx]
  50. .text:004293AB                 mov     eax, [ebp+arg_8]
  51. .text:004293AE                 cmp     ecx, [eax]
  52. .text:004293B0                 jnz     short loc_4293B8
  53. .text:004293B2                 mov     eax, [ebp+arg_0]
  54. .text:004293B5                 pop     ecx
  55. .text:004293B6                 pop     ebp
  56. .text:004293B7                 retn
  57. .text:004293B8 ; ----------------------------------------------
  58. .text:004293B8 loc_4293B8:
  59. .text:004293B8                 add     [ebp+arg_0], 4
  60. .text:004293BC                 mov     edx, [ebp+arg_0]
  61. .text:004293BF                 mov     ecx, [edx]
  62. .text:004293C1                 mov     eax, [ebp+arg_8]
  63. .text:004293C4                 cmp     ecx, [eax]
  64. .text:004293C6                 jnz     short loc_4293CE
  65. .text:004293C8                 mov     eax, [ebp+arg_0]
  66. .text:004293CB                 pop     ecx
  67. .text:004293CC                 pop     ebp
  68. .text:004293CD                 retn
  69.  
  70. ...
В листинге код не очень понятен, а вот отладчик вносит ясность. В программе по адресу 006D71F8 содержится массив заранее определенных проверочных значений для серийных номеров в формате DWORD размером 10000 элементов:
  1. .data:006D71F8 dword_6D71F8    dd 0C6DDDh
  2. .data:006D71FC                 dd 1267C1h
  3. .data:006D7200                 dd 14EC36h
  4. .data:006D7204                 dd 15BFAFh
  5. .data:006D7208                 dd 1F2B5Bh
  6. .data:006D720C                 dd 259E5Ch
  7. .data:006D7210                 dd 3F92A6h
  8. .data:006D7214                 dd 47A33Eh
  9. ...
Если рассчитанное проверочное значение совпадает с одним из значений их массива, то программа считается зарегистрированной. То есть какого-либо особого алгоритма подсчета серийного номера нет, все возможные значения заранее определены. Итак, мы знаем формат серийного номера, алгоритм подсчета контрольного значения и массив из 10000 проверочных значений. Кейген пишется на базе простейшего брутфорса.

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

Защиты такого типа, где серийный номер сравнивается с заранее подготовленным списком, встречаются не очень часто, но все-таки авторы упорно продолжают их использовать. И хорошо, если для хеширования используется что-то типа MD5 или более серьезные алгоритмы, а что если при слабом алгоритме хеширования ключ из списка будет подобран брутфорсом или реверсом алгоритма из исходного значения? Автор, по идее, должен забанить такие ключи сразу после релиза. Что же в этом случае делать легальным пользователям? Вопрос остается без ответа.

Поделиться ссылкой ВКонтакте
Просмотров: 7554 | Комментариев: 16

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (24.09.2016 в 08:32):
Три года прошло, не удивительно.
brute (24.09.2016 в 07:43):
Хотел разминки ради заломать "самостоятельно", но в новых версиях (3.4 - с сервера/4.3 - с сайта) всё поменялось - и интерфейс и защита. Похоже, накрыто армой_4.х. То есть, статья не актуальна..
Alex (09.01.2016 в 21:21):
ты супер .....
ManHunter (19.07.2013 в 19:39):
This is a "HiEW" -> hiew.ru or google for warez.
vhexr (19.07.2013 в 19:24):
I wonder what is the name you use the software ?

this pic

http://www.manhunter.ru/upload...28828fa5.gif
sim31 (18.07.2013 в 13:07):
Спасибо за упоминание о программе, давно что-то подобное искал (в Irfan View тоже можно делать и даже автоматизировать хоть 10 000 файлов одним кликом), но тут нагляднее, и за картинку 7fb45f10190c1bac07ad20058b93f6f7.gif :)
ManHunter (15.07.2013 в 23:09):
vhexr, I never do that and will not do.
vhexr (15.07.2013 в 23:07):
Hi ManHunter please this release video tutorial
Noobie (15.07.2013 в 13:47):
>патчится один из хешей
Само собой, так и было сделано, дел на 5 минут по часам :-) Но всегда хочется чего-то большого и чистого.
Vladimir (15.07.2013 в 11:36):
Fix CRC v1.01 от PEiD прекрасно помог, после патча программа запускается и работает. Пытался патчить проверку CRC, но не разобрался с этим madExcept. <offtop>Сам люблю в программах портативность, потому серийные номера не так интересны.</offtop>
ManHunter (15.07.2013 в 10:55):
Ну или же как вариант можно сделать лоадер и патчить программу в памяти. Но это если уж совсем скучно и нечем заняться.
irokkezz (15.07.2013 в 09:14):
Спасибо большое!
ManHunter (15.07.2013 в 09:12):
Хех :) Я специально не рассматривал тут вариант с патчем :)
А копать надо в направлении CRC в PE-хидере, защита основана на ней. Достаточно пофиксить заголовок после модификации файла и все заработает.
irokkezz (15.07.2013 в 09:09):
Доброе утро!
Попробовал пропатчить процедуру 0042920C:
mov eax,11h
ret
Под отладчиком программа запускается без триальных ограничений, а в обычном режиме выводится сообщение о том, что файл поврежден. Попытался определить условие вывода этого сообщения и совсем запутался.
Не подскажете, в каком направлении копать?
ManHunter (15.07.2013 в 08:15):
В таких случаях генерируется какой-нибудь свой серийник и патчится один из хешей под него. MD5 при 25 цифрах брутить очень гиморно.
Noobie (15.07.2013 в 06:48):
Спасибо за статью, как всегда наглядно и доходчиво. Вопрос - встретилась программа с аналогичным методом защиты, серийник 25 цифр, первые 5 известны, считается MD5 и сравнивается с несколькими десятками зашитых значений. Насколько реально брутфорснуть, путем тупого перебора значений MD5 и их сравнения с валидными?

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0066 сек.) / Память: 4.5 Mb
Наверх