Книга знаний

1С:Предприятие / v8

v8: Защита исходного кода конфигураций в 1С 8.0

Рассмотрены все типовые способы защиты исходного кода конфигураций в 1С 8.0Автор статьи: Гений 1С | Редакторы: eva1, Evrepid
Последняя редакция №11 от 26.08.09 | История
URL: http://kb.mista.ru/article.php?id=519

Ключевые слова: исходный код,защита,шифрование


В 1С можно защитить исходный код общих модулей и модулей объектов.
Код формы защитить нельзя.
Защитить можно только те модули, которые не содержат


Пароль на код модуля


Исходный код шифруется с помощью данного пароля, взломать без знания пароля невозможно.

Откройте любой код модуля.
Выберите пункт меню Текст - Установить Пароль. Введите пароль и подтверждение.

Исключение кода модуля


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

Открываете конфигурацию.
Через пункт Конфигурация - Поставка конфигурации - Настройка поставки заходите в форму настройки поставки, выбираете текст каких модулей нужно исключить из поставки (можно выбрать сразу все для каждого уровня иерархии). Выберите также галочку "Файл поставки может использоваться для обновления".

Далее через пункт Конфигурация - Поставка конфигурации - Комплект поставки создаете CF-файл.

Теперь этот CF файл можно загружать у клиента - в этом CF файле нужные модули исключены (защищены от просмотра).

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



-----------------------------------------------------


Но к сожалению это слабая защита. Простенький пример подтверждает это.
ВНИМАНИЕ! ДАЛЕЕ ТЕКСТ ПИСАЛ НЕ АВТОР СТАТЬИ, ГЕНИЙ 1С
Просьба не обращаться к Гению с просьбами рассказать, как расшифровать байт-код или как его получить из закрытой конфигурации. Я этого никогда не делал.
Чтобы получить байт код, рекомендую открытый конфигуратор SQL для 1С8 (ищите на инфостарт)
Чтобы расшифровать байт-код, посмотрите обработки автора TormozIT там же.
Точнее не знаю.
Вот маленький кусок программы:
к=3;
строка="йцукен";
к_симв_строки=Лев(строка,к);


имеющий вот такой байт-код:
(он получается когда ставится пароль на модуль и когда делается поставка без текстов)

{1,
{"Cmd",15,0,
{1,2},
{2,0},
{4,0},
{16,0},
{1,3},
{2,1},
{4,1},
{16,0},
{1,4},
{2,2},
{2,1},
{2,0},
{57,0},
{16,0},
{22,0}
},
{"Const",2,
{"N",3},
{"S","йцукен"}
},
{"Var",3,
{"к",0,-1},
{"строка",0,-1},
{"к_симв_строки",0,-1}
}
} 



А вот программа декомпилирования этого примера, написать которую может каждый второй 1С-ник:

Функция ПолучитьЗначениеПозиции(ном,стр,УбратьКавычки=Ложь)
    рез="";
    к2=ном-1;
    Для к=2 По СтрДлина(стр)-1 Цикл
        а=Сред(стр,к,1);
        
        Если к=СтрДлина(стр)-1 и а="}" Тогда
            Прервать;
        ИначеЕсли а="," Тогда
            Если рез<>"" Тогда
                Прервать;
            КонецЕсли; 
            к22-1;
        ИначеЕсли к2=0 Тогда
            рез=рез+а;
        КонецЕсли; 
    КонецЦикла;
    Если УбратьКавычки Тогда
        Если Лев(рез,1)="""" Тогда
            рез=Сред(рез,2);
        КонецЕсли; 
        Если Прав(рез,1)="""" Тогда
            рез=Лев(рез,СтрДлина(рез)-1);
        КонецЕсли; 
    КонецЕсли; 
    Возврат рез;
КонецФункции // ПолучитьЗначениеПозиции


Процедура ВыполнитьДекомпиляцию()
    Перем МасКонст;  //"Const"
    Перем МасПерем;  //"Var"
    Текст=ЭлементыФормы.ПолеТекстовогоДокумента1; //исходный текст байт-кода
    КоличествоСтрок=Текст.КоличествоСтрок();
    сч=1;  //поиск первой строки
    стр0=Текст.ПолучитьСтроку(сч);
    Пока Лев(стр0,1)<>"}" и сч<КоличествоСтрок Цикл
        сч=сч+1;
        стр0=Текст.ПолучитьСтроку(сч);
    КонецЦикла; 
    сч=сч+1;
    стр0=Текст.ПолучитьСтроку(сч);
    МасКонст=Новый Массив;  
    Если Лев(стр0,8)="{""Const""" Тогда
        сч=сч+1;
        стр0=Текст.ПолучитьСтроку(сч);
        Пока Лев(стр0,1)<>"}" и сч<КоличествоСтрок Цикл
            МасКонст.Добавить(ПолучитьЗначениеПозиции(2,стр0));
            сч=сч+1;
            стр0=Текст.ПолучитьСтроку(сч);
        КонецЦикла; 
        сч=сч+1;
        стр0=Текст.ПолучитьСтроку(сч);
    КонецЕсли;    
    МасПерем=Новый Массив;
    Если Лев(стр0,6)="{""Var""" Тогда
        сч=сч+1;
        стр0=Текст.ПолучитьСтроку(сч);
        Пока Лев(стр0,1)<>"}" и сч<КоличествоСтрок Цикл
            МасПерем.Добавить(ПолучитьЗначениеПозиции(1,стр0,Истина));
            сч=сч+1;
            стр0=Текст.ПолучитьСтроку(сч);
        КонецЦикла; 
    КонецЕсли;    
    мас=Новый Массив;
    Результат="";
    сч=3;
    стр0=Текст.ПолучитьСтроку(сч);
    Пока Лев(стр0,1)<>"}" и сч<КоличествоСтрок Цикл
        зн1=Число(ПолучитьЗначениеПозиции(1,стр0));
        зн2=Число(ПолучитьЗначениеПозиции(2,стр0));
        Если зн1=1 Тогда
            //Результат=Результат+"// номер строки "+зн1+","+зн2+Символы.ПС;
            Результат=Результат+Символы.ПС;
        ИначеЕсли зн1=2 Тогда
            мас.Добавить(МасПерем[зн2]);
        ИначеЕсли зн1=4 Тогда
            Результат=Результат+мас[0]+"="+МасКонст[зн2];
            мас.Очистить();
        ИначеЕсли зн1=16 Тогда 
            Если мас.Количество()>1 Тогда
                Результат=Результат+мас[0]+"="+мас[1];
            КонецЕсли; 
            мас.Очистить();
            Результат=Результат+";";
        ИначеЕсли зн1=22 Тогда 
        ИначеЕсли зн1=57 Тогда 
            мас[1]="Лев("+мас[1]+","+мас[2]+")";
            мас.Удалить(2);
        Иначе
            Результат=Результат+"// неизв. код "+зн1+","+зн2+Символы.ПС; 
        КонецЕсли;    
        сч=сч+1;
        стр0=Текст.ПолучитьСтроку(сч);
    КонецЦикла; 
    сообщить(Результат);
КонецПроцедуры


Описание байт-кода имеет разделы:
- описание алгоритма
- описание констант
- описание переменных
- описание процедур

{"Proc",95,
{"Инициализировать",16,0,0,
{"Var",3,
{"спрЖурналы",1,-1},
{"обЖурнал",1,-1},
{"обНастройка",1,-1}
}
},


Разбор байт-кода показывает, что движок 1С это простая стековая машина с "польской" системой вычислений.
Данная система исчисления хорошо знакома большинству программистов по калькуляторам БЗ-34 и другим этой серии.

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

Не разбираемый в приведенном коде набор "{22 , 0}", на самом деле возврат управления из выполняемого фрагмента на выше стоящий уровень.

Естественно, мы не приводим полный набор команд этого макро языка, составить список сможет, каждый второй 1С-ник, готовый потратить на это какое то время.

Приведу, только фрагмент:




№ п/пОператорКоличество данных подним в стекеНаименование
1561СокрЛП
2572Лев
3582Прав
4593Сред
5602Найти



Как понятно, структура байт-кода такова:
{Тег_Начала}
{Тег_Начала_Блока_Кода}{Тег_Разделитель}{Тег_Количество_Записей}{Тег_Разделитель}{Тег_Количество_Переменных_Модуля}{Тег_Разделитель}
//Запись кода
{Тег_Начала_Записи}{Тег_Команды}{Тег_Разделитель}{Тег_Данных}{Тег_Окончания_Записи}
//Запись спусковой записи
{Тег_Начала_Записи}{Тег_Команды_Спуска}{Тег_Разделитель}{Тег_Данных_Спуска}{Тег_Окончания_Записи}

...

//Запись терминатор кода
{Тег_Начала_Записи}{Тег_Команды_Терминатора}{Тег_Разделитель}{Тег_Данных_Терминатора}{Тег_Окончания_Записи}
{Тег_Окончания_Блока_Кода}

{Тег_Начала_Блока_Констант}{Тег_Разделитель}{Тег_Количество_Записей}{Тег_Разделитель}
//Запись константы
{Тег_Начала_Записи}{Тег_Типа_Константы}{Тег_Разделитель}{Тег_Данных}{Тег_Окончания_Записи}
...
{Тег_Окончания_Блока_Констант}

{Тег_Начала_Блока_Переменных}{Тег_Разделитель}{Тег_Количество_Записей}{Тег_Разделитель}
//Запись Переменной
{Тег_Начала_Записи}{Тег_Имя_Переменной}{Тег_Разделитель}{Тег_Типа_Данных}{Тег_Разделитель}{Тег_}{Тег_Окончания_Записи}
...
{Тег_Окончания_Блока_Переменных}

{Тег_Начала_Блока_Процедур}{Тег_Разделитель}{Тег_Количество_Записей}{Тег_Разделитель}
//Запись Переменной
{Тег_Начала_Записи}{Тег_Имя_Процедуры_Функции}{Тег_Разделитель}{Теги_Описания_Данных_и_Их_Типов_Предопределенных_Значений}{Тег_Разделитель}{Тег_Окончания_Записи}
...
{Тег_Окончания_Блока_Процедур}

{Тег_Окончания}


Пример кода позволит разобрать дерево байт кода какой бы глубины оно не было (Не стоит думать, что приведенный код станет основой для написания полноценного декомпилятора для стековой машины 1С, т.к. он содержит упрощенный алгоритм разбора потока):

Процедура РазобратьПоРазделам(ИсхТекст)
                                    
    ДеревоДанных    = Новый ДеревоЗначений;                            
    ДеревоДанных.Колонки.Добавить("Значение");
    СтрокаДанных    = ДеревоДанных;
    Счетчик            = 0;                            
    КоличествоСтрок = ИсхТекст.КоличествоСтрок();
    Пока Счетчик < КоличествоСтрок Цикл
        СтрокаИсходная    = ИсхТекст.ПолучитьСтроку(Счетчик);
        Счетчик = Счетчик + 1;
        //Собственно строка пустая и ничего делать не будем да и не надо
        Если ПустаяСтрока(СтрокаИсходная) Тогда
            Продолжить;
        КонецЕсли;
        //Удалим лишние пробелы
        СтрокаИсходная = СокрЛП(СтрокаИсходная);
        
        НачалоБлока        = Лев(СтрокаИсходная,1)="{";
        ОкончаниеБлока    = (Прав(СтрокаИсходная,1) = "}") или (Прав(СтрокаИсходная,2) = "},");
        //Есть начало блока добавим строку
        Если НачалоБлока Тогда
            СтрокаДанных = СтрокаДанных.Строки.Добавить();
        КонецЕсли;
        //Обработаем текст блока
        Если ПустаяСтрока(СтрокаДанных.Значение) Тогда
            СтрокаДанных.Значение = СтрокаИсходная;
        КонецЕсли;
        
        //Есть окончание блока перейдем на верхний уровень
        Если ОкончаниеБлока Тогда
            СтрокаДанных = СтрокаДанных.Родитель;
        КонецЕсли;
    КонецЦикла;
    
       ЭлементыФормы.ДеревоРазбора.Значение = ДеревоДанных;
    ЭлементыФормы.ДеревоРазбора.СоздатьКолонки();
    
    СтрокиМодуля = Новый ТекстовыйДокумент;
    СтрокиМодуля.Очистить();
    
    //Ищем ветки с данными
    //На основании ствола
    ВетвьКода        = Неопределено;
    ВетвьПеременных    = Неопределено;
    ВетвьКонстант    = Неопределено;
    ВетвьПроцедур    = Неопределено;
    Для Каждого ВеткаДерева из ДеревоДанных.Строки Цикл
        Если ВеткаДерева.Значение = ОписательКорня Тогда
            //ЭтоКорень и его мы и будем разбирать
            Для каждого ВетвьКроны Из ВеткаДерева.Строки Цикл
                Если Не Найти(ВетвьКроны.Значение,ОписательКодовойВетки) = 0 Тогда
                    //Это кодовая ветвь
                    ВетвьКода    = ВетвьКроны.Строки;
                ИначеЕсли Не Найти(ВетвьКроны.Значение,ОписательВетвиКонстант) = 0 Тогда
                    //Это ветвь констант
                    ВетвьКонстант    = ВетвьКроны.Строки;
                ИначеЕсли Не Найти(ВетвьКроны.Значение,ОписательВетвиПеременных) = 0 Тогда
                    //Это ветвь переменных
                    ВетвьПеременных    = ВетвьКроны.Строки;
                ИначеЕсли Не Найти(ВетвьКроны.Значение,ОписательПроцедурнойВетви) = 0 Тогда
                    //Это ветвь констант
                    ВетвьПроцедур    = ВетвьКроны.Строки;
                КонецЕсли;
            КонецЦикла;
            Если ВетвьКода        = Неопределено
                и ВетвьПеременных    = Неопределено
                и ВетвьКонстант    = Неопределено
                и ВетвьПроцедур    = Неопределено Тогда
                Сообщить("Нет описателей ! Нет данных для декомпиляции!");
                Продолжить;
            КонецЕсли;
            //Ветки содержат данные
            //Но если ветви кода нет, то нет особого смысла что либо делать
            Если Не ВетвьКода = Неопределено Тогда
                //Помним что это код форт-машины, дурацкая с точки зрения человека "польская система"
                ВеткаСучьев = Новый Массив;
                Для каждого СукВетви Из ВетвьКода Цикл
                    ВеткаСучьев.Добавить(СукВетви.Значение);
                    Если СукВетви.Значение = ТочкаСЗапятой Тогда
                        //Обработаем ветку сучьев/ операторную строку
                        СтрокаКода = ОбработатьВетку(ВеткаСучьев,ВетвьКонстант,ВетвьПеременных,ВетвьПроцедур);
                        
                        СтрокиМодуля.ДобавитьСтроку(СтрокаКода);
                        
                        //Инициализируем ветку сучьев
                        ВеткаСучьев.Очистить();
                    КонецЕсли;
                КонецЦикла;
                
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    
    //Все распарсили выводим текстМодуля
    ЭлементыФормы.ТекстМодуля.УстановитьТекст(СтрокиМодуля.ПолучитьТекст());
    
КонецПроцедуры

Функция ОбработатьВетку(Сучья,ВетвьКонстант,ВетвьПеременных,ВетвьПроцедур)
    Результат = "";
    
    СтекФортМашины = Новый Массив;
    СтекФортМашины.Очистить();
    
    КоличествоСучьев = Сучья.Количество()-1;
    
    //Имитируем фортмашину
    Для Счетчик = 0 По КоличествоСучьев Цикл
        //Получим сук
        Сук = Сучья.Получить(Счетчик);
        
        Тег1=Число(ПолучитьЗначениеПозиции(1,Сук));
        Тег2=Число(ПолучитьЗначениеПозиции(2,Сук));
        
        //Это будет CASE
        Если Тег1 = 1 Тогда //Перевод строки
            СтекФортМашины.Добавить(Символы.ПС);
            //Тег2 в этом случае количество данных
        ИначеЕсли Тег1 = 2 Тогда
            СтекФортМашины.Добавить(ПолучитьЗначениеПозиции(1,СокрЛП(ВетвьПеременных[Тег2].Значение),Истина));
        ИначеЕсли Тег1 = 4 Тогда
            Значение = ПолучитьЗначениеПозиции(2,СокрЛП(ВетвьКонстант[Тег2].Значение),Истина);
            ТипЗначения = ПолучитьЗначениеПозиции(1,СокрЛП(ВетвьКонстант[Тег2].Значение),Истина);
            СтекФортМашины.Добавить(?(ТипЗначения = "S",""""+Значение+"""",Значение));
        ИначеЕсли Тег1 = 16 Тогда
            СтекФортМашины.Добавить("=");
            СтекФортМашины.Добавить(";");
        ИначеЕсли Тег1 = 57 Тогда
            СтекФортМашины.Добавить("Лев(");
        КонецЕсли;
        
    КонецЦикла;
    
    Результат = Результат + ОбработатьСтекФортМашины(СтекФортМашины);
    
    Возврат Результат;
КонецФункции


Второй приводимый фрагмент, рассматривает код более обобщенно по сравнению с предыдущим, но не претендует на абсолютную истину и написан только для демонстрации обобщенного взгляда на проблему.

Не забывайте о том, что декомпилируя код, вы можете нарушить право собственности на интелектуальный продукт.


Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2025 | Mista.ru

Яндекс.Метрика