1С:Ассемблер. Немного летнего веселья!

21.06.19

Разработка - Языки и среды

Все вы, наверное, слышали, что 1С-ники жалуются на свою систему, считая язык 1С недостаточно низкоуровневым, скучным и т.п. Все они с тоской поглядывают в сторону "настоящих" языков программирования. Так вот, господа, они неправы. В системе 1С есть места, где можно размять программерский мозг и получить удовольствие от низкоуровневой техники. Предлагаю вам погрузиться в недра виртуальной машины 1С и понять, как она работает. Там есть свой "ассемблер" и мы попробуем его в действии!

Скачать файлы

Наименование Файл Версия Размер
1С:Ассемблер. Немного летнего веселья!:
.epf 34,10Kb
156
.epf 34,10Kb 156 Скачать

Посвящается Валерию Агееву (awa)

Стековые виртуальные машины

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

Ремарка для тех, кто забыл

Стек (Stack) в переводе с английского означает "стопка". Когда мы кладем в стопку (скажем, книг) какую-то новую книжку, то она оказывается сверху. Убирать из стопки книги мы можем только сверху вниз. Т.е. последняя добавленная книжка снимается из стопки самой первой. Это тот самый принцип "LIFO" - last in/first out. Обратной ситуацией является очередь (в магазине). Кто первый встал - того и тапки.

Java, Python, C# и 1С - все они используют стековые машины для выполнения своего кода. Рискну предположить, что node.js - тоже, но это неточно, а гуглить мне лень. 95% вероятности, что это так и есть.

 

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

У языка 1С тоже есть несколько реализаций. Первая - сама 1С, вторая - например, 1Script. Есть еще несколько, чуть менее известных.

Стековая машина

Устроена стековая машина невероятно просто. Я разбирал ее устройство на Хабре еще в 2014 году, поэтому здесь просто коротенько напомню.

Итак, вот есть у вас выражение А = 1 + 1; как оно выполняется стековой машиной?

 

PushConst 1
PushConst 1
Add
LoadVar A

 

Поместить в стек операнд-константу 1 (2 раза), затем выполнить операцию Add.

Операция Add извлекает свои аргументы из стека (2 штуки) и складывает. Результат кладет обратно на стек. Операция LoadVar берет переданную переменную А и загружает в нее то, что лежит на стеке (в данном случае - результат сложения).

Этот простой алгоритм позволяет эффективно вычислять цепочки выражений. Например, операция А = 1+1+2 будет выглядеть вот так:

 

PushConst 1 ; поместили константу на стек
PushConst 1 ; поместили константу на стек
Add ; забрали 2 аргумента из стека (1 и еще 1), сложили, поместили в стек результат
PushConst 2 ; поместили константу на стек
Add ; забрали 2 аргумента из стека (2 и еще 2), сложили, поместили в стек результат
LoadVar A ; забрали из стека то, что там лежало (4) и загрузили в переменную А.

 

Каждая машина имеет свой набор операций, не обязательно такой, как здесь, но принцип у всех общий: операции по очереди кладут что-то на стек или извлекают что-то из стека.

Операции, выполняемые виртуальной машиной, принято называть "байт-кодом". Это такой "ассемблер" для стековой машины.

Как увидеть байт-код машины 1С

По умолчанию, если вы сохраняете какой-либо модуль, он сохраняется в чистом виде, так, как вы его написали. Однако, если поставить на модуль пароль или удалить модуль из поставки, то системе потребуется как-то все-таки знать - что выполнять. Поэтому, система при сохранении компилирует ваш код 1С в байт-код и сохраняет уже его. Например, если вы поставите пароль на модуль внешней обработки, то в файл epf ляжет скомпилированный байткод. Его можно посмотреть утилитами семейства v8unpack.

 

Какой же хакер без подходящих инструментов

Давайте посмотрим на байткод 1С. Права на описанные инструменты принадлежат их авторам, как и всяческие респекты от меня и сообщества.

 

Для начала нам потребуется распаковщик файлов epf. Самый простой способ, это установить его через chocolatey

 

choco install v8unpack --version 3.0.41 --source https://www.myget.org/F/onescript/api/v2

 

Тем, у кого нет chocolatey (эй,чуваки, как вы без него живете?) можно скачать по прямой ссылке https://github.com/e8tools/v8unpack/releases/download/v.3.0.40/v8unpack.exe но не забудьте потом exe прописать в PATH, чтобы было удобнее запускать.

 

Итак, возьмем любую внешнюю обработку 1С, модуль которой не скрыт паролем, и посмотрим на нее изнутри.

 

v8unpack -P КакаяТоОбработка.epf content

 

Будет создан каталог content, а в нем размещено содержимое внутренних файлов контейнера 1С (кому интересно - формат контейнера описан вот здесь: //infostart.ru/public/250142/)

 

Итак, в этом контейнере нас интересует каталог "<какой-то-GUID>.0", а в нем файлы "info" и "text". Файл "text" это просто текст модуля, а файл инфо это служебный файл, с которым мы еще поработаем. Теперь удалим каталог content и закроем модуль нашей обработки паролем. Откройте редактирование модуля в конфигураторе и в меню "Текст" выберите пункт "Установить пароль". Теперь, при попытке редактирования модуля Конфигуратор будет спрашивать пароль.

 

Отлично, а что же на уровне внутренних файлов? Сохраним запароленную обработку, удалим каталог content от предыдущего запуска и повторим команду

 

v8unpack -P КакаяТоОбработка.epf content

 

посмотрим в каталог с GUID.0 ого, появился файлик image, а в файле text - какая-то абракадабра. Платформа зашифровала содержимое модуля, его действительно не видно, но ей же надо как-то выполнять алгоритмы, верно? Для этого она перед шифрованием скомпилировала код 1С в байт-код виртуальной машины и записала его в файл image. Посмотрим на него:

 

 

Это - ассемблер 1С. Именно его выполняет наша любимая платформа, когда считает всем зарплату. И знаете, что самое интересное? Мы можем напрямую писать код на этом ассемблере, не прибегая к услугам компилятора! Слабо? Я же говорил, что будет весело!

 

А зачем это нужно?

Ну, во-первых, это просто прикольно, это позволит вам лучше понимать устройство вашей системы и понять, как работают современные управляемые языки программирования. Это позволит вам прокачать навык инженера-программиста и просить более высокую зарплату.

Во-вторых, вы все знаете, что байт-код 1С очень легко декомпилируется обратно в скриптовый код, вплоть до сохранения имен переменных и процедур. Существуют даже обфускаторы кода (здесь же на инфостарте), которые портят код, так чтобы он плохо читался после декомпиляции. На "ассемблере 1С" можно написать код таким образом, что он вообще не будет декомпилироваться. Позже я покажу, как написать работоспособный код, который платформа будет выполнять, но для которого просто не существует соответствующих ему синтаксических конструкций в языке 1С. Декомпилятору будет просто нечего выдать на выходе. Здорово, правда?

Оговорка

Я против воровства результатов чужого труда. Если вы пользуетесь декомпилятором, то скорее всего, вы не хотите платить автору, т.е. просто хотите украсть его работу. Это некрасиво. Поэтому, я не буду приводить ссылки на декомпилятор 1С в сети. Более того, каждый раз, когда в сети (чаще на Мисте) вы видите вопрос про "как декомпилировать обработку", "как снять пароль" - обязательно напишите автору вопроса, что он говнюк и мелкий воришка.

Далее, я также против и обфускаторов. Толку от них мало, их результат все равно декомпилируется и восстанавливается. С помощью написания алгоритма на уровне байт-кода можно создать супер-обфускатор, который неподвластен имеющимся на рынке 1С декомпиляторам. Вопрос взлома станет дороже покупки, что и требовалось обеспечить.

 

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

Ну че, поехали?

К статье прилагается внешняя обработка "Ассемблер", которая позволяет изучить формат модуля 1С. Создайте внешнюю обработку с зашифрованным модулем, распакуйте ее с помощью v8unpack и в "Ассемблере" откройте файл "image", который мы рассмотрели чуть раньше.

 

Тут пока все будет не очень понятно, поэтому, давайте разбираться, как работает виртуальная машина 1С.

Раздел "Операции"

Основной код модуля описан в виде потока операций (команд). Каждая команда имеет числовой номер - код операции. Сокращенно его называют ОпКод (OpCode), этот термин можно встретить в специальной литературе. Каждая команда, помимо опкода имеет один числовой аргумент. Трактовка аргумента зависит от опкода. Каждая команда, получая на вход свой аргумент сама принимает решение, что с ним делать. Некоторые команды не имеют аргумента (ничего не делают со своим аргументом). Итак, запомнили, каждая команда - это два числа: опкод и аргумент операции.

Какие же бывают ОпКоды? В машине 1С их 128, но большую часть составляют встроенные функции типа Лев, НачалоКвартала и им подобные. Низкоуровневых операций существенно меньше.

Раздел "Константы"

С константами вообще классно. Любая ЭВМ должна иметь где-то прошитый набор констант, чтобы понимать что один - это один, а ноль - это ноль. В моем военном прошлом я изучал и работал со старой советской ЭВМ и там был даже специальный блок "ЗУ Констант", хранивший побитовые представления основных констант. Т.е. чтобы прибавить к чему-либо единицу, машина должна понимать, а как вообще выглядит единица с точки зрения включенных/выключенных электронных регистров памяти. Помимо единицы и нуля там же хранилась Пи, таблица синусов/косинусов и всякое такое. Полагаю, современные устройства тоже имеют нечто подобное внутри ПЗУ.

 

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

Раздел "Переменные"

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

Как кодить-то?

Кодить в такой парадигме довольно увлекательно. По сути - очень похоже на настоящий ассемблер, только вместо регистров - один стек, на который складываютя и из которого вынимаются операнды. Давайте попробуем сложить 2 числа на байткоде 1С.

Запишем в раздел констант числовую константу 2. Она будет иметь номер 0 в списке констант.

Сложение чисел 2 и 2 будет выглядеть следующим образом:

 

LdConst 0
LdConst 0
Add 0

 

Готово! Следите за руками: Все опкоды, которые кладут что-либо на стек, я по традиции обозначил префиксом Ld от слова "Load". Этой традиции много лет, даже Терминатор в своем будущем соблюдал канон и пользовался этим сокращением в своей прошивке.

Команда LdConst имеет аргумент, который указывает на номер константы в списке констант (мы помним, что там по адресу 0 лежит двойка). Команда смотрит в свой аргумент, достает двойку по заданному адресу и кладет ее в стек. В стеке один элемент - двойка.

Вторая команда делает то же самое, в стеке 2 элемента (и оба - двойки)

Третья команда - это операция сложения. Она не нуждается в аргументе, так как оба ее операнда должны лежать в стеке, но поскольку совсем без аргумента нельзя, то в байткоде у Add будет просто 0. Операция сложения извлекает 2 элемента из стека и складывает их по правилам языка 1С (с учетом типа значения самого левого аргумента).

Результат сложения кладется обратно на стек, таким образом получается как бы "возврат" из функции сложения.

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

Превед, Мир!

По традиции, изучение нового языка или технологии проще всего начать с демонстрационной программы "Hello World".

 

Откройте обработку "Ассемблер" и на закладке "Процедуры" введите строку следующего содержания:

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

Далее, на закладке "Константы и переменные" заведите строку

 

 

А теперь, наберите в поле "Операции" следующий код:

 

LdConst 0
ArgNum 1
CallLoc 0
End 0

 

и нажмите кнопку "Запустить". Моргнет, фыркнет-пшикнет и результат будет выведен в окно сообщений

 

А чо это было?

При нажатии кнопки "Запустить" за кадром ваш код был записан в "скобочном формате" и с помощью v8unpack упакован во временную внешнюю обработку. Эта обработка была запущена штатным 1С-овским образом и выдала результат. Полная спецификация байткода доступна по кнопке "Справка по командам" в обработке "Ассемблер". Далее будут рассмотрены основные моменты работы с байткодом.

Работа с переменными

Рассмотрим работу с переменными. Пока пусть будут только глобальные переменные. Вот такой фрагмент кода

А = 8;
Б = А + 2;
М = Б - А;

 

При трансляции из него получится следующее: во-первых, в раздел констант попадут константы 8 и 2 в порядке их "встречи" компилятором. Восьмерка получит номер 0, а двойка - номер 1. Далее, в раздел переменных попадут переменные А, Б и М. Опять же номера будут присвоены по порядку попадания в поле зрения компилятора.

 

Константы

 

Номер

Тип

Значение

0

Число

8

1

Число

2

 

Переменные

 

Номер

Имя

Признаки

0

А

Глобальная

1

Б

Глобальная

2

М

Глобальная

 

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

 

LineNum 1 ; маркер строки исходника
LdVar 0 ; загрузить на стек переменную 0 (А)
LdConst 0 ; загрузить на стек константу 0 (равную 8)
Assign ; присваивание - снять со стека правую и левую части, присвоить.
LineNum 2 ; маркер строки исходника
LdVar 1 ; загрузить на стек переменную 1 (Б)
LdVar 0 ; загрузить на стек переменную 0 (А)
LdConst 1 ; загрузить на стек константу 1 (равную 2)
Add ; сложить 2 значения на стеке, результат положить в стек (А+2)
Assign ; присваивание - снять со стека правую и левую части, присвоить (Б = (А+2)).
LineNum 3 ; маркер строки
LdVar 2 ; поместить переменную 2 (М)
LdVar 1 ; поместить переменную 1 (Б)
LdVar 0 ; поместить переменную 0 (А)
Sub; вычитание. Снять 2 значения, результат положить
Assign; присваивание М = (Б-А)
End; конец блока кода

 

Видите, все довольно просто, хотя, наверняка, возникли вопросы. Например, что делает оператор LineNum? Очень просто, он привязывает байткод к строкам исходного кода, чтобы при возникновении исключения можно было бы выдать номер строки, в которой произошла ошибка. Без этого оператора машина не узнает какой набор опкодов какой строке исходника принадлежит. Кстати, весьма вероятно, что народный способ "писать код в одну строку, чтобы было быстрее" происходит именно отсюда. Если весь код написать в одну строку, то мы сократим число вызовов LineNum. Однако, сопровождение такого кода превращается в ад, и я бы отрывал руки тем, кто так пишет для корпоративного продакшена.

Вызовы методов

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

 

 

ArgNum

Кладет на стек число, показывающее сколько параметров было положено в стек для вызова метода (число переданных параметров)

CallLoc

Вызов локальной функции по номеру из таблицы методов

CallProc

Вызов метода объекта, как процедуры

CallFunc

Вызов метода объекта, как функции (с возвратом значения)

Ret

Запоминание результата функции (не на стеке)

LdRet

Положить результат последнего метода на стек

 

Давайте попробуем вызвать метод. Пусть в таблице методов существует запись про метод "МояФункция" с одним параметром. Пусть эта запись имеет номер 0.

 

Для краткости, я опущу операции LineNum. С помощью обработки "Ассемблер" вы всегда сможете посмотреть в каких местах 1С добавляет этот оператор.

 

LdConst 0; поместим на стек какую-нибудь константу

ArgNum 1; указываем сколько аргументов надо будет снять со стека при вызове метода

CallLoc 0; вызов метода с номером 0.

 

При вызове метода машина снимает со стека число значений, заданное оператором ArgNum. Это число показывает - сколько значений надо снять со стека и распределить по параметрам метода. Эта механика необходима потому, что у нас могут быть необязательные параметры в методах. Тогда при вызове метода с неполным числом параметров машина должна знать, сколько реальных значений засунуто в стек при вызове.

Обработка возвратов

Мы можем вызвать любой метод, как процедуру (игнорируя возвращаемое значение), а можем вызвать, как функцию - присваивая куда-то результат. Вспомним оператор Assign, рассмотренный ранее. Он извлекает из стека 2 аргумента. Чтобы присваивание возвращаемого значения сработало требуется поместить результат функции на стек. Это делает оператор LdRet. При этом, сама функция не знает, будут использовать ее результат или нет. Поэтому компилятор всегда компилирует тело функции одинаково, вызывая оператор Ret, который кладет результат функции в некое временное хранилище. Если значение из этого хранилища извлекут методом LdRet - хорошо. А если нет, значит оно не понадобилось. Просто и элегантно. Я при разработке 1Script не додумался про временное хранилище и мне пришлось городить довольно муторную механику "отброса" неиспользованных результатов Возврата со стека.

Более сложные случаи

Код, выполняющийся линейно, мы разобрали. Давайте посмотрим на ветвления и циклы. Для начала изучим самый простой оператор, обеспечивающий ветвление. Это Jmp. Он просто переводит выполнение на команду, номер которой передан в Jmp аргументом. С помощью джампов можно строить очень запутанный код и декомпиляция его может превратиться в ад. Если вы пишете обфускатор, то не сможете выполнить такую обфускацию, портя код исключительно на уровне исходника 1С. А в байт-коде - сколько душе угодно, была бы фантазия.

Сокращенные вычисления логических выражений

Я думаю, вы знаете, что 1С использует сокращенные вычисления логических выражений. Этот термин означает, что при вычислении выражения "А и Б" выражение Б может вообще не выполняться, если результат А - ложь. Если А = Ложь, то и все "А и Б" равно ложь и нет смысла вычислять Б. Например:

 

Если ТипЗнч(Переменная) = Тип("Структура") И Переменная.Свойство = 2 Тогда

 

имеются 2 части логического выражения: проверка типа И обращение к свойству. В 1С такая конструкция безопасна именно благодаря сокращенному вычислению. Если тип переменнной не структура, то левая часть И будет равна Ложь, а значит и все выражение будет равно Ложь. Правая часть И вообще не будет выполнена, а значит обращение к свойству "НЕ структуры" не произойдет.

 

Аналогично с ИЛИ, только наоборот. Если левая часть равна Истина, то правую часть вычислять нет смысла. Для реализации этой логики существуют 2 ОпКода - And и Or соответственно. Аргументом операции идет число, показывающее на какой адрес команды перейти, если сработает сокращенное вычисление.

 

Например, для "И" если операнд на вершине стека Ложь, то происходит переход по адресу, указанному в аргументе опкода, т.е. пропуск вычисления второго операнда, т.к. от него уже ничего не зависит. Из стека при этом значение не удаляется (результатом операции является Ложь). Иначе, если на вершине стека Истина, из стека удаляется значение и перехода не происходит, т.е. вычисляется второй операнд, результат которого полностью определяет результат всей операции).

 

Есть еще 2 команды условных переходов: JmpTrue и JmpFalse. Аргументом опкода идет адрес (номер команды), на который надо перейти, а переход выполняется только тогда, когда на стеке находится Истина или Ложь соответственно. Причем, оператор JmpTrue 1С никогда не использует при компиляции исходника. Т.е. декомпилировать байткод, использующий JmpTrue, будет намного сложнее, т.к. в синтаксисе языка 1С отсутствует соответствующая ему конструкция (trollface) Кстати, 1Script тоже никогда не использует JmpTrue и в его байткоде такая операция совсем не предусмотрена.

Условия

Как же выглядят условия? Пусть есть константа 0 со значением 1, пусть есть переменная А с номером 0 и значением 1

 

0: LdVar 0 ; кладем на стек значение А
1: LdConst 0 ; кладем на стек значение 1
2: Cmp ; сравниваем (оператор кладет результат сравнения на стек)
3: JmpFalse 6 ; перейти на команду 6, если на стеке Ложь
4: внутри IF ; не выполняется
5: внутри IF ; не выполняется
6: продолжение кода… ; выполняется

 

Если условие не выполнено - идет переход на конкретный адрес. Если выполнено - просто идет выполнение дальше и заходит в блок условия.

Циклы

Циклов у нас 3 вида: "Пока", "Для..По" и "Для Каждого.. Из". Причем для "Пока" вообще не требуется дополнительных опкодов, он целиком реализуется на джампах.

 

Для цикла "Для..По" используется отдельный стек "временных переменных", который хранит значения, обеспечивающие работу цикла. Так, во временный стек кладется конечное значение итерации цикла, и при каждом проходе текущее значение инкрементируется и сравнивается с временным. Сравнение выполняется оператором Gte (больше-или-равно) и уже знакомым нам JmpFalse если счетчик цикла стал больше или равен конечному значению цикла. Приводить байткод не буду, это домашнее задание для тех кто захочет разобраться с обработкой "Ассемблер".

 

 

Цикл с итератором "Для Каждого Из"

С итератором все вообще интересно. Итератор - это такой объект, который отвечает за обход коллекции и выдает очередной ее элемент при вызове условного метода Next()

 

Для получения итератора используется опкоды Iter, Next и неявная служебная переменная, которую компилятор создает в блоке переменных. Плюс, используется стек временных переменных и… зачем так сложно, я так и не понял, цикл с итератором в 1Скрипт сделан, на мой взгляд, попроще.

 

Попробуем разобраться. Вот фрагмент кода Внимание, потребуется включить мозг, рекомендуется налить кофе:

Функция А(Арг)

    Для Каждого Элемент Из Арг Цикл
        Элемент.Метод();
    КонецЦикла; 

КонецФункции

 

Для начала, в блок переменных у нас попадет "Арг" с номером 0, и "Элемент" с номером 1. Потом, внезапно, у нас в блоке переменных появится переменная "0Элемент", которую добавит компилятор. Вам говорили, что имя переменной не может начинаться с цифры? Забудьте, это все вранье :) В байткоде вы легко можете создать переменную с именем "@#$&^%" и вам ничего за это не будет.

 А что же дальше? Дальше вот что, сначала на стеке временных переменных будет создана новая переменная и размещена в основном стеке:

 

PutTmp ; создаем пустую переменную
LdTmp ; кладем пустую переменную в стек

 

затем на стек будет помещена итерируемая коллекция (она лежит в переменной 0)

 

LdLoc 0 ; помещаем Арг на стек

 

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

 

Iter ; взять со стека значение Арг и получить от него объект-итератор.
Assign ;присваивание итератора в Tmp

 

Вот полный листинг указанной функции, лучше сверяться с ним при дальнейшем чтении:

 
 Много байт-кода

 

Мы находимся на адресе 5. Оператор Assign, берет со стека 2 аргумента - временную переменную и объект итератор. Итератор попадает во временную переменную.

Дальше идет последовательность операций:

 

LdLoc 2 ; загрузили на стек волшебную переменную 0Элемент
LdTmp 0 ; загрузили на стек итератор
Assign; записали итератор в переменную 0Элемент
LdLoc 2; опять загрузили 0Элемент на стек
Next; вызвали получение очередного элемента из переменной на стеке
JmpFalse 26 ; если это конец коллекции - переход на адрес очистки состояния цикла.

 

Следует подробнее остановиться на операторе Next. Он извлекает из итератора очередной элемент и записывает его в переменную, которая была на стеке в этот момент (0Элемент), эта переменная извлекается из стека, а на стек кладется значение Истина, если элемент был получен, или Ложь, если коллекция кончилась. Идущий следом JmpFalse съедает этот флаг и выполняет переход согласно логике цикла.

 

Итак, после операции JmpFalse у нас в переменной 0Элемент лежит значение элемента, во временном стеке лежит итератор. Зачем нужна чехарда с перекладыванием итератора из временных переменных в 0Элемент, а потом результата итератора опять в 0Элемент - я не понял. Можно же воспользоваться сразу переменной Элемент и складывать значение в нее…

 

Наконец-то можно приступить к телу цикла. Адреса 19-23 это тело цикла. Адрес 25 - Jmp на верхушку цикла, адрес 26 и далее - выход из цикла и чистка всех временных сущностей.

Особенно интересны операции с 27-й по 32-ю. Это очистка переменной 0Элемент, которая выполняется почему-то 2 раза. Наверное, если с первого раза присваивание Неопределено не сработало, то надо сделать еще одну попыточку… Кажется, мы имеем +3 ненужных операции на каждом цикле с итератором, помимо магии с 0Элемент в начале цикла. А может в этом есть тайный смысл, который я недопостиг, как знать…

 

Для сравнения, тот же самый код в 1Script выглядит следующим образом:

 

0  :(LineNum     3)
1  :(PushLoc     0)
2  :(PushIterator  0)
3  :(LineNum     3)
4  :(IteratorNext  0)
5  :(JmpFalse   12)
6  :(LoadLoc     1)
7  :(LineNum     4)
8  :(PushLoc     1)
9  :(ArgNum      0)
10 :(ResolveMethodProc  0)
11 :(Jmp         3)
12 :(StopIterator  0)
13 :(PushConst   1)
14 :(Return      0)

 

Имеем 15 операций байткода вместо 37. Такое сравнение нельзя считать корректным, т.к. не столько количество опкодов влияет на скорость, сколько время выполнение каждого конкретного опкода. И нельзя сказать, что циклы 1Script заведомо быстрее циклов 1С. Но байткод получился намного понятнее и прозрачнее.

Хардкор для сильных духом

Ну что же, мы разобрали линейное выполнение, условия, циклы. Что еще там бывает при выполнении кода? А бывают, товарищи, исключения. Это такая штука, которая требует отдельного разговора.

 

Что такое исключение? Это, в первую очередь, прерывание текущего потока исполнения и переход либо вверх по стеку вызовов, либо в блок "Исключение" оператора "Попытка".

 

Во-первых стоит разобраться с тем, как 1С выполняет возврат из метода. Она применяет опкод BlckEnd сразу за которым идет Jmp на конец тела метода. По всей видимости, BlckEnd - это какой-то специализированный оператор очистки конца блока. При выходе из тела процедуры аргумент опкода BlckEnd всегда равен 0.

 

Чуть сложнее обстоит дело с Попыткой. При выходе из блока "Попытка" тоже исполняется операция BlckEnd, но в качестве аргумента передается номер вложенности блока Попытка относительно тела метода.


Попытка // 1
    Попытка // 2
        Попытка // 3
            Возврат 2; // BlckEnd 3

 

Т.е. "возврат" просто из тела процедуры - это BlckEnd 0, а "возврат" из Попытки - это BlckEnd <номер вложенности блока попытки>. Да, под словом "блок Попытка" я понимаю именно тот блок, который находится между словами Попытка и Исключение, т.е. я имею в виду "безошибочную" часть конструкции "Попытка Исключение".

Разбор конструкции Попытка-Исключение

Блок обработки ошибок открывается опкодом BeginTry, аргументом которого идет адрес начала блока Исключение. Т.е. при возникновении ошибки будет переход на тело обработчика. Далее, идет собственно код тела Попытка, а в его конце будет стоять BlckEnd <номер> и Jmp за пределы оператора КонецПопытки;

 

Рассмотрим байткод для следующего модуля:

Функция А()
    Попытка
       ;
    Исключение
       ;
    КонецПопытки;
КонецФункции

 

 

 

 

 

Адрес

Операция

Аргумент

Описание

 

0

BeginTry

4

Индекс шага в блоке Cmd раздела Исключение

 

1

LineNum

6

Начало строки кода

Номер строки в исходном модуле

 

2

BlckEnd

1

Окончания блока Попытка номер 1

 

3

Jmp

6

Переход за строку КонецПопытки

 

4

LineNum

6

Начало строки кода

Номер строки в исходном модуле

 

5

EndTry

0

КонецПопытки (конец блока Исключение)

 

6

LineNum

7

Начало строки кода

Номер строки в исходном модуле

 

7

End

0

Конец блока (процедуры, модуля)

 

8

End

0

Конец блока (процедуры, модуля)

 

9

End

0

Конец блока (процедуры, модуля)

 

 

Здесь все довольно прозрачно. Блок Попытка открывается оператором BeginTry и указанием адреса, куда перейти, если вдруг что случится (начало блока Исключение).

 

Далее идет тело блока (здесь отсутствует), а в конце оператор очистки BlckEnd и прыжок за пределы обработчика ошибок (адреса 2-3). Блок Исключение завершается оператором EndTry.

 

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

Самая вкуснятина

Ну а как же все это применить на практике? Как создать работоспособную обработку, написанную на чистом байткоде? Для этого, в обработке "Ассемблер" есть кнопка "Сохранить". Она позволяет сохранить в файл image весь код и описания констант-процедур, который вы введете в обработке, а затем с помощью v8unpack собрать готовый epf.

 

Сейчас мы сделаем одну интересную вещь, которую вы вряд ли увидите в другой ситуации.

 

Смотрите какая штука: у каждой коллекции есть итератор. Итератор это полноценный объект и размещается в том же самом стеке, что и другие переменные. Это значит, что с ним можно работать как с обычным (не-системным) значением, так ведь? Например, можно цикл "Для Каждого" переделать в примерно такой вариант:

 

Итератор = Массив.ПолучитьИтератор();

Пока Итератор.Следующий() Цикл
    Сообщить(Итератор.Значение);
КонецЦикла;

 

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

 

Давайте проверим эту гипотезу. Откройте обработку "Ассемблер" и в разделе "Переменные" заведите любую переменную. Далее, в разделе константы заведите строковую константу со значением "Массив" - это будет имя типа который нам нужен. А в коде введите следующее:

 

Код

Пояснение

Читает со стека

Кладет  на стек

LdVar 0

 загрузили переменную-приемник

 

Переменную

ArgNum 0

в конструктор не будем передавать аргументов (ноль)

 

 

New 0

Вызвали конструктор типа, имя которого указано в константе 0

 

Массив

Assign

Присвоили массив в переменную на стеке

Правую часть присваивания и левую часть присваивания

Ничего

LdVar 0

Кладем на стек переменную с массивом

 

Массив

Iter

Получаем итератор

Массив

Итератор

ТипЗнч

проверим тип того, что лежит на стеке

аргумент ТипЗнч

Тип

ArgNum 1

Число аргументов, которые будем передавать в Сообщить

 

 

CallLoc 0

Вызовем процедуру Сообщить

Тип

 

End

Корректный выход

 

 

 

А теперь сохранитесь на всякий случай и нажмите кнопку "Запустить". У меня выводится слово "Итератор". Поняли что мы сделали? Мы сделали, чтобы "Сообщить(ТипЗнч(М))" выдавало слово "Итератор". Поищите-ка такой тип в синтакс-помощнике. Нету? А он - есть!

 

Об этой фишке мне рассказал Сергей Батанов (baton_pk, он же dmpas), я просто пересказал ее здесь, а все респекты за этот трюк должны идти ему :)

 

Disclaimer

Следует отдавать себе отчет в том, что вы действительно работаете на низком уровне системы, в котором не предусматривается присутствие пользователя. Если вы ошибетесь при вводе команд, будете работать не с теми адресами или значениями стека, то клиент 1С у вас будет аварийно завершаться. Это нормально, здесь за вас никто ничего контролировать и перепроверять не будет. Сама обработка "Ассемблер" тоже поставляется в образовательных целях. Она не является универсальным удобным редактором, там довольно мало проверок и подсказок по заполнению таблиц. Мне важно показать саму возможность управления байткодом. Ровно по этой же причине, код внутри обработки "Ассемблер" написан с нарушением почти всех мыслимых стандартов кодирования, просьба это учитывать.

К чему я это все

1С - это невероятно сложная и многослойная система и изучать ее - крайне интересно. А чем лучше вы знаете работу своей системы - тем эффективнее можете ее использовать. Сегодня мы рассмотрели чуть поближе один из ключевых компонентов системы - виртуальную машину, которая выполняет скрипты бизнес-логики. Мы узнали, как работают стековые машины, расширили наш технический кругозор. Ну и кроме того, получили в свои руки игрушку-конструктор, ведь играться с ассемблером довольно весело!

 

Творческих вам успехов!

 

P.S.

Исследование и документирование команд байт-кода, а также расшифровка значений "скобочного формата" модулей выполнены Валерием Агеевым (awa) незадолго до ухода. Без этого человека не случилось бы очень многих легендарных разработок в сообществе 1С. Я очень горд тем, что был с ним знаком лично.

См. также

Зачем нам 1С:Элемент

Мобильная разработка Языки и среды Бесплатно (free)

Flutter может быть использован с 1С:Предприятием для разработки кроссплатформенных мобильных приложений, обеспечивая единый интерфейс и функциональность на устройствах под управлением iOS и Android. Это позволяет создавать приложения с высокой производительностью благодаря использованию собственного движка рендеринга Flutter. Интеграция Flutter с 1С:Предприятием позволяет создавать мобильные приложения любого уровня сложности, интегрировать их в корпоративные информационные системы, а также реализовывать бизнес-логику

19.03.2024    6983    ROk_dev    56    

37

(Не) Строгая типизация 1С

Языки и среды Платформа 1С v8.3 Бесплатно (free)

Существует множество языков программирования, и каждый имеет свои особенности по работе с типами данных. Слабые, явные, динамические и другие... Но кто же здесь 1С и почему с приходом "строгой" типизации EDT 1С-программистам стоит задуматься над изменением своих привычек.

16.01.2024    4229    SeiOkami    21    

55

Простое приложение на Dart

Языки и среды Бесплатно (free)

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

08.08.2023    3201    acvatoris    6    

13

Статический анализатор кода 1С на Си

Языки и среды Платформа 1С v8.3 Россия Бесплатно (free)

Написание статического анализатора для 1С традиционным способом на Си.

30.06.2023    2983    prohorp    15    

12

Сквозная задача на Исполнителе - часть первая (IMAP)

Языки и среды Абонемент ($m)

Поставили нам задачу - вынести на отдельный сервер функционал получения заказов от клиентов по электронной почте, парсинг полученных XLS в приемлемый вид и трансформация заказов в красивый JSON, понятный нашей учетной системе на 1С. Всю эту красоту желательно запустить в отдельном докер - контейнере, по возможности не тратя лицензии, поэтому отдельно стоящую конфигурацию на БСП отвергаем сразу. Можно было бы собрать всё на Apache Airflow или Apache NiFi, но решили попробовать реализовать всю логику без Open Source, будем делать свой ETL, с Исполнителем, который в версии 3.0 научился взаимодействовать с электронной почтой по IMAP. Начнем с середины - сначала напишем скрипты, а потом соберем их в рабочую конструкцию

1 стартмани

01.06.2023    1896    0    kembrik    2    

7

1С# - Расширяем код 1С кодом на C#

Языки и среды Инструментарий разработчика Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Вставки кода на C# внутри кода на 1С.

7 стартмани

07.04.2023    9288    4    SerVer1C    56    

43

Независимая разработка совместимых компонент на ORM 1С – миф или истина где-то в аннотациях Java?

Языки и среды Платформа 1С v8.3 Бесплатно (free)

При работе с 1С ORM (object relation mapping) все время преследует ощущение постоянного создания монолитного приложения — один раз привязался к какой либо сущности (например, справочник Контрагенты), и весь код заполнен ссылками на эту конкретную реализацию. Можно ли независимо разрабатывать в ORM совместимые между собой справочник «Контрагентов» и использующий его документ «Платежное поручение», но при этом избежать жестких зависимостей? Спасут ли нас микросервисы? Пример на аннотациях Java демонстрирует, как это возможно делать.

13.03.2023    1025    1CUnlimited    0    

2

xPath в 1С

Файловый обмен (TXT, XML, DBF), FTP Языки и среды Платформа 1С v8.3 Бесплатно (free)

Опыт работы методами языка xPath в 1С.

04.03.2023    4941    DemetrKlim    40    

46
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
78. tsukanov 24.06.19 15:02 Сейчас в теме
86. Evil Beaver 8108 24.06.19 17:42 Сейчас в теме
(78) Чует мое сердце, тут меряют что-то сами не понимая что. В кучу и строковые операции и БигДецималы и чопопало ваще...
89. tsukanov 24.06.19 18:14 Сейчас в теме
(86) Строковые? Ввод вывод может быть?

Ну в любом случае там сравнение не совсем честное, да. Понятно биг децималы медленнее будут.
145. CheBurator 3119 24.08.21 22:14 Сейчас в теме
(62)
почему ВМ 1С такое УГ...

может потому, что ВМ 1С написана на 1С...? ;-)
83. Indgo 338 24.06.19 16:18 Сейчас в теме
Ничего не понял, Автору большой лайк. Пиши больше может тоже въеду.
NeLenin; DoReMi; +2 3 Ответить
87. Evil Beaver 8108 24.06.19 17:49 Сейчас в теме
(83) Как-то вы так запанибрата на "ты" непонятно с чего. Ладно бы еще "въехал", а то вообще мимо проходил, приказал писать больше и дальше пошел....
98. Indgo 338 24.06.19 21:22 Сейчас в теме
(87)как то в детстве програмульки писал на ассемблере. Потом чего то сьехал.
97. ValeriVP 1303 24.06.19 19:46 Сейчас в теме
(83) я случайно минус нажал, сори, не убирается
105. Tavalik 3352 07.07.19 20:41 Сейчас в теме
Понятия не имею, где мне все это применить в работе (видать скучно живу 🙁), но прочитал с большим удовольствием! Огромная работа проделана. Андрей, браво!
106. Evil Beaver 8108 08.07.19 16:02 Сейчас в теме
(105) Ну применение для обфускации тут уже подробно разобрали в комментариях. А еще можно применить для самообразования, изучив работу стековой машины на живом примере.
107. Darklight 32 15.07.19 16:53 Сейчас в теме
Спасибо! Очень позновательно!

Предлагаю пойти дальше - и сделать конвертер LLVM для байткода 1С (как frontend, так backend) - сбудится "мечта идиота" - например написать на C# алгоритм и внедрить его в конфигурацию 1С (ну в рамках доступности используемых функций библиотек - их расширение это уже другая, более масштабная задача). Ну или наоборот - написать алгоритма на языке 1С - и сгенерить на его основе, скажем, внешнюю компоненту (хотя, это уже как раз скореее декомпиляция будет - хоть и частичная; вероятно, плясать в направлении генерации LLVM кода может проще не от байткода 1С, а от исходного кода - алгоритм разбора которого и так есть в парсере OneScript)
108. o.nikolaev 211 14.08.19 09:11 Сейчас в теме
Просто восторг. Как поставить 10 звезд статье? Наш ответ "CLR via C#" Рихтера :). Спасибо вам за интереснейшую статью!
109. Evil Beaver 8108 15.08.19 10:04 Сейчас в теме
(108) И вам спасибо за отзыв!
110. d.zhukov 1392 28.10.19 12:47 Сейчас в теме
&НаСервере
Функция ПрочитатьМодуль(Знач Адрес)
	
	ДД = ПолучитьИзВременногоХранилища(Адрес);
	Чтение = Новый ЧтениеТекста(ДД.ОткрытьПотокДляЧтения(), КодировкаТекста.UTF8);
	Текст = Чтение.Прочитать();
	
	Возврат Parse(Текст);

КонецФункции
Показать


Укажите принудительно кодировку
111. Evil Beaver 8108 29.10.19 10:50 Сейчас в теме
(110) зачем? UTF-8 и так значение по умолчанию.
112. d.zhukov 1392 29.10.19 10:52 Сейчас в теме
(111)а-кадабра иначе в процедурах
113. Evil Beaver 8108 29.10.19 11:02 Сейчас в теме
(112) можно подробнее - в каких процедурах, в какой момент?
114. AlexSinichenko 20.11.19 10:02 Сейчас в теме
Очень жаль, что Валерий ушел... Он пролил свет на много вещей и сделал много полезных программ... Его вклад в общее дело на самом деле не оценим. Вечная, светлая память.
115. ByNiko1984 29.11.19 07:42 Сейчас в теме
(0) Специально зарегистрировался, чтобы поставить плюс!

До этого был чтецом без регистрации.
Автор, ты монстр! В хорошем смысле :)
AlexSinichenko; +1 Ответить
116. Evil Beaver 8108 02.12.19 14:09 Сейчас в теме
(115) спасибо! :) Рекомендую и другие мои статьи/разработки посмотреть :)
AlexSinichenko; ByNiko1984; +2 Ответить
117. ByNiko1984 02.12.19 20:14 Сейчас в теме
118. Ish_2 1104 16.03.20 01:54 Сейчас в теме
(0) Про Валеру(awa) только узнал. А ведь как хорош был.. Жаль.
119. tr01egr 19.06.20 05:38 Сейчас в теме
Андрей, постучите, пожалуйста, в личку, отвечу на ваши вопросы по антидекомпилятору. К сожалению по другому не получается связаться.
120. AlexO 135 09.08.20 02:51 Сейчас в теме
Вам ЗП зазря платят - если у вас остается время заниматься подобной ерундой.
Вместо исправления тонн ошибок в типовых - забавляетесь игрушками "в ассембллер на 1С".
Да и всю основную работу сделал awa, - разобрал побайтово структуру баз и обработок 1С. Тут хоть по делу - битую файловую базу восстановить (средствами, запрещенными самим же 1С-ом), или обработку "открыть".
Вы ж только восторги и ахи описываете.
Куда всё это примените? В свой псевдообфускатор? Так его и без этого можно сделать, а нужен - только 1сникам скрыть свой "гениальный" код от товарищей.
Это - ассемблер 1С. Именно его выполняет наша любимая платформа, когда считает всем зарплату. И знаете, что самое интересное? Мы можем напрямую писать код на этом ассемблере, не прибегая к услугам компилятора! Слабо? Я же говорил, что будет весело!

Это обыкновенный "игрушечный" байт-код, который потом 1С-ом еще и интерпретируется посредством компилятора (написанного на С) в машинный. Или в настоящий Ассемблер, если угодно.
Вы где с ним играться будете, со своим "открытием"? Зарплату считать? Вы и так вместо проблем в ЗУП и БП - занимаетесь "забавами от 1С", и теперь собираетесь на "ассемблере 1С" зарплату считать? Вам её просто зазря платят.
123. Evil Beaver 8108 17.08.20 11:33 Сейчас в теме
(120) Эм... и в чем мораль сего потока мыслей?
126. vissarion249 11.09.20 05:47 Сейчас в теме
(123) Ну как в чем. Человек, который занимается тем, что обсирает все статьи на ИС говорит о бесполезности человека, который показывает байт-код 1С.

За статью спасибо нечеловеческое, появилось понимание работы языка)
124. SpulN 28.08.20 00:40 Сейчас в теме
Добрый день, в разделе Proc в описании процедуры встречается "Lbl", не подскажите что он описывает?
125. SpulN 04.09.20 17:49 Сейчас в теме
(124) Отвечу сам себе, может еще кому-нибудь пригодится, lbl - это индекс метки для оператора GOTO
127. skyboy13 13 20.10.20 15:58 Сейчас в теме
А как туда запихнуть общий модуль? он не выходит как "text",
как выходит как Module.bin
128. Evil Beaver 8108 27.10.20 10:25 Сейчас в теме
(127) покажите скриншот папки с общим модулем
129. skyboy13 13 27.11.20 13:42 Сейчас в теме
Тестовая конфигурация с одним модулем. Через конфигурация - выгрузить конфигурацию в файлы.
Прикрепленные файлы:
130. Evil Beaver 8108 30.11.20 19:14 Сейчас в теме
(129) прогоните этот bin через v8unpack -P Module.bin GoodModule.bin
131. skyboy13 13 12.01.21 18:19 Сейчас в теме
А где бы найти описание байт-кода, такого вида.

№ п/п Оператор
1 56 1 СокрЛП
2 57 2 Лев
3 58 2 Прав
4 59 3 Сред
5 60 2 Найти

Везде есть только куски.
132. skyboy13 13 12.01.21 18:28 Сейчас в теме
(131) или 128 штук в описании это и есть полный список?
133. Evil Beaver 8108 13.01.21 10:17 Сейчас в теме
(132) это и есть полный список, причем "встроенные функции" там есть. Справка по каждому байткоду есть в обработке, прикрепленной к статье.
134. skyboy13 13 13.01.21 10:28 Сейчас в теме
(133) вот это класс. теперь можно спокойно собирать код обратно
135. karpik666 3761 22.02.21 21:57 Сейчас в теме
Спасибо, очень пригодилось, немного доработал, но теперь можно и смотреть сам код
Прикрепленные файлы:
SagittariusA; MoiseevSN; skyboy13; artbear; +4 Ответить
136. artbear 1448 25.02.21 10:23 Сейчас в теме
(135) выложишь свою доработку кода статьи?
137. PaulMarko 28.02.21 10:13 Сейчас в теме
(135) класс, теперь все любители смены ОпКод или обфускации идут отдыхать.
Есть шанс увидеть обработку где-то или писать свою? В принципе, там не долго.
Только денек нужно свободный найтие
138. karpik666 3761 01.03.21 01:16 Сейчас в теме
(136) (137) Привет, такую доработку делал чисто для себя из любопытства, так как появилось наконец-то внятное описание байт-кода 1С (за что очень благодарен Андрею). Я не одобряю декомпиляцию разработок, поэтому выкладывать, или распространять такое решение не буду. Если есть желание, каждый может реализовать для себя подобное решение, под свои уже задачи.
139. PaulMarko 01.03.21 09:10 Сейчас в теме
(138) у нас к сожалению нет культуры производства закрытых модулей у разработчиков. каждый выдумает свой вариант. На дня тут видел вариант, где разработчик предлагает выложить открытый модуль, если с ним что случиться, через доверенное лицо, которое читает его почту. Прям, как Крестный отец какой-то..
А достаточно просто описать каждую функцию, что на входе, что на выходе. Хотя от скрытого встроенного кода ни кто еще не защищен. Не зря 1С требует себе оригинал всегда, даже если код поставляется закрытым.
У меня, например, всегда модули открыты. Не вижу смысла их закрывать, только сложности людям создаешь.
140. karpik666 3761 01.03.21 11:07 Сейчас в теме
(139) вы видимо еще не использовали систему лицензирования в своих решениях, у нас как раз нет нормального механизма защиты коммерческих разработок средствами 1С, единственное, что еще можно нормально использовать это СЛК, поэтому и приходиться идти на подобные ухищрения. 1С себе может позволить не закрывать код, так как у нее система лицензирования встроена в саму платформу, чуть в сторону к отраслевым, так там как раз используется СЛК и защищенные модули. Я лично не припомню случая необходимости доработки защищенных модулей, кроме необходимо отвязки от ключа. Для себя такой функционал реализовал для того, чтобы понять как сделана система работы с торговым оборудованием на конфигурации Далион, так как она почему-то зашита именно в защищенные модули, но прекрасно понимаю, что подобное решение в других руках можно использовать и не для простого любопытства.
141. Evil Beaver 8108 01.03.21 13:01 Сейчас в теме
(140) посмотрите сюда: https://netlenka.org/ и код открыт и взламывать никто не будет.
142. karpik666 3761 01.03.21 13:54 Сейчас в теме
(141) да, я видел, отличное решение. Но в принципе, даже такую реализацию можно переписать и использовать, все зависит от желания, терпения и компетенции, плюс все упирается в стоимость зашифрованного решения, если она низка, то никто не будет заморачиваться со взломом.
143. skyboy13 13 01.03.21 18:48 Сейчас в теме
(142) один из вариантов это опубликованный метод в google/ там стоит копейки. туда закидываешь обработчик, на вход таблицу с пользователями, к примеру, и данным. на выходе результат. все делается через http, проверка идет по кол-ву пользователей. У функции есть входные данные, есть выходные.
И заказчику дается документация, как и где это работает.
146. MoiseevSN 100 22.11.21 13:32 Сейчас в теме
(135) вот так сидишь, делаешь систему лицензирования, и вот те здрасьте, новый декомпилятор)
147. Evil Beaver 8108 22.11.21 14:03 Сейчас в теме
(146) это не декомпилятор, это просмотрщик модуля. Он не восстанавливает исходный текст
153. пользователь 28.03.22 10:28
Сообщение было скрыто модератором.
...
144. CheBurator 3119 24.08.21 20:54 Сейчас в теме
148. sacred 173 10.12.21 20:00 Сейчас в теме
Обработка некорректно считывает даты-литералы из скомпилированного образа. И потом не может сохранить считанный образ. Фикс:
&НаСервере
Процедура ЗаполнитьКонстанты(Знач Блок)
.......
		СтрКонстанты.ТипКонстанты = Типы[Константа[0]];
// ╒══════════════ Ушаков С.И. 9 декабря 2021 г. ════════════════╕
		Если СтрКонстанты.ТипКонстанты = "Дата" Тогда
			СтрКонстанты.Значение  = Константа[1]; // не преобразуем литерал. Иначе он превратится в число, потом в строку с разделителями разрядов...
		КонецЕсли;
// ╘══════════════ Ушаков С.И. 9 декабря 2021 г. ════════════════╛
		Счетчик = Счетчик + 1;

Показать
149. Evil Beaver 8108 13.12.21 16:05 Сейчас в теме
150. sacred 173 14.12.21 19:41 Сейчас в теме
Спасибо вам!
Замечательный инструмент!
Живая база знаний по байткоду 1с. Да и статья - огонь.
151. kislitsin 27.03.22 16:35 Сейчас в теме
Всем привет, попытался использовать обработку. Почему-то у меня результат работы утилиты v8unpack.exe совершенно другой. Образовалось большая куча файлов https://www.screencast.com/t/YOvx6JEu
Какой из них открывать данной обработкой ? может я как то не так распаковал ?
152. vikad 129 27.03.22 20:10 Сейчас в теме
(151) Утилиту v8unpack нужно запускать с параметром -p
v8unpack -P КакаяТоОбработка.epf content

тогда каталоги появятся)
154. DiasA 7 13.06.23 17:40 Сейчас в теме
Платформа 8.3.18 x64
Обычные формы.
Расширение *cfe.
Добавлен в расширение общий модуль из конфигурации : Клиент, Сервер, ВызовСервера.
На текст модуля в *.cfe поставил пароль, чтоб байт-код получить.
Аннотация "&Вместо()" на процедуры.
В байт-коде к стандартным блокам "Var", "DefPrm", "Lbl" добавился "Annot".

В вашем редакторе нет логики под это. + нет логики для блоков "Lbl" по процедурам и на глобальный (точку входа в модуль).

{"Proc",4,
{"Расш_ПолучитьОстатки",1,1,12,
{"Var",3,
{"Арг1",5,-1},
{"Прм1",1,-1},
{"Прм2",1,-1}
},
{"DefPrm",1,
{"B",0}
},
{"Lbl",1,
{"Перейти1",120}
},
{"Annot",1,
{"Вместо",
{"AnnotAttr",1,
{"value",
{"S","ПолучитьОстатки"}
}
}
}
}
}
}

---------------------------------------
Но я всё равно благодарен за эту разработку . я с нее стартовал в байт-код. оооочень помогла .
155. Evil Beaver 8108 13.06.23 18:06 Сейчас в теме
(154) да, тут есть что доработать. Код открыт на гитхабе, можете присылать свои исправления и доработки!
156. DiasA 7 23.06.23 08:24 Сейчас в теме
(155)
не знаю известно ли, но выяснил сам что делают ОпКоды
"Unknown(11)" = Unload
"Unused(32)" =ToBool
157. Evil Beaver 8108 23.06.23 08:25 Сейчас в теме
(156) спасибо! С ToBool понятно, а что такое Unload?
158. DiasA 7 23.06.23 08:34 Сейчас в теме
(157)снимает со стека последний один элемент. так и знал что должна быть такая операция.

LdTrue
LdFalse
LdUndef
LdNone
Unload
Unload
Unload

на стеке останется LdTrue.

а ToBool работает так же как "Системная функция Булево(69)" .
159. DiasA 7 23.06.23 09:06 Сейчас в теме
(158) ToBool (видимо по другому назвать надо) работает так:

принимает только Булево и Число. отдает Булево
Булево -> Булево
Число -> Булево (если 0, то Ложь. если любое число не равно 0 , даже отрицательное то Истина)
Оставьте свое сообщение