gifts2017

Простые примеры реализации демо-версий обработок на платформе «1С:Предприятие 8».

Опубликовал Андрей Скляров (coder1cv8) в раздел Программирование - Практика программирования

Каждый из тех кто продает свой интеллектуальный труд, не раз сталкивался с необходимостью создания демо-версии разработки, дабы продемонстрировать клиенту функциональность, но при этом сохранить для клиента потребность в приобретении полнофункциональной версии. В этой статье я хотел бы рассмотреть несколько не сложных примеров создания демо-версий обработок/отчетов для платформы «1С:Предприятие 8».
Перед тем как приступить, собственно, к сути вопроса, хотелось бы сделать небольшое отступление для «крутых хакеров». Как известно, штатные возможности 1С, не представляют достаточно надежных средств для защиты исходного кода, поэтому приведенные здесь примеры – это исключительно защита от ПОЛЬЗОВАТЕЛЯ и ничего более.
Да и в целом, по моему глубокому убеждению, открытость кода в 1С – это одно из важнейших (если не самое важное) её достоинств. Поэтому я сторонник «установки пароля на модуль, поставки без исходного текста», только в случае демо-версии разработки. А при приобретении обработки, клиент приобретает её ЦЕЛИКОМ, в том числе и исходный код.

Для начала, немного общих моментов. Для того что бы ограничить использование нашего «уникального» функционала, будь-то алгоритм проведения документа или процедура формирования отчета, необходимо вынести код в модуль объекта и скрыть его от пользователя. Это можно достичь двумя путями. Во-первых установка пароля на модуль объекта. Что бы установить пароль, откройте модуль объекта, далее в меню «Текст» выберите пункт «Установить пароль». Во-вторых поставка без исходного кода. Что бы получить обработку/отчет без исходного кода, необходимо сначала создать поставку включающую в себя наш отчет без исходного кода, а затем сохранить его как внешний. Настройка поставки производится в диалоге «Конфигурация > Поставка конфигурации > Настройка поставки».

Итак, пример № 1. Ограничение по времени использования. Заключается в том, что функционал работает в течении определенного, ограниченного времени, например 10 дней. Для этого, нам надо как-то «запомнить» дату первого запуска, сделаем это с помощью реестра Windows.
Функция СрокДемоЗакончился()
	Перем Значение;
	RegProv=ПолучитьCOMОбъект("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv");
	// 2147483649 - раздел реестра HKEY_CURRENT_USER 
	RegProv.GetStringValue("2147483649","Software\1C\1Cv8\Report","StartDate",Значение);
	Если Значение=NULL Тогда	// Ключ ещё не создан, считаем этот запуск первым
		// Создадим ключ, установив значение в текущую дату
		RegProv.CreateKey("2147483649","Software\1C\1Cv8\Report");	// создание раздела
		// установка значения для ключа
		RegProv.SetStringValue("2147483649","Software\1C\1Cv8\Report","StartDate",Строка(Формат(ТекущаяДата(),"ДФ=ггггММддЧЧммсс")));
		Возврат Ложь;
	Иначе
		// проверка срока использования демо-версии
		ДатаСтарта=Дата(Значение);
		КонецПериода=ДатаСтарта+60*60*24*10;	// 10 дней
		ТекДата=ТекущаяДата();
		Возврат НЕ (ТекДата>ДатаСтарта И ТекДата<=КонецПериода);
	КонецЕсли;	
КонецФункции

Таким образом, осталось только вставить проверку на продолжение работы в нашу основную процедуру, например:
Процедура СформироватьОтчет() Экспорт
	Если СрокДемоЗакончился() Тогда
		Возврат;	
	КонецЕсли;
	…
КонецПроцедуры 

Для особо хитрых пользователей, можно сделать запрос точного времени из Интернета, что бы защититься от изменения системного времени. Например, так:
Функция ТочноеВремяПоГринвичу()
	XMLHTTP=Новый COMОбъект("MSXML2.XMLHTTP");
	XMLHTTP.Open("get","http://ntp.greenwichmeantime.com/time/scripts/clock-7/x.php",Ложь);
    	XMLHTTP.Send();
	UTC=Цел(XMLHTTP.Responsetext/1000);	// в секундах
	Возврат '19700101000000'+UTC+60*60*4;	// по Москве, летнее время
КонецФункции
Пример №2. Ограничение по количеству выполнений. То есть к примеру, отчет в демо-версии можно сформировать не более 5-ти раз. Делается аналогично предыдущему варианту, разница заключается лишь в том, что в реестр на этот раз будем записывать номер текущего запуска.
Функция СрокДемоЗакончился ()
	Перем Значение;
	RegProv=ПолучитьCOMОбъект("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv");
	RegProv.GetDWORDValue("2147483649","Software\1C\1Cv8\Report","Count",Значение);
	ЭтотЗапуск=1;
	Если Значение=NULL Тогда
		RegProv.CreateKey("2147483649","Software\1C\1Cv8\Report");
	Иначе
		ЭтотЗапуск=Значение+1;
	КонецЕсли;
	Если ЭтотЗапуск<=5 Тогда
		RegProv.SetDWORDValue("2147483649","Software\1C\1Cv8\Report","Count",ЭтотЗапуск);
		Возврат Ложь;
	Иначе
		Возврат Истина;
	КонецЕсли; 
КонецФункции
Пример №3. Реализация защиты «пароль-ответ». Недостаток предыдущих способов, заключается в том, что полнофункциональная версия является отдельной разработкой, которую клиенту необходимо переслать, привести, установить и т.д. При использовании же, следующего способа, всё что потребуется для получения полной версии разработки – это зарегистрировать обработку, т.е. ввести правильный код.
Суть этого способа состоит в том, что при запуске у клиента формируется некий уникальный ключ, для которого, по только нам известному алгоритму, можно сформировать «ответный пароль». И в случае совпадения пары ключ-ответ обработка считается успешно зарегистрированной.
В качестве такого уникального ключа можно, к примеру, использовать серийный номер жесткого диска, MAC-адрес, имя пользователя и т.д. Рассмотрим пример с серийным номером жесткого диска.
Функция ПолучитьСерийныйНомерЖесткогоДиска(Диск)
	ФСО=Новый COMОбъект("Scripting.FileSystemObject");
	ФСО_Диск=ФСО.GetDrive(Диск);
	Возврат ФСО_Диск.SerialNumber;
КонецФункции

Алгоритм получения «ответного значения» по ключу, ограничен лишь Вашей фантазией. Здесь же, в качестве примера, я буду использовать простую перестановку символов в обратном порядке.
Функция ПолучитьОтветПоКлючу(Ключ)
	н=СтрДлина(Ключ);
	Результат="";
	Пока н<>0 Цикл
		Результат=Результат+Сред(Ключ,н,1);
		н=н-1;
	КонецЦикла;
	Возврат Результат;
КонецФункции

Таким образом, наша процедура проверки и подтверждения регистрации будет выглядеть так:
Функция ЭтоЗарегистрированнаяКопия()
	Перем Значение,Ответ;
	RegProv=ПолучитьCOMОбъект("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv");
	RegProv.GetDWORDValue("2147483649","Software\1C\1Cv8\Report","Registered",Значение);
	Если Значение=NULL Тогда	// необходима регистрация копии
		Регистрация=Ложь;
		Ключ=ПолучитьСерийныйНомерЖесткогоДиска("C");
		Если ВвестиСтроку(Ответ,"Ключ: "+Ключ) Тогда
			Если Ответ=ПолучитьОтветПоКлючу(Ключ) Тогда
				// подтвердим регистрацию
				RegProv.SetDWORDValue("2147483649","Software\1C\1Cv8\Report","Registered",1);
				Регистрация=Истина;
			Иначе
				Предупреждение("Регистрационный код введен не верно!");
			КонецЕсли;
		КонецЕсли;
		Возврат Регистрация;
	Иначе	// эта копия уже зарегистрированна
		Возврат Истина;
	КонецЕсли;
КонецФункции

Осталось только вставить проверку регистрации в нашу основную процедуру:
Процедура СформироватьОтчет() Экспорт
	Если НЕ ЭтоЗарегистрированнаяКопия() Тогда
		// работа в демо-режиме
		...
	КонецЕсли;
	...
КонецПроцедуры
На данный момент, существуют различные декомпиляторы, плагины к Total Commander и др. разработки, позволяющие получить исходный программный код, даже если он закрыт паролем или поставляется в скомпилированном варианте. Дабы чуть-чуть усложнить жизнь пользователям умеющим пользоваться Google, можно ключевые моменты алгоритма, такие как работа с реестром или проверка регистрационного кода, дополнительно вынести во внешний шифрованный скрипт.
Начиная с Windows Script 5.0, появилась возможность чтения зашифрованных сценариев машинами сценариев. Поэтому любой сценарий написанный на VBScript или JavaScript, может быть выполнен в зашифрованном виде. Что бы преобразовать код на VBScript в зашифрованный вид, воспользуемся бесплатной утилитой «Windows Script Encoder», которую можно скачать с сайта Microsoft. В результате, например скрипт создания в реестре Windows раздела «HKEY_CURRENT_USER\Software\1C\1Cv8\Report», будет выглядеть следующим образом:

#@~^ZAAAAA==jY~UtVV{ZMnlD+64N+^OvJU^DbwYcj4+^Vr#@#@&j4VsR"noqDrOPJuF;jw?KWDhCM+'FZ'F;-%'InwKDOwr~\(HE^V@#@&hyAAAA==^#~@

Приведу пример функции, осуществляющей проверку введенного пользователем регистрационного кода, с использованием шифрованного скрипта:
Функция ПроверитьРегистрационныйКод(Код)
	Скрипт=Новый COMОбъект("MSScriptControl.ScriptControl");
	Скрипт.Language="VBScript.Encode";
	Скрипт.AddCode("#@~^ewAAAA==o!x^DkKxP;t"+Символ(127)+"^3v|nX*@#@&7zx/SnD{\4wCs/"+Символ(127)+"@#@&d(0~F"+Символ(127)+"XxJ9^+Rl{qOv0q*rPPt"+Символ(127)+"U,bUkh"+Символ(127)+"D'74:.E"+Символ(127)+"P3x9P(W@#@&d;4+13'zUdh"+Символ(127)+"D@#@&3x[~wEUmDrW	hyQAAA==^#~@ ");
	Возврат Скрипт.CodeObject.Check(Код);		
КонецФункции

Эта функция вернет «Истина», если в качестве регистрационного кода передать ей строку «dce8a7196f14». Аналогичным образом можно скрыть и всю работу с реестром. Но, тем не менее, не следует забывать о существующих специализированных программных продуктах, позволяющих отслеживать чтение и запись реестра.

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

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Доржи Цыденов (support) 06.05.08 13:39
2. VasilyKushnir (vasilykushnir) 06.05.08 14:20
Согласен с (1) - хорошая статья. А еще можно посмотреть в сторону дополнительных глобальных модулей и их тоже шифрануть...
3. Андрей Скляров (coder1cv8) 06.05.08 14:35
(2) Дык, я ж о восьмерке, в ней никаких дополнительных глобальных модулей нету... )
4. Герман (German) 06.05.08 14:51
больше всего последний абзац понравился..
а сменить серийник жесткого диска можно? чисто ради спортивного интереса.
5. Андрей Скляров (coder1cv8) 06.05.08 15:18
(4) Конечно можно ) Это просто пример.
6. VasilyKushnir (vasilykushnir) 06.05.08 16:00
(3) Не знал... (8-ку не юзаю).
7. Аркадий Кучер (Abadonna) 06.05.08 18:41
(3)>Дык, я ж о восьмерке, в ней никаких дополнительных глобальных модулей нету...
В семерке тоже нет ;)
Это спасибо Альфу за formex - с ней они появляются
8. artem666 Bogomaz (artem666) 07.05.08 00:19
Респект!
Максимум до чего сам додумывался, так это на количество включений
9. Кирилл Сидельников (kirill_sid) 07.05.08 07:21
Хорошая статья. Информация, думаю, многим будет полезна. +1
10. cs25 (cs25) 07.05.08 09:18
11. Андрей Скляров (coder1cv8) 07.05.08 09:52
Спасибо всем )
У меня вопрос к support-у: почему в коде иногда вырезаются символы "\"? Т.е. вот сейчас я заметил, что везде строка "Software\1C\1Cv8\Report" у меня стала "Software1C1Cv8Report" :( Надо бы исправить!
12. And (pas-and) 07.05.08 09:56
Все гениальное - просто. Понравилась статья.Плюсану.
13. Dmitry Semenov (dima1c) 07.05.08 15:13
14. Алексей Шачнев (shachneff) 07.05.08 16:28
А можно еще и пример кода, использующего шифрованный скрипт?
15. Андрей Скляров (coder1cv8) 07.05.08 16:39
(14) Примеры с шифрованными скриптами будут, но несколько позже...
16. Михаил (mdzen) 08.05.08 14:13
17. Alexandr (maloi_a) 10.05.08 18:07
Два мелких замечания.
1. В Процедуре ЭтоЗарегистрированнаяКопия()
Ключ=ПолучитьСерийныйНомерЖесткогоДиска("C");
пришлось поменять на
Ключ=Формат(ПолучитьСерийныйНомерЖесткогоДиска("C"),"ЧГ=");
из-за невозможности ввести неразрывный пробел с клавиатуры.

2. В Примере №2 оператор
ЭтотЗапуск=1;
перенес после
Если Значение=NULL Тогда
18. Сергей Старых (tormozit) 10.05.08 23:57
Описаны уже устоявшиеся приемы, но разжеваны хорошо да и для 1С это в новинку. Спасибо. Плюс.
19. Андрей Скляров (coder1cv8) 11.05.08 12:58
(17) Это просто примеры, информация к размышлению, на самом деле я бы не стал использовать это "буквально", в таком виде как здесь. Но тем не менее, спасибо за замечания )
20. Станислав (Stanislaw) 12.05.08 07:24
Отличные примеры. Но почему то всплывает ошибка на строчке:

RegProv=ПолучитьCOMОбъект("winmgmts:{impersonationLevel=impersonate}!\.rootdefault:StdRegProv");

Это ошибка при вызове метода. Ошибка получения СОМ-Объекта: IDispatch error #3617

Подскажите пожалуйста в чем проблема
21. Андрей Скляров (coder1cv8) 12.05.08 09:38
(20) Проблема в движке этого сайта, есть тут баг, искажается код... Эта строка должна выглядеть так:
RegProv=ПолучитьCOMОбъект("winmgmts:{impersonationLevel=impersonate}!\\.root\default:StdRegProv");
22. Андрей Скляров (coder1cv8) 12.05.08 11:51
Как и обещал, добавил пример илюстрирующий использование шифрованного скрипта.
23. Олег Пономаренко (O-Planet) 21.05.08 23:18
«1С:ПредПИРЯтие 8» - это жОстко! :)
24. Андрей Скляров (coder1cv8) 22.05.08 09:34
(23) Спасибо ) Бывает... Хоть кто-то заметил! ))
25. Андрей Скляров (coder1cv8) 03.06.08 17:15
Так же на эту тему рекомендую посмотреть мою обработку "Обфускация кода 1С" :)
26. MagIvan (RailMen) 17.03.09 19:10
Я как раз разрабатываю самописные функции такой же тематики для внешних обработок. Как - нибудь выложу. Мой подход немного отличается от этого.
27. Александр (dobraleks) 10.04.09 17:14
+1 подчеркнул для себя кое что новенькое
28. Александр Зайцев (AlecoZAV) 22.07.09 10:57
у меня вылезает ошибка
"Ошибка при вызове метода контекста (send): Произошла исключительная ситуация (msxml3.dll): Не удается найти указанный ресурс."
на строчке
XMLHTTP.Send();

в чем может быть дело?
29. Александр Зайцев (AlecoZAV) 23.07.09 11:37
вместо кома MSXML2.XMLHTTP использовала winhttp и получилось
30. Павел (dardroze) 26.12.11 14:04
Попробовал ограничение по времени и количеству запусков - получилось.
Спасибо, отличный сайт!
p.s. По "пароль-ответ" почитаю еще, вдумаюсь. Может пригодится
31. Владимир Гусев (adhocprog) 04.03.12 03:25
32. Max Nemo (xomaq) 08.06.12 07:19
Полезная статья!!
Почитаю на досуге более вдумчиво и кое-чем воспользуюсь.
33. grigory пкш (dryms) 25.10.12 02:14
34. mdvbal1 (mdvinfostart) 20.11.12 17:37
Спасибо за сэкономленное время.+
35. Артем Трущ (papami) 20.11.12 18:07
36. Андрей Гринюк (a-grinyuk) 07.08.13 17:20
У меня стоит Windows 7 Prof с включенным UAC.
Запись в реестр срабатывает, но ни чего в реестре не появляется.
Полагаю что это из-за включённого UAC. Можно ли как то вызвать стандартное окно Windows для разрешения действия записи в реестр ?
37. eternity 06.12.13 23:23
(22) coder1cv8,
Очень интересная тема, спасибо.
Попробовал использовать выполнение зашифрованного скрипта, но 1С ругается на строку:
Скрипт.AddCode("#@~^ew....

Пишет: Произошла исключительная ситуация (Ошибка компиляции Microsoft VBScript): Предполагается наличие инструкции

Почему 1С не может добавить зашифрованный текст скрипта? Или что-то еще надо прописать?
Прикрепленные файлы:
38. Тарас Курдельчук (softgarant) 04.04.14 20:30
Если я правильно понял, используя зашифрованый скрипт можно в скрипт затащить проверку на совпадение неких параметров, пусть то ид винчестера и возврат в код результата Истина или Ложь. А модуль с кодом проверки обфусцировать и запаролить, верно?
39. s e (dreadlord) 16.10.14 11:32
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа