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

Программа NTFS Streams Info предназначена для просмотра и редактирования альтернативных потоков NTFS. Не самый удобный инструмент, тот же NTFS Stream Explorer гораздо лучше и по возможностям, и по интерфейсу, да к тому же и бесплатный. Как бы то ни было, проект давно прекратил свое существование, на офсайте уже совсем другая информация. Хороший повод, чтобы потренироваться в некромантии и обратной инженерии.

В связи с закрытием разработки, ссылок на закачу на бывшем офсайте больше нет, но глобальный интернет-архив еще хранит данные о самой последней версии. Забираем оттуда дистрибутив, устанавливаем: http://web.archive.org/web/20051124144929/http://www.isgeo.kiev.ua/shareware/NTFSInfoSetup.exe

http://s019.radikal.ru/i642/1702/4a/d2a35ef2a630.png

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

На ввод левого серийника программа реагирует характерным сообщением. Исполняемый файл ничем не упакован, поэтому отправим его в дизассемблер, а пока поищем строку сообщения.

http://s010.radikal.ru/i313/1702/b5/6187042d6cb0.png

Строка в файле

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

Но запустить программу под отладчиком обычным способом не получается. На каждый чих она сыплет необрабатываемые исключения и при попытке запуска под отладчиком валится с ошибкой памяти, не доходя до проверки регистрации. Это срабатывает защита от взлома. Но на каждую хитрую жопу есть хуй с винтом, в нашем случае это возможность приаттачить отладчик к работающему процессу. Запускаем программу в обычном режиме, ждем появления окна регистрации. Аттачим отладчик к процессу и ставим точку останова на начало функции проверки. Отпускаем программу на выполнение. Теперь никаких исключений нет (пока что), после ввода серийника срабатывает точка останова и мы можем пройти все проверки в пошаговом режиме. Поехали.

Цитата:

CODE:0049553D mov eax, [ebp+var_8]
CODE:00495540 call sub_404B44
CODE:00495545 cmp eax, 13h
CODE:00495548 jz short loc_495560
CODE:0049554A mov ecx, offset _str_Invalid_registr.Text
CODE:0049554F mov dl, 1
CODE:00495551 mov eax, off_4953F0

Первая проверка. Длина серийного номера должна быть 13h (19 в десятичной системе) символов. И тут же первый сюрприз от разработчика: в каждое поле для ввода частей серийника помещается 5 символов, а согласно этой проверке должно быть 4 (4 группы по 4 символа + 3 разделителя = 19).

Цитата:

CODE:0049559F mov [ebp+var_1C], 1
CODE:004955A6 loc_4955A6:
CODE:004955A6 lea eax, [ebp+var_28]
CODE:004955A9 push eax
CODE:004955AA mov ecx, 1
CODE:004955AF mov edx, [ebp+var_1C]
CODE:004955B2 mov eax, [ebp+var_14]
CODE:004955B5 call unknown_libname_592
CODE:004955BA mov eax, [ebp+var_28]
CODE:004955BD movzx eax, byte ptr [eax]
CODE:004955C0 add [ebp+var_18], eax
CODE:004955C3 inc [ebp+var_1C]
CODE:004955C6 cmp [ebp+var_1C], 5
CODE:004955CA jnz short loc_4955A6

Суммируются 4 символа первого блока. В дальнейшем к этой сумме будут добавляться и другие части серийника, но всему свое время. Для удобства назовем эту сумму накопителем.

Цитата:

CODE:004955E5 mov al, [eax]
CODE:004955E7 sub al, 47h
CODE:004955E9 jz short loc_49560D
CODE:004955EB sub al, 5
CODE:004955ED jz short loc_49560D
CODE:004955EF sub al, 6
CODE:004955F1 jz short loc_49560D
CODE:004955F3 sub al, 4
CODE:004955F5 jz short loc_49560D
CODE:004955F7 mov ecx, offset _str_Invalid_registr.Text
CODE:004955FC mov dl, 1
CODE:004955FE mov eax, off_407CF4

Начинается проверка первой группы. Второй символ должен иметь код 47h, (47h+5), (47h+5+6) или (47h+5+6+4), то есть "G", "L", "R", или "V".

Цитата:

CODE:00495631 mov eax, [ebp+var_30]
CODE:00495634 mov al, [eax]
CODE:00495636 cmp al, 44h
CODE:00495638 jbe short loc_4956A5
CODE:0049563A lea eax, [ebp+var_38]
CODE:0049563D push eax
CODE:0049563E mov ecx, 1
CODE:00495643 mov edx, 4
CODE:00495648 mov eax, [ebp+var_14]
CODE:0049564B call unknown_libname_592
CODE:00495650 mov eax, [ebp+var_38]
CODE:00495653 mov al, [eax]
CODE:00495655 cmp al, 4Fh
CODE:00495657 jnb short loc_4956A5
CODE:00495659 lea eax, [ebp+var_3C]
CODE:0049565C push eax
CODE:0049565D lea eax, [ebp+var_40]
CODE:00495660 mov edx, [ebp+var_14]

4-й символ первой группы должен находиться в интервале 45h..4Eh, то есть от "E" до "N".

Цитата:

CODE:0049567D mov eax, [ebp+var_3C]
CODE:00495680 mov al, [eax]
CODE:00495682 cmp al, 50h
CODE:00495684 jb short loc_4956A5
CODE:00495686 lea eax, [ebp+var_44]
CODE:00495689 push eax
CODE:0049568A mov ecx, 1
CODE:0049568F mov edx, 3
CODE:00495694 mov eax, [ebp+var_14]
CODE:00495697 call unknown_libname_592
CODE:0049569C mov eax, [ebp+var_44]
CODE:0049569F mov al, [eax]
CODE:004956A1 cmp al, 59h
CODE:004956A3 jbe short loc_4956BD

3-й символ первой группы должен находиться в интервале 50h..59h, то есть от "P" до "Y".

Цитата:

CODE:004956EC push offset loc_495929
CODE:004956F1 push dword ptr fs:[eax]
CODE:004956F4 mov fs:[eax], esp
CODE:004956F7 lea ecx, [ebp+var_14]
CODE:004956FA mov edx, [ebp+var_C]
CODE:004956FD sub edx, 42h
CODE:00495700 mov eax, [ebp+var_10]
CODE:00495703 mov ebx, [eax]
CODE:00495705 call dword ptr [ebx+0Ch]
CODE:00495708 mov [ebp+var_1C], 1

1-й символ серийника проверяется очень замысловато: из его кода вычитается 42h, затем выполняется какая-то операция типа чтения из памяти, которая при попытке трассировки под отладчиком выбрасывает исключение, если значение получилось отрицательным. То есть код первого символа должен быть 43h и больше, например, символ "С".

Цитата:

CODE:00495750 mov bl, [eax]
CODE:00495752 lea eax, [ebp+var_58]
CODE:00495755 push eax
CODE:00495756 mov ecx, 1
CODE:0049575B mov edx, 1
CODE:00495760 mov eax, [ebp+var_14]
CODE:00495763 call unknown_libname_592
CODE:00495768 mov eax, [ebp+var_58]
CODE:0049576B movzx eax, byte ptr [eax]
CODE:0049576E add eax, 6
CODE:00495771 cmp ebx, eax
CODE:00495773 jnz short loc_4957C3
CODE:00495775 lea eax, [ebp+var_5C]
CODE:00495778 push eax

Проверяем вторую группу серийника. 3-й символ второй группы должен равняться первому + 6.

Цитата:

CODE:0049579C mov dl, [edx+1]
CODE:0049579F call unknown_libname_77
CODE:004957A4 mov eax, [ebp+var_64]
CODE:004957A7 mov ecx, 1
CODE:004957AC mov edx, 1
CODE:004957B1 call unknown_libname_592
CODE:004957B6 mov eax, [ebp+var_60]
CODE:004957B9 movzx eax, byte ptr [eax]
CODE:004957BC add eax, 2
CODE:004957BF cmp ebx, eax
CODE:004957C1 jz short loc_4957DB

4-й символ второй группы должен равняться второму + 2.

Цитата:

CODE:00495708 mov [ebp+var_1C], 1
CODE:0049570F loc_49570F:
CODE:0049570F lea eax, [ebp+var_50]
CODE:00495712 push eax
CODE:00495713 mov ecx, 1
CODE:00495718 mov edx, [ebp+var_1C]
CODE:0049571B mov eax, [ebp+var_14]
CODE:0049571E call unknown_libname_592
CODE:00495723 mov eax, [ebp+var_50]
CODE:00495726 movzx eax, byte ptr [eax]
CODE:00495729 add [ebp+var_18], eax
CODE:0049572C inc [ebp+var_1C]
CODE:0049572F cmp [ebp+var_1C], 5
CODE:00495733 jnz short loc_49570F

К накопителю поочередно прибавляется вторая группа символов серийника.

Цитата:

CODE:004957F0 mov [ebp+var_1C], 1
CODE:004957F7 loc_4957F7:
CODE:004957F7 lea eax, [ebp+var_68]
CODE:004957FA push eax
CODE:004957FB lea eax, [ebp+var_6C]
CODE:004957FE mov edx, [ebp+var_14]
CODE:00495801 mov ecx, [ebp+var_1C]
CODE:00495804 mov dl, [edx+ecx-1]
CODE:00495808 call unknown_libname_77
CODE:0049580D mov eax, [ebp+var_6C]
CODE:00495810 mov ecx, 1
CODE:00495815 mov edx, 1
CODE:0049581A call unknown_libname_592
CODE:0049581F mov eax, [ebp+var_68]
CODE:00495822 movzx eax, byte ptr [eax]
CODE:00495825 add [ebp+var_18], eax
CODE:00495828 inc [ebp+var_1C]
CODE:0049582B cmp [ebp+var_1C], 5
CODE:0049582F jnz short loc_4957F7

Третья группа вообще никак не проверяется, просто прибавляется к накопителю. Тут могут быть любые символы.

Цитата:

CODE:0049584A mov edx, 3
CODE:0049584F mov eax, [ebp+var_14]
CODE:00495852 call unknown_libname_592
CODE:00495857 mov eax, [ebp+var_70]
CODE:0049585A movzx eax, byte ptr [eax]
CODE:0049585D add [ebp+var_18], eax
CODE:00495860 lea eax, [ebp+var_74]
CODE:00495863 push eax
CODE:00495864 mov ecx, 1
CODE:00495869 mov edx, 1
CODE:0049586E mov eax, [ebp+var_14]
CODE:00495871 call unknown_libname_592
CODE:00495876 mov eax, [ebp+var_74]
CODE:00495879 movzx eax, byte ptr [eax]
CODE:0049587C add [ebp+var_18], eax

Переходим к четветой, последней группе серийного номера. К накопителю прибавляется первый и третий символ группы.

Цитата:

CODE:0049587F mov eax, [ebp+var_18]
CODE:00495882 mov ecx, 0Ah
CODE:00495887 cdq
CODE:00495888 idiv ecx
CODE:0049588A mov [ebp+var_20], edx


CODE:004958BB mov eax, [ebp+var_78]
CODE:004958BE movzx eax, byte ptr [eax]
CODE:004958C1 mov edx, [ebp+var_20]
CODE:004958C4 add edx, 41h
CODE:004958C7 cmp eax, edx
CODE:004958C9 jnz short loc_4958F1

Накопитель делится на 10, к остатку прибавляется 41h. Четвертый символ последней группы должен равняться этому результату.

Цитата:

CODE:0049588D mov eax, [ebp+var_18]
CODE:00495890 mov ecx, 64h
CODE:00495895 cdq
CODE:00495896 idiv ecx
CODE:00495898 mov eax, edx
CODE:0049589A mov ecx, 0Ah
CODE:0049589F cdq
CODE:004958A0 idiv ecx
CODE:004958A2 mov [ebp+var_24], eax


CODE:004958E1 mov eax, [ebp+var_7C]
CODE:004958E4 movzx eax, byte ptr [eax]
CODE:004958E7 mov edx, [ebp+var_24]
CODE:004958EA add edx, 41h
CODE:004958ED cmp eax, edx
CODE:004958EF jz short loc_495909

Накопитель делится на 100, остаток делится на 10, к финальному частному прибавляется 41h. Второй символ последней группы должен равняться этому результату.

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

Вооружившися калькулятором, можно подсчитать правильный серийник по описанному алгоритму. Например, валидным серийником должен быть "CGQE-0062-PCLX-MEHI". Проверим. Вводим серийник на запрос программы, никаких сообщений о неправильной регистрации не появляется, сразу открывается главное окно программы. Выходим. Снова запускаем. На этот раз о регистрации ни слова, программа сразу работает как надо. Цель достигнута. Кейген вы можете написать самостоятельно.

• Author: ManHunter

Оставьте комментарий