Blog. Just Blog

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

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

FabulousMP3 - очень толковый "улучшайзер" для музыкальных файлов в формате MP3 и WAV, позволяющий в автоматическом режиме добавить полноту звука, выровнять громкость звучания, выделить голос, скорректировать баланс частот и динамический диапазон, после чего даже самая обычная композиция будет звучать чуть ли не профессионально даже на бытовой аудиоаппаратуре. Готовый результат не обязательно сохранять на диск, программа умеет работать как плеер, естественно, со всеми настройками звука. Лично мне программа понравилась. А вот идея выкладывать 38 баксов за лицензию не понравилась.

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

Сообщение о неправильном серийнике
Сообщение о неправильном серийнике

Это не обычное окно сообщения, оно оформлено как полноценное диалоговое окно и хранится в ресурсах под индексом 167, оно же 0A7h в шестнадцатеричной системе счисления.

Окно сообщения в ресурсах
Окно сообщения в ресурсах

Поиском этого значения по листингу дизассемблера обнаруживается следующий код. Тут ничего сложного нет, обычная проверка, по ее результатам программа или ругается на неправильный серийник, либо благодарит за регистрацию и обновляет заголовок главного окна.
  1. .text:004065AB                 mov     esi, ds:GetDlgItemTextA
  2. .text:004065B1                 lea     eax, [ebp+String]
  3. .text:004065B7                 push    0FFh            ; cchMax
  4. .text:004065BC                 push    eax             ; lpString
  5. .text:004065BD                 push    3E9h            ; nIDDlgItem
  6. .text:004065C2                 push    edi             ; hDlg
  7. .text:004065C3                 call    esi ; GetDlgItemTextA
  8. .text:004065C5                 push    0FFh            ; cchMax
  9. .text:004065CA                 lea     eax, [ebp+String2]
  10. .text:004065D0                 push    eax             ; lpString
  11. .text:004065D1                 push    40Fh            ; nIDDlgItem
  12. .text:004065D6                 push    edi             ; hDlg
  13. .text:004065D7                 call    esi ; GetDlgItemTextA
  14. .text:004065D9                 lea     eax, [ebp+String]
  15. .text:004065DF                 push    eax
  16. .text:004065E0                 lea     eax, [ebp+String2]
  17. .text:004065E6                 push    eax
  18. ; Вызвать функцию проверки
  19. .text:004065E7                 call    loc_412E20
  20. .text:004065EC                 add     esp, 8
  21. .text:004065EF                 push    100h
  22. .text:004065F4                 push    0
  23. .text:004065F6                 push    offset String1
  24. ; Если она вернула EAX=7115h, то регистрация успешна
  25. .text:004065FB                 cmp     eax, 7115h
  26. ; Серийный номер неправильный
  27. .text:00406600                 jnz     loc_406697
  28. .text:00406606                 mov     dword_496788, 1
  29. .text:00406610                 call    sub_4462B0
  30. .text:00406615                 push    100h
  31. .text:0040661A                 push    0
  32. .text:0040661C                 push    offset byte_6FC520
  33. .text:00406621                 call    sub_4462B0
  34. .text:00406626                 mov     esi, ds:lstrcpyA
  35. .text:0040662C                 lea     eax, [ebp+String2]
  36. .text:00406632                 add     esp, 18h
  37. .text:00406635                 push    eax             ; lpString2
  38. .text:00406636                 push    offset String1  ; lpString1
  39. .text:0040663B                 call    esi ; lstrcpyA
  40. .text:0040663D                 lea     eax, [ebp+String]
  41. .text:00406643                 push    eax             ; lpString2
  42. .text:00406644                 push    offset byte_6FC520 ; lpString1
  43. .text:00406649                 call    esi ; lstrcpyA
  44. .text:0040664B                 push    0               ; dwInitParam
  45. .text:0040664D                 push    offset DialogFunc ; lpDialogFunc
  46. .text:00406652                 push    edi             ; hWndParent
  47. .text:00406653                 push    0A6h            ; lpTemplateName
  48. .text:00406658                 push    ebx             ; hInstance
  49. .text:00406659                 call    ds:DialogBoxParamA
  50. .text:0040665F                 push    offset byte_6FC520 ; lpString
  51. .text:00406664                 push    offset String1  ; int
  52. .text:00406669                 call    sub_412F30
  53. .text:0040666E                 add     esp, 8
  54. .text:00406671                 xor     ecx, ecx
  55. .text:00406673                 cmp     eax, 7115h
  56. .text:00406678                 setz    cl
  57. .text:0040667B                 push    offset String   ; "FabulousMP3 V3.04"
  58. .text:00406680                 push    dword_4965E4    ; hWnd
  59. .text:00406686                 mov     dword_496788, ecx
  60. .text:0040668C                 call    ds:SetWindowTextA
  61. .text:00406692                 jmp     loc_40658A
  62. .text:00406697 ; ---------------------------------------
  63. .text:00406697 loc_406697:
  64. .text:00406697                 mov     dword_496788, 0
  65. .text:004066A1                 call    sub_4462B0
  66. .text:004066A6                 push    100h
  67. .text:004066AB                 push    0
  68. .text:004066AD                 push    offset byte_6FC520
  69. .text:004066B2                 call    sub_4462B0
  70. .text:004066B7                 push    0BB8h
  71. .text:004066BC                 call    sub_4131D0
  72. .text:004066C1                 add     esp, 1Ch
  73. ; Вывести сообщение о неправильной регистрации
  74. .text:004066C4                 push    0               ; dwInitParam
  75. .text:004066C6                 push    offset DialogFunc ; lpDialogFunc
  76. .text:004066CB                 push    edi             ; hWndParent
  77. .text:004066CC                 push    0A7h            ; lpTemplateName
  78. .text:004066D1                 push    ebx             ; hInstance
  79. .text:004066D2                 call    ds:DialogBoxParamA
Самое важное, что корректный результат проверки определяется не просто 1 или 0, а уникальным значением 7115h. Смотрим функцию проверки. Часть кода я пропустил для удобства понимания.
  1. .text:00412E20                 push    ebp
  2. .text:00412E21                 mov     ebp, esp
  3. .text:00412E23                 mov     eax, [ebp+8]
  4. .text:00412E26                 sub     esp, 8
  5. .text:00412E29                 mov     ecx, offset byte_462483
  6. .text:00412E2E                 mov     edi, edi
  7. ...
  8. ...
  9. ...
  10. ; Подсчитать длину строки серийного номера
  11. .text:00412E91                 mov     ecx, ebx
  12. .text:00412E93                 lea     edx, [ecx+1]
  13. .text:00412E96 loc_412E96:
  14. .text:00412E98                 inc     ecx
  15. .text:00412E99                 test    al, al
  16. .text:00412E9B                 jnz     short loc_412E96
  17. .text:00412E9D                 sub     ecx, edx
  18. ; Длина строки должна быть 29 (1Dh) символов
  19. .text:00412E9F                 cmp     ecx, 1Dh
  20. .text:00412EA2                 jnz     short loc_412EB6
  21. ; Строка серийника должна начинаться с "FAB1-"
  22. .text:00412EA4                 push    offset aFab1    ; "FAB1-"
  23. .text:00412EA9                 push    ebx
  24. .text:00412EAA                 call    sub_413260
  25. .text:00412EAF                 add     esp, 8
  26. .text:00412EB2                 test    eax, eax
  27. .text:00412EB4                 jnz     short loc_412EBD
  28. .text:00412EB6 loc_412EB6:
  29. .text:00412EB6                 xor     eax, eax
  30. .text:00412EB8                 pop     ebx
  31. .text:00412EB9                 mov     esp, ebp
  32. .text:00412EBB                 pop     ebp
  33. .text:00412EBC                 retn
  34. .text:00412EBD ; ---------------------------------------
  35. .text:00412EBD loc_412EBD:
  36. .text:00412EBD                 push    edi
  37. ; Антиотладка: фиксация тайминга выполнения программы
  38. .text:00412EBE                 call    sub_4479C1
  39. .text:00412EC3                 cdq
  40. .text:00412EC4                 mov     edi, offset unk_492F88
  41. .text:00412EC9                 mov     [ebp-4], eax
  42. .text:00412ECC                 mov     [ebp-8], edx
  43. .text:00412ECF                 push    esi
  44. .text:00412ED0 loc_412ED0:
  45. .text:00412ED0                 mov     esi, [edi]
  46. .text:00412ED2                 push    esi
  47. .text:00412ED3                 push    dword ptr [ebp+8]
  48. .text:00412ED6                 call    sub_404ED0
  49. .text:00412EDB                 add     esp, 8
  50. ; Посимвольное сравнение строки серийника с правильным результатом
  51. .text:00412EDE                 cmp     al, [esi+ebx]
  52. .text:00412EE1                 jnz     short loc_412F1B
  53. ; Антиотладка: проверка тайминга выполнения программы
  54. .text:00412EE3                 call    sub_4479C1
  55. .text:00412EE8                 cdq
  56. .text:00412EE9                 mov     esi, eax
  57. .text:00412EEB                 mov     ecx, edx
  58. .text:00412EED                 sub     esi, [ebp-4]
  59. .text:00412EF0                 sbb     ecx, [ebp-8]
  60. .text:00412EF3                 test    ecx, ecx
  61. .text:00412EF5                 jg      short loc_412F24
  62. .text:00412EF7                 jl      short loc_412EFE
  63. ; Если код выполнялся больше 100 микросекунд, то на выход
  64. .text:00412EF9                 cmp     esi, 64h
  65. .text:00412EFC                 ja      short loc_412F24
  66. .text:00412EFE loc_412EFE:
  67. ; Следующая позиция
  68. .text:00412EFE                 add     edi, 4
  69. .text:00412F01                 mov     [ebp-4], eax
  70. .text:00412F04                 mov     [ebp-8], edx
  71. ; Достигнут конец списка позиций?
  72. .text:00412F07                 cmp     edi, offset unk_492FE8
  73. .text:00412F0D                 jl      short loc_412ED0
  74. .text:00412F0F                 pop     esi
  75. .text:00412F10                 pop     edi
  76. ; Серийник правильный
  77. .text:00412F11                 mov     eax, 7115h
  78. .text:00412F16                 pop     ebx
  79. .text:00412F17                 mov     esp, ebp
  80. .text:00412F19                 pop     ebp
  81. .text:00412F1A                 retn
  82. .text:00412F1B ; ---------------------------------------
  83. .text:00412F1B loc_412F1B:
  84. ; Серийник неправильный
  85. .text:00412F1B                 pop     esi
  86. .text:00412F1C                 pop     edi
  87. .text:00412F1D                 xor     eax, eax
  88. .text:00412F1F                 pop     ebx
  89. .text:00412F20                 mov     esp, ebp
  90. .text:00412F22                 pop     ebp
  91. .text:00412F23                 retn
  92. .text:00412F24 ; ---------------------------------------
  93. .text:00412F24 loc_412F24:
  94. ; Антиотладка: завершить работу программы
  95. .text:00412F24                 push    0
  96. .text:00412F26                 call    sub_446FF2
Что мы выяснили из кода. Длина серийного номера ровно 29 символов и он должен начинаться со строки "FAB1-". Например, это может быть строка типа "FAB1-1234-5678-90AB-CDEF-GHIJ". Дальше на основе регистрационного имени создается некое проверочное значение, потом на основании этого значения в цикле генерируются одиночные символы и сравниваются с символами на соответствующей позиции серийного номера. Символы генерятся вразнобой, позиции идут не последовательно. Такие ситуации хорошо отрабатываются под отладчиком.

Но похоже, что автор программы попытался предусмотреть подобный вариант подбора серийника и добавил простейшую антиотладку, подвязанную на время исполнения определенного участка кода. Если на проход цикла сравнения потребовалось больше времени, чем это надо в обычном "машинном" исполнении, то значит кто-то ковыряется в программе под отладчиком в пошаговом режиме и поэтому будьте любезны проследовать на выход. Программа принудительно завершит работу. Чтобы этого не произошло, достаточно прямо в отладчике заNOP'ить условные переходы по адресам 00412EF5 и 00412EFC.

Цикл проверки серийника
Цикл проверки серийника

Теперь ставим точку останова на адрес 00412EDE внутри цикла проверки и отпускаем программу на выполнение. На каждое срабатывание в поле информации будут видны параметры сравнения: символ левого серийника в памяти, а в регистре AL правильное значение. Нам останется только заменить каждый неправильный символ на правильное значение. Долго и нудно. Давайте сделаем хитрее, а конкретно заменим команду проверки на команду записи правильного значения в проверяемую ячейку памяти. Таким образом программа превратится в кейген серийников для любого имени.

Автогенерация правильного серийника
Автогенерация правильного серийника

Ставим точку останова после цикла и отпускаем программу на выполнение. Когда точка останова сработает, неправильный серийник будет перезаписан правильным, останется его только скопировать из дампа. Так, для имени "ManHunter / PCL" корректным будет серийный номер FAB1-MT8G-D52V-UEE7-LO0D-2072. Закрываем отладчик, запускаем программу как обычно, повторяем регистрацию.

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

На этот раз программа принимает имя и серийник, благодарит за регистрацию. Не забудьте сохранить серийный номер в сухом прохладном месте, как советует автор, ведь его невозможно выпустить заново. Шутка.

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

Проверяем функционал. Все работает, ограничений по времени нет, триальное окно при запуске пропало, в заголовке окна больше нет сообщения о незарегистрированной программе. Цель достигнута. Спасибо автору за отличную софтину.

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

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

Комментарии

Отзывы посетителей сайта о статье
user (22.04.2023 в 15:39):
Программа сырая - падает на разборе ID3-тегов при загрузке файла.
Илья (17.04.2023 в 17:57):
Спасибо! Небольшая неточность - длина строки серийника 29 символов
ManHunter (17.04.2023 в 16:03):
OllyDebug
Илья (17.04.2023 в 15:56):
Как называется используемая вами программа-отладчик?
ManHunter (21.02.2023 в 21:44):
Все тлен и безысходность. А в конце ждет смерть и небытие.
Ярослав (21.02.2023 в 11:03):
А стоит ли вообще авторам софта заморачиваться с защитой, если так или иначе её можно снять? Походу самый надёжный способ это вырезать куски кода, а после оплаты высылать полную версию.

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

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

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