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

Скриншот программы HTML Password Wizard
Программа HTML Password Wizard предназначена для создания защищенных HTML-страниц, содержимое которых отображается только после ввода правильного пароля. Практическую ценность этого я оставлю за рамками этой статьи, тут же будет исследование ее защиты. Проект благополучно скончался, так что даже если у кого-то вдруг возникло бы желание купить эту программу, у него все равно ничего бы не получилось.
Забираем с файлообменника дистрибутив, распаковываем, устанавливаем. Исполняемый файл ничем не упакован, отправляем его на разборку в дизассемблер.

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

Триальное окно в ресурсах
Шаблон триального окна находится в ресурсах исполняемого файла под индексом 205 или 0CDh, если в шестнадцатеричной системе счисления. Поищем в листинге код, где это значение используется, а также чтобы рядом была какая-нибудь функция для работы с ресурсами или диалоговыми окнами. Обнаружится следующий код:
Code (Assembler) : Убрать нумерацию
- .text:0040A2B0 push ebp
- .text:0040A2B1 mov ebp, esp
- .text:0040A2B3 push 0FFFFFFFFh
- .text:0040A2B5 push offset loc_46DC98
- .text:0040A2BA mov eax, large fs:0
- .text:0040A2C0 push eax
- .text:0040A2C1 push ecx
- .text:0040A2C2 mov eax, dword_498608
- .text:0040A2C7 xor eax, ebp
- .text:0040A2C9 push eax
- .text:0040A2CA lea eax, [ebp+var_C]
- .text:0040A2CD mov large fs:0, eax
- .text:0040A2D3 mov [ebp+var_10], ecx
- .text:0040A2D6 mov eax, [ebp+arg_0]
- .text:0040A2D9 push eax
- ; Открыть диалоговое окно с идентификатором 205
- .text:0040A2DA push 0CDh
- .text:0040A2DF mov ecx, [ebp+var_10]
- .text:0040A2E2 call ??0CDialog@@QAE@IPAVCWnd@@@Z
- ; CDialog::CDialog(uint,CWnd *)
- .text:0040A2E7 mov [ebp+var_4], 0
- .text:0040A2EE mov ecx, [ebp+var_10]
- .text:0040A2F1 mov dword ptr [ecx], offset off_4751A4
- .text:0040A2F7 mov ecx, [ebp+var_10]
- .text:0040A2FA add ecx, 74h
- .text:0040A2FD call sub_40A6D0
- .text:0040A302 mov edx, [ebp+var_10]
- .text:0040A305 mov dword ptr [edx+0C8h], 0
- .text:0040A30F mov [ebp+var_4], 0FFFFFFFFh
- .text:0040A316 mov eax, [ebp+var_10]
- .text:0040A319 mov ecx, [ebp+var_C]
- .text:0040A31C mov large fs:0, ecx
- .text:0040A323 pop ecx
- .text:0040A324 mov esp, ebp
- .text:0040A326 pop ebp
- .text:0040A327 retn 4
Code (Assembler) : Убрать нумерацию
- .text:0040DE2A mov ecx, [ebp+var_7B8]
- ; Вызвать функцию проверки
- .text:0040DE30 call sub_40EDD0
- ; Сохранить ее результат в переменной
- .text:0040DE35 mov [ebp+var_818], eax
- ; Загрузить в ECX значение переменной
- .text:0040DE3B mov ecx, [ebp+var_818]
- ; Установить статус зарегистрированности
- .text:0040DE41 mov dword_4999B8, ecx
- .text:0040DE47 cmp dword_4999BC, 0
- .text:0040DE4E jnz short loc_40DE5A
- .text:0040DE50 mov dword_4999B8, 0
- .text:0040DE5A loc_40DE5A:
- ; Программа зарегистрирована?
- .text:0040DE5A cmp dword_4999B8, 0
- .text:0040DE61 jnz loc_40DEEE
- ; Нет, показать триальное окно
- .text:0040DE67 push 0
- .text:0040DE69 lea ecx, [ebp+var_764]
- .text:0040DE6F call sub_40A2B0
- .text:0040DE74 mov byte ptr [ebp+var_4], 12h
- .text:0040DE78 mov edx, dword_4999C4
- .text:0040DE7E push edx
Функция проверки регистрации объемная, но вся мякотка заключается вот в этом коде. Проходить его лучше всего под отладчиком.
Code (Assembler) : Убрать нумерацию
- ; Сформировать строку "htlpassw" из отдельных символов
- .text:0040EE80 mov [ebp+var_11C], 68h
- .text:0040EE87 mov [ebp+var_11B], 74h
- .text:0040EE8E mov [ebp+var_11A], 6Ch
- .text:0040EE95 mov [ebp+var_119], 70h
- .text:0040EE9C mov [ebp+var_118], 61h
- .text:0040EEA3 mov [ebp+var_117], 73h
- .text:0040EEAA mov [ebp+var_116], 73h
- .text:0040EEB1 mov [ebp+var_115], 77h
- .text:0040EEB8 mov [ebp+var_114], 0
- .text:0040EEBF push 0FFh ; size_t
- .text:0040EEC4 lea ecx, [ebp+MultiByteStr]
- .text:0040EECA push ecx ; char *
- .text:0040EECB lea edx, [ebp+var_220]
- .text:0040EED1 push edx ; char *
- .text:0040EED2 call _strncpy
- .text:0040EED7 add esp, 0Ch
- .text:0040EEDA lea eax, [ebp+var_220]
- .text:0040EEE0 push eax ; char *
- .text:0040EEE1 call _strlen
- .text:0040EEE6 add esp, 4
- .text:0040EEE9 mov [ebp+var_324], eax
- .text:0040EEEF mov [ebp+var_8], 0
- .text:0040EEF6 jmp short loc_40EF01
- .text:0040EEF8 ; ---------------------------------------
- .text:0040EEF8 loc_40EEF8:
- ; Цикл формирования строки правильного серийника из регистрационного имени
- .text:0040EEF8 mov ecx, [ebp+var_8]
- .text:0040EEFB add ecx, 1
- .text:0040EEFE mov [ebp+var_8], ecx
- .text:0040EF01 loc_40EF01:
- .text:0040EF01 mov edx, [ebp+var_8]
- .text:0040EF04 cmp edx, [ebp+var_324]
- .text:0040EF0A jge short loc_40EF6C
- .text:0040EF0C mov eax, [ebp+var_8]
- .text:0040EF0F mov cl, [ebp+eax+var_220]
- .text:0040EF16 mov [ebp+var_325], cl
- .text:0040EF1C movsx eax, [ebp+var_325]
- .text:0040EF23 add eax, [ebp+var_8]
- .text:0040EF26 add eax, [ebp+var_324]
- .text:0040EF2C mov edx, [ebp+var_8]
- .text:0040EF2F and edx, 80000007h
- .text:0040EF35 jns short loc_40EF3C
- .text:0040EF37 dec edx
- .text:0040EF38 or edx, 0FFFFFFF8h
- .text:0040EF3B inc edx
- .text:0040EF3C loc_40EF3C:
- .text:0040EF3C movsx ecx, [ebp+edx+var_11C]
- .text:0040EF44 add eax, ecx
- .text:0040EF46 cdq
- .text:0040EF47 mov ecx, 0Ah
- .text:0040EF4C idiv ecx
- .text:0040EF4E add edx, 30h
- .text:0040EF51 mov [ebp+var_1], dl
- .text:0040EF54 mov edx, [ebp+var_324]
- .text:0040EF5A sub edx, 1
- .text:0040EF5D sub edx, [ebp+var_8]
- .text:0040EF60 mov al, [ebp+var_1]
- .text:0040EF63 mov [ebp+edx+var_428], al
- .text:0040EF6A jmp short loc_40EEF8
- .text:0040EF6C ; ---------------------------------------
- .text:0040EF6C loc_40EF6C:
- .text:0040EF6C mov eax, [ebp+var_324]
- .text:0040EF72 add eax, 6Fh
- .text:0040EF75 cdq
- .text:0040EF76 mov ecx, 0Ah
- .text:0040EF7B idiv ecx
- .text:0040EF7D add edx, 30h
- .text:0040EF80 mov eax, [ebp+var_324]
- .text:0040EF86 mov [ebp+eax+var_428], dl
- .text:0040EF8D mov ecx, [ebp+var_324]
- .text:0040EF93 mov [ebp+ecx+var_427], 0
- .text:0040EF9B lea edx, [ebp+var_110]
- ; Занести в стек адреса строки правильного серийника и введенного ранее
- .text:0040EFA1 push edx ; char *
- .text:0040EFA2 lea eax, [ebp+var_428]
- .text:0040EFA8 push eax ; char *
- ; Сравнить строки
- .text:0040EFA9 call _strcmp
- .text:0040EFAE add esp, 8
- .text:0040EFB1 test eax, eax
- .text:0040EFB3 jz short loc_40EFD7

Программа успешно зарегистрирована
А теперь давайте вытащим код генерации правильного серийника. Алгоритм несложный, для удобства я заменю нечитаемые регистры на человекопонятные имена переменных. Код, конечно, далек до оптимального, хоть я его и немного подчистил, но так уж постарался компилятор.
Code (Assembler) : Убрать нумерацию
- ; regname_len - длина строки регистрационного имени
- ; regname_str - строка регистрационного имени
- ; htlpassw - строка "htlpassw"
- ; serial_str - строка формируемого серийника
- .text:0040EEEF mov [position], 0
- .text:0040EEF6 jmp short loc_40EF01
- .text:0040EEF8 ; -------------------------------
- .text:0040EEF8 loc_40EEF8:
- .text:0040EEF8 inc [position]
- .text:0040EF01 loc_40EF01:
- .text:0040EF01 mov edx, [position]
- .text:0040EF04 cmp edx, [regname_len]
- .text:0040EF0A jge loc_done
- .text:0040EF0C mov eax, [position]
- .text:0040EF1C movsx eax, byte [eax+regname_str]
- .text:0040EF23 add eax, [position]
- .text:0040EF26 add eax, [regname_len]
- .text:0040EF2C mov edx, [position]
- .text:0040EF2F and edx, 80000007h
- .text:0040EF35 jns short loc_40EF3C
- .text:0040EF37 dec edx
- .text:0040EF38 or edx, 0FFFFFFF8h
- .text:0040EF3B inc edx
- .text:0040EF3C loc_40EF3C:
- .text:0040EF3C movsx ecx, [edx+htlpassw]
- .text:0040EF44 add eax, ecx
- .text:0040EF46 cdq
- .text:0040EF47 mov ecx, 10
- .text:0040EF4C idiv ecx
- .text:0040EF4E add edx, '0'
- .text:0040EF51 mov [temp2], dl
- .text:0040EF54 mov edx, [regname_len]
- .text:0040EF5A sub edx, 1
- .text:0040EF5D sub edx, [position]
- .text:0040EF60 mov al, [temp2]
- .text:0040EF63 mov [edx+serial_str], al
- .text:0040EF6A jmp short loc_40EEF8
- .text:0040EF6C ; ---------------------------------------
- .text:0040EF6C loc_done:
- .text:0040EF6C mov eax, [regname_len]
- .text:0040EF72 add eax, 6Fh
- .text:0040EF75 cdq
- .text:0040EF76 mov ecx, 10
- .text:0040EF7B idiv ecx
- .text:0040EF7D add edx, '0'
- .text:0040EF80 mov eax, [regname_len]
- .text:0040EF86 mov [eax+serial_str], dl
Просмотров: 1802 | Комментариев: 14
Метки: исследование защиты, HTML

Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
Petya
(01.09.2020 в 12:46):
PAQ8, если вы тут ещё появитесь, как успехи?

PAQ8
(01.02.2020 в 22:39):
ManHunter, благодарствую! Попробую поиграться...

ManHunter
(01.02.2020 в 13:17):
Вот реализация библиотеки zlib на JS. https://github.com/imaya/zlib.js
Можно с ней поиграться.
Можно с ней поиграться.

PAQ8
(01.02.2020 в 12:48):
HTA не открывается в Firefox.
Есть расширение для FF, называется SingleFile. Позволяет сохранять любую страницу в обычном html файле с использованием data URI. Хотелось бы ещё иметь возможность эту страницу как-то упаковать хотя бы LZ4 и встроить туда inline-JS распаковщик, а-ля UPX для html.
Есть расширение для FF, называется SingleFile. Позволяет сохранять любую страницу в обычном html файле с использованием data URI. Хотелось бы ещё иметь возможность эту страницу как-то упаковать хотя бы LZ4 и встроить туда inline-JS распаковщик, а-ля UPX для html.

ManHunter
(01.02.2020 в 01:15):
А чем обычный hta не радует? Ну да, при энтропийных данных размер получится чутарика больше. Но кого это в наше время беспокоит? Нынче решать софтовые проблемы мощностями железа - это прям стильно модно молодежно.

PAQ8
(01.02.2020 в 01:02):
Немного не в тему, но всё же по теме. Ищу прогу, которая будет сжимать HTML страницы и помещать туда JS-распаковщик. Хочу заиметь формат htmZ, но в обычной html упаковке. Аналог серверного сжатия, но без сервера. Аналог однофайлового CHM, но без CHM. Есть ли такое в природе?

ManHunter
(19.01.2020 в 22:13):
Пишешь кейген на ассемблере, вставляешь в него этот код, пользуешься.

Luna ©orporation
(19.01.2020 в 21:44):
"в неизменном виде переносить в кейген."
Это как и куда ?!?
Это как и куда ?!?

xussr
(16.01.2020 в 10:26):
Пример пользительный !!!

Ellephant
(13.01.2020 в 11:49):
в закладке Protect, можно указать кодировку. Пошло. Теперь читается )
Кстати браузер запоминает страницу в расшифрованном виде, и уже при следующем заходе не надо пароль вводить.
Кстати браузер запоминает страницу в расшифрованном виде, и уже при следующем заходе не надо пароль вводить.

ManHunter
(13.01.2020 в 10:49):
Есть такое дело, там надо страницу в win-1251 делать, тогда вроде прокатывает.

Ellephant
(13.01.2020 в 10:45):
попробовал эту программку, при шифровании страницы русский шрифт слетает

ManHunter
(13.01.2020 в 08:49):
После ввода пароля можно в браузере посмотреть исходный код страницы. А так да, реально шифруется по паролю.

Ellephant
(13.01.2020 в 07:04):
А открыть/прочитать саму зашифрованную страницу, созданную этой программой не пробовал?

Добавить комментарий
Заполните форму для добавления комментария
