Blog. Just Blog

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

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

Программа Cassette Converter Plus предназначена для записи звука с линейного входа или с микрофона, что, в принципе, позволяет оцифровать старые кассеты, пластинки и что-нибудь в этом роде. У меня не осталось не то что кассет, но даже ни одного устройства, на котором их можно было бы прокрутить, так что интерес к программе будет чисто академический. Тем более, что цена за нее явно завышена.

Начинаем исследование как обычно со скачивания дистрибутива. Устанавливаем, запускаем, смотрим, этот этап ничем не отличается от исследования других программ. Ну и не забываем сразу же отправить главный исполняемый файл в дизассемблер, предварительно убедившись, что он ничем не запакован. Это тоже уже должно войти в привычку при исследовании программ. Сразу после запуска программа предлагает нам зарегистрироваться:

Окно регистрации
Окно регистрации

Там же получаем текст сообщения о неправильной регистрации. Поищем строчку в исполняемом файле. Найдется юникодная строчка:

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

Когда строка сообщения о неправильной регистрации найдена, в дизассемблере посмотрим условия ее появления.
  1. .text:00521DED                 mov     eax, [ebp-28h]
  2. .text:00521DF0                 mov     edx, [eax]
  3. ; Указатель на переменную
  4. .text:00521DF2                 lea     ecx, [ebp-0CCh]
  5. .text:00521DF8                 push    ecx
  6. .text:00521DF9                 mov     ecx, [esi+3Ch]
  7. .text:00521DFC                 push    ecx
  8. .text:00521DFD                 mov     ecx, [esi+34h]
  9. .text:00521E00                 push    ecx
  10. .text:00521E01                 mov     ecx, [ebp-2Ch]
  11. .text:00521E04                 push    ecx
  12. .text:00521E05                 mov     ecx, [ebp-30h]
  13. .text:00521E08                 push    ecx
  14. .text:00521E09                 push    eax
  15. ; Вызвать неявно какую-то функцию
  16. .text:00521E0A                 call    dword ptr [edx+0A8h]
  17. .text:00521E10                 fnclex
  18. ; По ее результатам принять решение о переходе
  19. .text:00521E12                 test    eax, eax
  20. .text:00521E14                 jge     short loc_521E2B
  21. .text:00521E16                 push    0A8h
  22. .text:00521E1B                 push    offset dword_43153C
  23. .text:00521E20                 mov     edx, [ebp-28h]
  24. .text:00521E23                 push    edx
  25. .text:00521E24                 push    eax
  26. .text:00521E25                 call    ds:__vbaHresultCheckObj
  27. .text:00521E2B loc_521E2B:
  28. ; Записать в EAX значение переменной, которое передавалось в неявную функцию
  29. .text:00521E2B                 mov     eax, [ebp-0CCh]
  30. ; Сохранить его в сухом прохладном месте
  31. .text:00521E31                 mov     [ebp-20h], eax
  32. .text:00521E34                 lea     ecx, [ebp-30h]
  33. .text:00521E37                 call    ds:__vbaFreeStr
  34. .text:00521E3D                 lea     ecx, [ebp-50h]
  35. .text:00521E40                 call    ds:__vbaFreeObj
  36. ; Восстановить значение из сухого прохладного места
  37. .text:00521E46                 mov     eax, [ebp-20h]
  38. ; Если EAX не равно 0, то переход на сообщение о неправильной регистрации
  39. .text:00521E49                 test    eax, eax
  40. .text:00521E4B                 jnz     loc_522373
  41. ...
  42. ; Часть кода пропущена
  43. ...
  44. .text:00522373 loc_522373:
  45. .text:00522373                 mov     ecx, 80020004h
  46. .text:00522378                 mov     [ebp-8Ch], ecx
  47. .text:0052237E                 mov     eax, 0Ah
  48. .text:00522383                 mov     [ebp-94h], eax
  49. .text:00522389                 mov     [ebp-7Ch], ecx
  50. .text:0052238C                 mov     [ebp-84h], eax
  51. .text:00522392                 mov     edx, [esi+34h]
  52. .text:00522395                 push    edx
  53. .text:00522396                 push    offset dword_42CAD4
  54. .text:0052239B                 call    edi ; __vbaStrCat
  55. .text:0052239D                 mov     edx, eax
  56. .text:0052239F                 lea     ecx, [ebp-4Ch]
  57. .text:005223A2                 call    ebx ; __vbaStrMove
  58. .text:005223A4                 push    eax
  59. .text:005223A5                 mov     eax, [esi+3Ch]
  60. .text:005223A8                 push    eax
  61. .text:005223A9                 call    edi ; __vbaStrCat
  62. .text:005223AB                 mov     [ebp-6Ch], eax
  63. .text:005223AE                 mov     dword ptr [ebp-74h], 8
  64. .text:005223B5                 push    offset aErrorRegistrat
  65. ; "Error registration key."
  66. .text:005223BA                 mov     ecx, [ebp-20h]
  67. .text:005223BD                 push    ecx
  68. .text:005223BE                 call    ds:__vbaStrI4
  69. ...
Код я прокомментировал, в нем должно быть все понятно. Кроме одного места, где нас ожидает сюрприз - функция проверки вызывается в неявном виде. То есть управление передается по значению какого-то участка памяти, который будет сформирован только в процессе выполнения. При статическом анализе программы в дизассемблере этот адрес узнать абсолютно нереально. Придется запускать программу под отладчиком и ставить точку останова на CALL по адресу 00521E0A.

Когда точка останова сработает, переходим в пошаговый режим трассировки с заходом в процедуры и попадаем сперва на команду JMP, а затем уже на саму функцию проверки:
  1. ...
  2. ; Переход на функцию проверки
  3. .text:0040E4CE                 jmp     loc_4AD630
  4. ...
  1. ; Функция проверки
  2. .text:004AD630                 push    ebp
  3. .text:004AD631                 mov     ebp, esp
  4. .text:004AD633                 sub     esp, 18h
  5. .text:004AD636                 push    offset __vbaExceptHandler
  6. .text:004AD63B                 mov     eax, large fs:0
  7. .text:004AD641                 push    eax
  8. .text:004AD642                 mov     large fs:0, esp
  9. .text:004AD649                 mov     eax, 0A4h
  10. .text:004AD64E                 call    __vbaChkstk
  11. .text:004AD653                 push    ebx
  12. .text:004AD654                 push    esi
  13. .text:004AD655                 push    edi
  14. .text:004AD656                 mov     [ebp-18h], esp
  15. .text:004AD659                 mov     dword ptr [ebp-14h], offset dword_4031B0
  16. .text:004AD660                 mov     dword ptr [ebp-10h], 0
  17. .text:004AD667                 mov     dword ptr [ebp-0Ch], 0
  18. .text:004AD66E                 mov     eax, [ebp+8]
  19. .text:004AD671                 mov     ecx, [eax]
  20. .text:004AD673                 mov     edx, [ebp+8]
  21. .text:004AD676                 push    edx
  22. .text:004AD677                 call    dword ptr [ecx+4]
  23. .text:004AD67A                 mov     dword ptr [ebp-4], 1
  24. .text:004AD681                 mov     edx, [ebp+0Ch]
  25. .text:004AD684                 lea     ecx, [ebp-40h]
  26. .text:004AD687                 call    ds:__vbaStrCopy
  27. .text:004AD68D                 mov     edx, [ebp+10h]
  28. .text:004AD690                 lea     ecx, [ebp-3Ch]
  29. .text:004AD693                 call    ds:__vbaStrCopy
  30. .text:004AD699                 mov     edx, [ebp+14h]
  31. .text:004AD69C                 lea     ecx, [ebp-44h]
  32. .text:004AD69F                 call    ds:__vbaStrCopy
  33. .text:004AD6A5                 mov     edx, [ebp+18h]
  34. .text:004AD6A8                 lea     ecx, [ebp-28h]
  35. .text:004AD6AB                 call    ds:__vbaStrCopy
  36. .text:004AD6B1                 mov     dword ptr [ebp-4], 2
  37. .text:004AD6B8                 push    1
  38. .text:004AD6BA                 call    ds:__vbaOnError
  39. .text:004AD6C0                 mov     dword ptr [ebp-4], 3
  40. .text:004AD6C7                 lea     eax, [ebp-4Ch]
  41. .text:004AD6CA                 push    eax
  42. .text:004AD6CB                 mov     ecx, [ebp-3Ch]
  43. .text:004AD6CE                 push    ecx
  44. .text:004AD6CF                 mov     edx, [ebp+8]
  45. .text:004AD6D2                 mov     eax, [edx]
  46. .text:004AD6D4                 mov     ecx, [ebp+8]
  47. .text:004AD6D7                 push    ecx
  48. .text:004AD6D8                 call    dword ptr [eax+0C0h]
  49. .text:004AD6DE                 mov     edx, [ebp-4Ch]
  50. .text:004AD6E1                 mov     [ebp-0C0h], edx
  51. .text:004AD6E7                 mov     dword ptr [ebp-4Ch], 0
  52. .text:004AD6EE                 mov     edx, [ebp-0C0h]
  53. .text:004AD6F4                 lea     ecx, [ebp-24h]
  54. .text:004AD6F7                 call    ds:__vbaStrMove
  55. .text:004AD6FD                 mov     dword ptr [ebp-4], 4
  56. .text:004AD704                 mov     dword ptr [ebp-74h], offset dword_42D054
  57. .text:004AD70B                 mov     dword ptr [ebp-7Ch], 8
  58. .text:004AD712                 lea     edx, [ebp-7Ch]
  59. .text:004AD715                 lea     ecx, [ebp-5Ch]
  60. .text:004AD718                 call    ds:__vbaVarDup
  61. .text:004AD71E                 push    0
  62. .text:004AD720                 push    0FFFFFFFFh
  63. .text:004AD722                 lea     eax, [ebp-5Ch]
  64. .text:004AD725                 push    eax
  65. .text:004AD726                 mov     ecx, [ebp-24h]
  66. .text:004AD729                 push    ecx
  67. .text:004AD72A                 lea     edx, [ebp-6Ch]
  68. .text:004AD72D                 push    edx
  69. .text:004AD72E                 call    ds:rtcSplit
  70. .text:004AD734                 lea     edx, [ebp-6Ch]
  71. .text:004AD737                 lea     ecx, [ebp-38h]
  72. .text:004AD73A                 call    ds:__vbaVarMove
  73. .text:004AD740                 lea     ecx, [ebp-5Ch]
  74. .text:004AD743                 call    ds:__vbaFreeVar
  75. .text:004AD749                 mov     dword ptr [ebp-4], 5
  76. .text:004AD750                 lea     eax, [ebp-38h]
  77. .text:004AD753                 push    eax
  78. .text:004AD754                 call    ds:__vbaRefVarAry
  79. .text:004AD75A                 mov     ecx, [eax]
  80. .text:004AD75C                 push    ecx
  81. .text:004AD75D                 push    1
  82. .text:004AD75F                 call    ds:__vbaUbound
  83. .text:004AD765                 cmp     eax, 5
  84. ; Какая-то проверка
  85. .text:004AD768                 jz      short loc_4AD77D
  86. ; Вернуть в переменную-приемник значение -2
  87. .text:004AD76A                 mov     dword ptr [ebp-4], 6
  88. .text:004AD771                 mov     dword ptr [ebp-48h], 0FFFFFFFEh
  89. ; Переход на выход из функции
  90. .text:004AD778                 jmp     loc_4AD9BB
  91. .text:004AD77D ; -----------------------------------
  92. ; Какие-то следующие проверки
  93. .text:004AD77D loc_4AD77D:
  94. .text:004AD77D                 mov     dword ptr [ebp-4], 9
  95. .text:004AD784                 mov     dword ptr [ebp-74h], 1
  96. ...
Сперва идет большой блок линейного кода, затем какая-то проверка и запись в параметы функции значения 0FFFFFFFEh или же -2 в десятичной системе счисления в случае знакового числа. Ничего не напоминает? А посмотрите повнимательнее на сообщение о неправильной регистрации. Число -2 и есть код ошибки в случае неправильной регистрации, а ниже по коду есть еще несколько похожих блоков, только код возврата другой. Но мы знаем, что код возврата должен быть нулевым. Конечно, можно просто поместить нужный ассемблерный код в начало функции, при этом не забыть откорректировать стек, правильно подобрать значения смещения в стеке и так далее. Вероятность ошибиться растет с каждым действием. Сделаем проще. Патчить будем не начало функции, а первую проверку. Забьем NOP'ами условный переход по адресу 004AD768 и заменим код ошибки на нулевой в команде по адресу 004AD771. Получится вот что:
  1. .text:004AD765                 cmp     eax, 5
  2. .text:004AD768                 nop
  3. .text:004AD769                 nop
  4. .text:004AD76A                 mov     dword ptr [ebp-4], 6
  5. .text:004AD771                 mov     dword ptr [ebp-48h], 0
  6. .text:004AD778                 jmp     loc_4AD9BB
Теперь код возврата всегда будет правильным, и, если я правильно понимаю, программа должна принять любые регистрационные данные. Проверим.

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

После успешной "регистрации" запускаем наш исправленный вариант и проверяем его на работоспособность. Исчезло окно регистрации при старте, в главном окне программы не показывается сообщений о триальном режиме и ограничении по времени записи, а также убран лимит на количество записываемых треков. Перевод системной даты за 14-дневный триальный период также никак не сказывается на работоспособности программы. Цель достигнута, а мы научились работать с неявными вызовами функций.

Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 3545 | Комментариев: 4

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

Комментарии

Отзывы посетителей сайта о статье
Евгений (24.06.2013 в 16:07):
С какой легкостью! Мастер-класс! Очень инересное исследование защиты программы. СПС
brute (18.06.2013 в 06:59):
как оказалось, анализу прог на VB очень способствует "VB Decompiler Pro v9.2"..
ManHunter (17.06.2013 в 21:22):
Смотри код снизу вверх. Это единственная функция, которая не относится к стандартным функциям визуального басица, результат которой сохраняется и затем проверяется. Я эти места специально прокомментировал.
brute (17.06.2013 в 20:27):
как был найден адрес 00521E0A? Функция проверки начинается гораздо раньше, по адресам 00412F1A, 005217E0. Начиная с адреса 005218DD считывается введенный логин..Ида граф не строит, Ctrl+X не работает, алгоритм (что происходит после адреса 005218DD) туманен и очень длинен до вызова сообщения об ошибке.. А тут так просто в два действия.. Как?!

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

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

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