Blog. Just Blog

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

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

WinCHM - небольшой по размерам, но сильный по возможностям редактор для создания файлов справок в формате CHM, HLP а также PDF- и DOC-файлов. Про все возможности этой замечательной программы во можете почитать на офсайте, и там же вдруг обнаружится ссылка на страницу покупки. Туда ходить не надо, обойдемся своими силами.

Первым делом качаем дистрибутив, устанавливаем, запускаем. Сразу получаем триальное окно:

Триальное окно
Триальное окно

Окей, введем какой-нибудь левак в качестве регистрационного имени и номера. Получаем сообщение "Illegal registration code!". Начальные данные для анализа у нас есть, переходим к стадии кодокопания. Файл ничем не упакован, поэтому сразу загоним его в дизассемблер, а пока тот работает, поищем нехорошую строчку.

Нехорошая строка найдена
Нехорошая строка найдена

Если приглядеться повнимательнее, то там же рядышком есть сообщение об успешной регистрации, а также какие-то интересные строки типа названий ключей реестра для хранения регистрационных данных, строки с вариантами лицензий программы и т.п. В дизассемблере смотрим откуда это все добро запрашивается:
  1. ...
  2. CODE:0051017E                 mov     eax, ebx
  3. CODE:00510180                 call    sub_403574
  4. CODE:00510185                 call    sub_50FB80
  5. ; Проверить, если DWORD по адресу 54B184 имеет не нулевое значение,
  6. ; то программа считается зарегистрированной
  7. CODE:0051018A                 mov     eax, ds:off_54B184
  8. CODE:0051018F                 cmp     dword ptr [eax], 0
  9. CODE:00510192                 jz      short loc_5101EB
  10. ; Сообщение об удачной регистрации
  11. CODE:00510194                 mov     ecx, ds:off_54B184
  12. CODE:0051019A                 mov     ecx, [ecx]
  13. CODE:0051019C                 lea     eax, [ebp+var_4]
  14. CODE:0051019F                 mov     edx, offset aThankYouForPur
  15. ; "Thank you for purchasing our product!\r\n"...
  16. CODE:005101A4                 call    sub_404670
  17. CODE:005101A9                 mov     eax, ds:off_54B4E8
  18. CODE:005101AE                 cmp     dword ptr [eax], 1
  19. CODE:005101B1                 jnz     short loc_5101CF
  20. CODE:005101B3                 push    [ebp+var_4]
  21. CODE:005101B6                 push    offset dword_5102B8
  22. CODE:005101BB                 push    offset aVersionWinchmP
  23. ; "Version: WinCHM Pro"
  24. CODE:005101C0                 lea     eax, [ebp+var_4]
  25. CODE:005101C3                 mov     edx, 3
  26. CODE:005101C8                 call    sub_4046E4
  27. CODE:005101CD                 jmp     short loc_5101F8
  28. CODE:005101CF ; ------------------------------------------------------
  29. CODE:005101CF loc_5101CF:
  30. CODE:005101CF                 push    [ebp+var_4]
  31. CODE:005101D2                 push    offset dword_5102B8
  32. CODE:005101D7                 push    offset aVersionWinchmS
  33. ; "Version: WinCHM (Standard)"
  34. CODE:005101DC                 lea     eax, [ebp+var_4]
  35. CODE:005101DF                 mov     edx, 3
  36. CODE:005101E4                 call    sub_4046E4
  37. CODE:005101E9                 jmp     short loc_5101F8
  38. CODE:005101EB ; ------------------------------------------------------
  39. ; Сообщение о некорректной регистрации
  40. CODE:005101EB loc_5101EB:
  41. CODE:005101EB                 lea     eax, [ebp+var_4]
  42. CODE:005101EE                 mov     edx, offset aIllegalRegistr
  43. ; Указатель на строку "Illegal registration code!"
  44. CODE:005101F3                 call    sub_4043E0
  45. ...
Тут вроде все понятно. Если DWORD по адресу 54B184 имеет нулевое значение, то программа работает в триальном режиме. Теперь надо найти в коде то место, где эта ячейка памяти инициализируется правильным ненулевым значением. Дизассемблер показывает, что ссылок на эту ячейку памяти очень много, и практически все они так или иначе связаны с проверкой ее значения. Все, кроме одного блока:
  1. ...
  2. CODE:0050FC75                 mov     eax, [ebp+var_4]
  3. CODE:0050FC78                 call    sub_403574
  4. ; Загрузить в регистр EDX ссылку на регистрационный код из реестра
  5. CODE:0050FC7D                 mov     edx, ds:off_54B33C
  6. CODE:0050FC83                 mov     edx, [edx]
  7. CODE:0050FC85                 mov     eax, ds:off_54B5E8
  8. ; Загрузить в регистр EAX ссылку на регистрационное имя из реестра
  9. CODE:0050FC8A                 mov     eax, [eax]
  10. ; Передать результаты в какую-то функцию проверки
  11. CODE:0050FC8C                 call    sub_50F3B4
  12. ; Результат проверки вернулся в регистре EAX
  13. CODE:0050FC91                 mov     ebx, eax
  14. CODE:0050FC93                 mov     eax, ebx
  15. ; Если EAX был = 0, то выполнится следующий условный переход
  16. CODE:0050FC95                 sub     eax, 1
  17. CODE:0050FC98                 jb      short loc_50FCA5
  18. ; Если EAX было = 1, то будет переход на Single-user License
  19. CODE:0050FC9A                 jz      short loc_50FCB1
  20. ; Если EAX было = 270Eh, то переход на Unlimited-user License
  21. CODE:0050FC9C                 sub     eax, 270Eh
  22. CODE:0050FCA1                 jz      short loc_50FCC2
  23. ; Иначе переход на NN-user License
  24. CODE:0050FCA3                 jmp     short loc_50FCD3
  25. CODE:0050FCA5 ; -------------------------------------------------
  26. CODE:0050FCA5 loc_50FCA5:
  27. CODE:0050FCA5                 mov     eax, ds:off_54B184
  28. CODE:0050FCAA                 call    sub_404348
  29. CODE:0050FCAF                 jmp     short loc_50FCEF
  30. CODE:0050FCB1 ; -------------------------------------------------
  31. CODE:0050FCB1 loc_50FCB1:
  32. CODE:0050FCB1                 mov     eax, ds:off_54B184
  33. CODE:0050FCB6                 mov     edx, offset aSingleUserLice
  34. ; "Single-user License"
  35. CODE:0050FCBB                 call    sub_40439C
  36. CODE:0050FCC0                 jmp     short loc_50FCEF
  37. CODE:0050FCC2 ; -------------------------------------------------
  38. CODE:0050FCC2 loc_50FCC2:
  39. CODE:0050FCC2                 mov     eax, ds:off_54B184
  40. CODE:0050FCC7                 mov     edx, offset aUnlimitedUserL
  41. ; "Unlimited-user License"
  42. CODE:0050FCCC                 call    sub_40439C
  43. CODE:0050FCD1                 jmp     short loc_50FCEF
  44. CODE:0050FCD3 ; -------------------------------------------------
  45. CODE:0050FCD3 loc_50FCD3:
  46. CODE:0050FCD3                 lea     edx, [ebp+var_18]
  47. CODE:0050FCD6                 mov     eax, ebx
  48. CODE:0050FCD8                 call    sub_4094E8
  49. CODE:0050FCDD                 mov     edx, [ebp+var_18]
  50. CODE:0050FCE0                 mov     eax, ds:off_54B184
  51. CODE:0050FCE5                 mov     ecx, offset aUserLicense
  52. ; "-user License"
  53. CODE:0050FCEA                 call    sub_404670
  54. CODE:0050FCEF loc_50FCEF:
  55. CODE:0050FCEF                 mov     eax, ds:off_54B184
  56. CODE:0050FCF4                 cmp     dword ptr [eax], 0
  57. CODE:0050FCF7                 jnz     short loc_50FD0A
  58. CODE:0050FCF9                 mov     eax, ds:off_54B3B4
  59. CODE:0050FCFE                 mov     edx, offset aUnregistered
  60. ; " [Unregistered]"
  61. CODE:0050FD03                 call    sub_40439C
  62. CODE:0050FD08                 jmp     short loc_50FD14
  63. ...
Интересного много, но логика работы не совсем понятна. Попробуем под отладчиком поставить точку остановка на адрес 0050FC75 и пройти код в пошаговом режиме. Я прокомментировал приведенный выше код на основании данных из отладчика, обратите внимание на условные переходы по адресу 0050FC93 и дальше. Ситуация потихоньку проясняется. Получается, что регистрационные данные передаются в процедуру по адресу 50F3B4, если она вернула ненулевое значение, то программа считается зарегистрированной. Возвращаемся к дизассемблеру:
  1. CODE:0050F3B4                 push    ebp
  2. CODE:0050F3B5                 mov     ebp, esp
  3. CODE:0050F3B7                 mov     ecx, 4
  4. CODE:0050F3BC loc_50F3BC:
  5. CODE:0050F3BC                 push    0
  6. CODE:0050F3BE                 push    0
  7. CODE:0050F3C0                 dec     ecx
  8. CODE:0050F3C1                 jnz     short loc_50F3BC
  9. CODE:0050F3C3                 push    ecx
  10. CODE:0050F3C4                 push    ebx
  11. CODE:0050F3C5                 mov     [ebp+var_8], edx
  12. CODE:0050F3C8                 mov     [ebp+var_4], eax
  13. CODE:0050F3CB                 mov     eax, [ebp+var_4]
  14. CODE:0050F3CE                 call    sub_404814
  15. CODE:0050F3D3                 mov     eax, [ebp+var_8]
  16. CODE:0050F3D6                 call    sub_404814
  17. CODE:0050F3DB                 xor     eax, eax
  18. CODE:0050F3DD                 push    ebp
  19. CODE:0050F3DE                 push    offset loc_50F9B9
  20. CODE:0050F3E3                 push    dword ptr fs:[eax]
  21. CODE:0050F3E6                 mov     fs:[eax], esp
  22. CODE:0050F3E9                 lea     eax, [ebp+var_14]
  23. CODE:0050F3EC                 mov     edx, [ebp+var_8]
  24. CODE:0050F3EF                 call    sub_4043E0
  25. CODE:0050F3F4                 mov     eax, [ebp+var_14]
  26. CODE:0050F3F7                 call    sub_404624
  27. CODE:0050F3FC                 mov     ebx, eax
  28. CODE:0050F3FE                 mov     eax, ds:off_54B4E8
  29. CODE:0050F403                 mov     dword ptr [eax], 0FFFFFFFFh
  30. ; В регистр EAX занести ссылку на серийный номер
  31. CODE:0050F409                 mov     eax, [ebp+var_14]
  32. ; Вызвать очередную функцию проверки
  33. CODE:0050F40C                 call    sub_50EBD0
  34. ; Результат функции возвращается в регистрах EDX и EAX
  35. CODE:0050F411                 mov     [ebp+var_10], eax
  36. CODE:0050F414                 mov     [ebp+var_C], edx
  37. ; Если EDX не равен 0, то перейти к следующей проверке
  38. CODE:0050F417                 cmp     [ebp+var_C], 0
  39. CODE:0050F41B                 jnz     short loc_50F430
  40. ; Если EAX=2B77h, то результат всей функции будет 1
  41. CODE:0050F41D                 cmp     [ebp+var_10], 2B77h
  42. CODE:0050F424                 jnz     short loc_50F430
  43. CODE:0050F426                 mov     ebx, 1
  44. CODE:0050F42B                 jmp     loc_50F991
  45. CODE:0050F430 ; ----------------------------------------------
  46. CODE:0050F430 loc_50F430:
  47. ; Если EDX не равен 0, то перейти к следующей проверке
  48. CODE:0050F430                 cmp     [ebp+var_C], 0
  49. CODE:0050F434                 jnz     short loc_50F449
  50. ; Если EAX=2B81h, то результат всей функции будет 2
  51. CODE:0050F436                 cmp     [ebp+var_10], 2B81h
  52. CODE:0050F43D                 jnz     short loc_50F449
  53. CODE:0050F43F                 mov     ebx, 2
  54. CODE:0050F444                 jmp     loc_50F991
  55. CODE:0050F449 ; ----------------------------------------------
  56. CODE:0050F449 loc_50F449:
  57. ; Если EDX не равен 0, то перейти к следующей проверке
  58. CODE:0050F449                 cmp     [ebp+var_C], 0
  59. CODE:0050F44D                 jnz     short loc_50F462
  60. ; Если EAX=2EF2h, то результат всей функции будет 3
  61. CODE:0050F44F                 cmp     [ebp+var_10], 2EF2h
  62. CODE:0050F456                 jnz     short loc_50F462
  63. CODE:0050F458                 mov     ebx, 3
  64. CODE:0050F45D                 jmp     loc_50F991
  65. CODE:0050F462 ; ----------------------------------------------
  66. CODE:0050F462 loc_50F462:
  67. ; Если EDX не равен 0, то перейти к следующей проверке
  68. CODE:0050F462                 cmp     [ebp+var_C], 0
  69. CODE:0050F466                 jnz     short loc_50F47B
  70. ; Если EAX=2FA7h, то результат всей функции будет 5
  71. CODE:0050F468                 cmp     [ebp+var_10], 2FA7h
  72. CODE:0050F46F                 jnz     short loc_50F47B
  73. CODE:0050F471                 mov     ebx, 5
  74. CODE:0050F476                 jmp     loc_50F991
  75. CODE:0050F47B ; ----------------------------------------------
  76. CODE:0050F47B loc_50F47B:
  77. ; Если EDX не равен 0, то перейти к следующей проверке
  78. CODE:0050F47B                 cmp     [ebp+var_C], 0
  79. CODE:0050F47F                 jnz     short loc_50F494
  80. ; Если EAX=2D80h, то результат всей функции будет 10
  81. CODE:0050F481                 cmp     [ebp+var_10], 2D80h
  82. CODE:0050F488                 jnz     short loc_50F494
  83. CODE:0050F48A                 mov     ebx, 0Ah
  84. CODE:0050F48F                 jmp     loc_50F991
  85. CODE:0050F494 ; ----------------------------------------------
  86. CODE:0050F494 loc_50F494:
  87. ; Если EDX не равен 0, то перейти к следующей проверке
  88. CODE:0050F494                 cmp     [ebp+var_C], 0
  89. CODE:0050F498                 jnz     short loc_50F4AD
  90. ; Если EAX=2CE2h, то результат всей функции будет 20
  91. CODE:0050F49A                 cmp     [ebp+var_10], 2CE2h
  92. CODE:0050F4A1                 jnz     short loc_50F4AD
  93. CODE:0050F4A3                 mov     ebx, 14h
  94. CODE:0050F4A8                 jmp     loc_50F991
  95. CODE:0050F4AD ; ----------------------------------------------
  96. CODE:0050F4AD loc_50F4AD:
  97. ; Если EDX не равен 0, то перейти к следующей проверке
  98. CODE:0050F4AD                 cmp     [ebp+var_C], 0
  99. CODE:0050F4B1                 jnz     short loc_50F4C6
  100. ; Если EAX=388Ah, то результат всей функции будет 50
  101. CODE:0050F4B3                 cmp     [ebp+var_10], 388Ah
  102. CODE:0050F4BA                 jnz     short loc_50F4C6
  103. CODE:0050F4BC                 mov     ebx, 32h
  104. CODE:0050F4C1                 jmp     loc_50F991
  105. CODE:0050F4C6 ; ----------------------------------------------
  106. CODE:0050F4C6 loc_50F4C6:
  107. ; Если EDX не равен 0, то перейти к следующей проверке
  108. CODE:0050F4C6                 cmp     [ebp+var_C], 0
  109. CODE:0050F4CA                 jnz     short loc_50F4DF
  110. ; Если EAX=25C1h, то результат всей функции будет 270Fh
  111. CODE:0050F4CC                 cmp     [ebp+var_10], 25C1h
  112. CODE:0050F4D3                 jnz     short loc_50F4DF
  113. CODE:0050F4D5                 mov     ebx, 270Fh
  114. CODE:0050F4DA                 jmp     loc_50F991
  115. ...
Что мы имеем? На основе серийного номера проводятся расчеты, и, в зависимости от этого результата, возвращается количество лицензий. Как видно из кода, их может быть 1, 2, 3, 5, 10, 20, 50 и Unlimited. Осталось выяснить какие же это расчеты. Идем обратно в отладчик, при этом ставим точку останова на вызов функции 50EBD0. Расчет заветного числа выполняется следующим образом:
  1. ...
  2. ; Счетчик символов
  3. CODE:0050EC1C                 mov     ecx, 1
  4. CODE:0050EC21 loc_50EC21:
  5. ; Указатель на серийный номер
  6. CODE:0050EC21                 mov     eax, [ebp+var_14]
  7. ; Записать в EAX символ из серийного номера на позиции ECX
  8. CODE:0050EC24                 movzx   eax, byte ptr [eax+ecx-1]
  9. ; Умножить его код на значение ECX
  10. CODE:0050EC29                 imul    ecx
  11. CODE:0050EC2B                 cdq
  12. ; Прибавить к счетчику
  13. CODE:0050EC2C                 add     eax, [ebp+var_20]
  14. CODE:0050EC2F                 adc     edx, [ebp+var_1C]
  15. CODE:0050EC32                 mov     [ebp+var_20], eax
  16. CODE:0050EC35                 mov     [ebp+var_1C], edx
  17. ; Взять следующий символ
  18. CODE:0050EC38                 inc     ecx
  19. CODE:0050EC39                 dec     ebx
  20. ; Достигнут конец серийника?
  21. CODE:0050EC3A                 jnz     short loc_50EC21
  22. ...
Ну вот и настал момент истины, мы добрались до алгоритма проверки серийного номера. Регистрационное имя любое, лишь бы не пустое, длина серийника тоже произвольная. Основная проверка заключается в следующем: код символа умножается на его позицию, причем счетчик начинается с 1. Результаты всех умножений суммируются. Затем проверяется итоговая сумма, она должна быть одним из следующих значений: 2B77h - 1 лицензия, 2B81h - 2 лицензии, 2EF2h - 3 лицензии, 2FA7h - 5 лицензий, 2D80h - 10 лицензий, 2CE2h - 20 лицензий, 388Ah - 50 лицензий, 25C1h - неограниченное количество лицензий. Например, валидным серийником будет строка MTXSMZTGYKSNFWQ. Судя по варезным релизам, константы в некоторых версиях меняются, но суть алгоритма остается неизменной. Кейген под любой тип лицензий пишется на основе простейшего брутфорса, теперь вы можете сделать это самостоятельно. Очередная защита повержена, спасибо автору за программу и за интересную головоломку.

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

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

Комментарии

Отзывы посетителей сайта о статье
Isaev (22.09.2011 в 18:27):
Да константы то шут с ними, константа для "неограниченное количество лицензий" она всё равно для версии Standard. Та проверка на
CODE:005101A9                 mov     eax, ds:off_54B4E8
CODE:005101AE                 cmp     dword ptr [eax], 1
CODE:005101B1                 jnz     short loc_5101CF
обламывается...

399F она в 4.21
ManHunter (22.09.2011 в 16:54):
В 4.21 опять константы сменили, кейген не подходит
Isaev (22.09.2011 в 16:51):
Похоже с таким подходом мы получаем "Version: WinCHM (Standard)", а Pro не получается )
mrbelyash (17.08.2011 в 18:05):
Жаль что не Help&Manual ;(
ManHunter (16.08.2011 в 09:36):
Isaev, путаешь, сколько я ее помню, она всегда без упаковщика шла. По молодости я ее тоже патчами до ума доводил.
Isaev (16.08.2011 в 02:23):
На счёт битхаков это вы зря :)
Хороший разбор! А константы можно дёргать из exe если совсем уж хочется универсально ))
Раньше она под аспротом вроде была... или путаю чего
ManHunter (15.08.2011 в 23:12):
У 1С это хорошо получается. Сперва подсадили всех на варез, потом всех закошмарили санкциями и сняли бабло за лицензирование + регулярный и гарантированный съем бабла за поддержку. Еще мне нравится как работает MySQL: все бесплатно, но если хотите внеочередную фичу конкретно под свои нужды - бабло в кассу. А вообще для корпоративного и узкоспециализированного софта я ничего не имею против оплаты и платной поддержки, здесь расходуются деньги компаний, а не пользователей. Персональный софт должен быть максимум donationware.
Wirtmih (15.08.2011 в 21:00):
Я не автор программы, но всё-таки спрошу. За корпоративный софт денег требовать можно или тоже, по вашему мнению, лучше зарабатывать на его поддержке?
AyTkACT (14.08.2011 в 22:21):
ManHunter, ну дык не все программы для людей пишут. А если уж так, то лучше как уже писал ниже.
ManHunter (14.08.2011 в 19:27):
Бабло надо зарабатывать на работе, а программы писать бесплатно, для души и для людей.
AyTkACT (14.08.2011 в 15:17):
Ввиду того что проверка идёт по константам, а константы меняются от версии или даже от билда - имхо, самое кучерявое в данном случае - битхак.
В очередной раз удивляюсь крутости алго проверок.
з.ы. Лучший вариант коммерциализации софта является добровольные пожертвования или акции по раскрутки сабжа а ля _http://adguard.ru/license.html#free
Dimas (14.08.2011 в 06:31):
А я по старинке бит хаком - замена двух прыжков и получил портабл Unlimited версию размером 3,5 метра.
ЗЫ сегодня проскочила прожка AP Tuner - очень обрадовался увидев - PCL.NFO ;-)

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

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

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