Как встроить lua в программу
Lua – Установка Lua
Установка lua это вопрос, который возникает достаточно часто. Что, вообще-то, достаточно удивительно, потому как установка lua на самом деле, крайне проста и понятна. Можно даже сказать, что там вообще нечего делать. Но, раз уж такие вопросы возникают, давайте немного поговорим об установке Lua.
Установка Луа. Скачиваем
Установка Луа. Компиляция.
Есть два варианта компиляции луа, которые, в принципе, ничем не отличаются ))) Первый – это компиляция из Visual Studio Command Prompt:
Второй способ без запуска Visual Studio:
Установка Луа. Доступ
Установка Луа. Статическая линковка.
Установка Луа. Проверка.
Проверим, всё ли работает. Создаём новый проект (я сделал консольный проект win32). Дописываем инклюды луа:
и доделываем сам main():
В настройки линковщика (Project/Properties/Linker/Input/Addition Dependencies) не забываем добавить нашу библиотеку (lua.lib для релиза и lua-debug.lib для дебага). Компилируем. Создаём тестовый файлик с луа-скриптом:
Запускаем и видим то, что и хотели. Теперь мы можем использовать Луа в наших программах и не таскать с собой этот ненавистный lua.dll – мне кажется, что это намного удобнее и приятнее )
На этом наш урок завершён. Теперь у вас есть собранные версии дебаг- и релиз- версий луа, собранные для статической компиляции и вы можете без каких-либо проблем использовать их в ваших проектах.
В следующих уроках мы более подробно поговорим про использование луа-скриптов в программах и, в частности, в играх.
Создание встраиваемых сценариев на языке Lua
В то время как интерпретируемые языки программирования, такие как Perl, Python, PHP и Ruby, пользуются все большей популярностью для Web-приложений (и уже давно предпочитаются для автоматизации задач по системному администрированию), компилируемые языки программирования, такие как C и C++, по-прежнему необходимы. Производительность компилируемых языков программирования остается несравнимой (она уступает только производительности ручного ассемблирования), поэтому некоторое программное обеспечение (включая операционные системы и драйверы устройств) может быть реализована эффективно только при использовании компилируемого кода.
Действительно, всегда, когда программное и аппаратное обеспечение нужно плавно связать между собой, программисты инстинктивно приходят к компилятору C: C достаточно примитивен для доступа к «голому железу» (то есть, для использования особенностей какой-либо части аппаратного обеспечения) и, в то же время, достаточно выразителен для описания некоторых высокоуровневых программных конструкций, таких как структуры, циклы, именованные переменные и области видимости.
Однако языки сценариев тоже имеют четкие преимущества. Например, после успешного переноса интерпретатора языка на другую платформу подавляющее большинство написанных на этом языке сценариев работает на новой платформе без изменений, не имея зависимостей, таких как системные библиотеки функций (представьте множество DLL-файлов операционной системы Microsoft® Windows® или множество libcs на UNIX® и Linux®). Кроме того, языки сценариев обычно предлагают высокоуровневые программные конструкции и удобные операции, которые программистам нужны для повышения продуктивности и скорости разработки. Более того, программисты, использующие язык сценариев, могут работать быстрее, поскольку этапы компиляции и компоновки не нужны. В сравнении с С и его родственниками цикл «кодирование, компоновки, связывание, запуск» сокращается до ускоренного «написание, запуск».
Новшества в Lua
Как и любой язык сценариев, Lua имеет свои особенности:
Большее количество примеров Lua-кода приведено в руководстве «Программирование в Lua» и в wiki Lua-пользователей (ссылки приведены в разделе «Ресурсы»).
Как всегда, выбор между компилируемым и интерпретируемым языком заключается в сравнении всех за и против каждого языка в контексте, оценке альтернатив и поиске компромиссов.
Беря все лучшее из обоих миров
Что, если бы вы могли взять лучшее из обоих миров: производительность работы с «голым железом» и высокоуровневые, мощные абстракции? Более того, если бы вы могли оптимизировать алгоритмы и функции, зависящие от системы и требующие много процессорного времени, так же как и отдельную логику, не зависящую от системы и очень чувствительную к изменениям требований?
Баланс требований для высокопроизводительного кода и высокоуровневого программирования является сутью Lua, встраиваемого языка программирования. Приложения, включающие Lua, представляют собой комбинацию компилируемого кода и Lua-сценариев. Компилируемый код может при необходимости заняться железом, и, в то же время, может вызывать Lua-сценарии для обработки сложных данных. И поскольку Lua-сценарии отделены от компилируемого кода, вы можете изменять сценарии независимо от него. С Lua цикл разработки более похож на «Кодирование, компоновка, запуск, создание сценариев, создание сценариев, создание сценариев …».
Например, на странице «Uses» Web-сайта Lua (см. раздел «Ресурсы») перечислены некоторые компьютерные игры для массового рынка, включая World of Warcraft и Defender (версия классической аркады для бытовых консолей), которые интегрируют Lua для запуска всего, начиная с пользовательского интерфейса и заканчивая искусственным интеллектом противника. Другие приложения Lua включают в себя механизмы расширения для популярного инструментального средства обновления Linux-приложений apt-rpm и механизмы управления чемпионатом Robocup 2000 «Сумасшедший Иван». На этой странице есть много хвалебных отзывов о маленьком размере и отличной производительности Lua.
Начало работы с Lua
Lua версии 5.0.2 на момент написания данной статьи была текущей версией (недавно появилась версия 5.1). Вы можете загрузить исходный код Lua с lua.org, а можете найти различные предварительно откомпилированные двоичные файлы на wiki Lua-пользователей (ссылки приведены в разделе «Ресурсы»). Полный код ядра Lua 5.0.2, включая стандартные библиотеки и Lua-компилятор, по размерам не превышает 200KB.
Если вы работаете на Debian Linux, то можете быстро и просто установить Lua 5.0 при помощи следующей команды
с правами суперпользователя. Все приведенные здесь примеры запускались на Debian Linux «Sarge» с использованием Lua 5.0.2 и ядра Linux 2.4.27-2-686.
Встраиваем Lua интерпретатор в проект для микроконтроллера (stm32)
В достаточно крупных приложениях немалую часть проекта составляет бизнес-логика. Эту часть программы удобно отлаживать на компьютере, после чего встраивать в состав проекта для микроконтроллера, ожидая, что эта часть будет выполняться в точности так, как было задумано без какой-либо отладки (идеальный случай).
Так как большинство программ для микроконтроллеров пишется на С/C++, то для этих целей обычно используют абстрактные классы, предоставляющие интерфейсы к низкоуровневым сущностям (в случае, если проект пишется только с использованием C, то зачастую используются структуры указателей на функции). Данный подход предоставляет требуемый уровень абстракции над железом, однако чреват надобностью в постоянной повторной компиляции проекта с последующим программированием энергонезависимой памяти микроконтроллера бинарным файлом прошивки большого объема.
Однако есть и другой путь — использование скриптового языка, позволяющего производить отладку бизнес-логики в реальном времени на самом устройстве или загружать сценарии работы прямо с внешней памяти, не включая данного кода в состав прошивки микроконтроллера.
В качестве скриптового языка я выбрал Lua.
Почему Lua?
Существуют несколько скриптовых языков, которые можно встроить в проект для микроконтроллера. Несколько простых BASIC-подобных, PyMite, Pawn… У каждого есть свои плюсы и минусы, обсуждение которых не входит перечень обсуждаемых вопросов данной статьи.
Кратко о том, чем хорош конкретно lua — можно прочесть в статье «Lua за 60 минут». Меня эта статья сильно вдохновила и я, для более детального изучения вопрос, прочел официальное руководство-книгу от автора языка Роберту Иерузалимски «Программирование на языке Lua» (имеется в официальном русском переводе).
Отдельно хочу отметить проект eLua. В моем случае, у меня уже имеется готовая программная низкоуровневая прослойка для взаимодействия как с периферией микроконтроллера, так и для прочей требуемой периферии, расположенной на плате устройства. Поэтому данный проект мною не рассматривался (поскольку он признан предоставить те самые прослойки для связи ядра Lua с периферией микроконтроллера).
О проекте, в который будет встраиваться Lua
По традиции, в качество поля для экспериментов будет использоваться мой проект-песочница (ссылка на коммит с уже интегрированной lua со всеми необходимыми доработками, описанными ниже).
Проект базируется на микроконтроллере stm32f405rgt6 c 1 МБ энергонезависимой и 192 КБ оперативной памяти (на данный момент используются старшие 2 блока с суммарным объемом 128 КБ).
О логике использования Lua в составе проекта
Для цели отладки бизнес-логики предполагается, что команды будут приходить по UART-у, паковаться (отдельным объектом) в законченные строки (завершающиеся символом «\n» + 0-терминатор) и отправляться в lua-машину. В случае неудачного выполнения, вывод по средствам printf (так как в проекте он был ранее задействован). Когда логика будет отлажена, можно будет реализовать загрузку конечного файла бизнес-логики из файла с MicroSD карты (не входит в материал этой статьи). Так же для цели отладки Lua машина будет исполняться внутри отдельного потока FreeRTOS (в будущем на каждый отлаженный скрипт бизнес-логики будет выделен отдельный поток, в котором он будет выполняться со своим окружением).
Включение субмодуля lua в состав проекта
В качестве источника библиотеки lua будет использоваться официальное зеркало проекта на github (поскольку мой проект так же размещен там. Вы можете использовать исходники напрямую с официального сайта). Так как в проекте имеется налаженная система сборки субмодулей в составе проекта по индивидуальным для каждого субмодуля CMakeLists-ам, то я создал отдельный субмодуль, в который включил fork данного и CMakeLists для сохранения единой стилистики сборки.
CMakeLists производит сборку исходников репозитория lua как статическую библиотеку со следующими флагами компиляции субмодуля (берутся из файла конфигурации субмодулей в основном проекте):
И флагами конкретизации используемого процессора (задаются в корневом CMakeLists-е):
Важно отметить необходимость в корневом CMakeLists-е указать определение, разрешающее не использовать double значения (поскольку микроконтроллер не имеет аппаратной поддержки double. Только float):
Ну и остается только сообщить компоновщику о необходимости собрать данную библиотеку и включить итог в компоновку конечного проекта:
Определение функций для работы с памятью
Так как сама Lua не занимается работой с памятью, то эта обязанность переходит на пользователя. Однако при использовании идущей в комплекте библиотеки lauxlib и функции luaL_newstate из нее, происходит привязка функции l_alloc в качестве системы работы с памятью. Она определена следующим образом:
Как было сказано в начале статьи, в проекте уже имеются переопределенные функции malloc и free, однако нет функции realloc. Нам требуется это исправить.
В стандартном механизме работы с кучей FreeRTOS, в файле heap_4.c, используемом в проекте, отсутствует функция для изменения размера ранее выделанного блока памяти. В связи с этим, придется на основе malloc и free сделать свою реализацию.
Поскольку в будущем возможно изменение схемы распределения памяти (использование другого файла heap_x.c), то было решено не использовать внутренности текущей схемы (heap_4.c), а сделать более высокоуровневую надстройку. Хоть и менее эффективную.
Важно учесть, что метод realloc не только удаляет старый блок (если таковой существовал) и создает новый, но еще и перемещает данные из старого блока в новый. Причем в случае, если в старом блоке было больше данных, чем в новом, то новый заполняется старыми до предела, а оставшиеся данные отбрасываются.
Если не учесть этого факта, то ваша машина сможет выполнить трижды вот такой скрипт из строки «a = 3\n«, после чего упадет в hard fault. На проблему удастся выйти после изучения остаточного образа регистров в обработчике hard fault, из которого удастся узнать, что падение произошло после попытки расширить таблицу в недрах кода интерпретатора и его библиотек. Если же вызывать скрипт по типу «print ‘test’«, то поведение будет меняться в зависимости от того, как соберется файл прошивки (иначе говоря, поведение не определено).
Для того, чтобы скопировать из старого блока данные в новый нам потребуется узнать размер старого блока. FreeRTOS heap_4.c (как и другие файлы, предоставляющие методы работы с кучей) не предоставляет API для этого. Поэтому придется дописать свой. За основу я взял функцию vPortFree и урезал ее функциональность до следующего вида:
Теперь дело за малым, написать realloc на основе malloc, free, и vPortGetSizeBlock:
Добавляем поддержку работы с stdout
Как становится известно из официального описания, сам lua интерпретатор не умеет работать с вводом-выводом. Для этих целей подключается одна из стандартных библиотек. Для вывода она использует поток stdout. За подключение к потоку отвечает функция luaopen_io из стандартной библиотеки. Для поддержки работы с stdout (в отличие от printf), потребуется переопределить функцию fwrite. Я переопределил ее на основе функций, описанных в предыдущей статье.
Без ее определения, функция print в lua будет отрабатывать успешно, но вывода результата не будет. При том на стеке Lua машины не будет никаких ошибок (поскольку формально функция выполнилась успешно).
Помимо этой функции нам понадобится функция fflush (для функционирования интерактивного режима, о чем будет далее). Так как эту функцию нельзя переопределить, то придется назвать ее чуть иначе. Функция является урезанной версией функции fwrite и предназначена для отправки того, что сейчас находится в буфере с последующей его очисткой (без дополнительного перевода каретки).
Получение строк из последовательного порта
Для получения строк для lua-машины я решил написать простенький класс uart-терминала, который:
В самом проекте объект класса nvic устанавливает приоритет прерывания 0x9, что входит в допустимый диапазон (класс nvic описан здесь и здесь).
Формирование строки для Lua-машины
Принятые из объекта uart_terminal байты передаются в экземпляр простенького класса serial_cli, который предоставляет минимальный интерфейс редактирования строки и передачу ее непосредственно в поток, в котором выполняется lua-машина (через вызов callback функции). По принятии символа ‘\r’ вызывается функция обратного вызова (callback). Эта функция должна скопировать себе строку и «отпустить» управление (поскольку во время вызова прием новых байт блокируется. Это не является проблемой при правильно расставленных приоритетах потоков и достаточной низкой скорости UART).
Ссылки на исходники:
Передача строки в Lua интерпретатор и ее исполнение
Сам по себе Lua интерпретатор не умеет принимать код блока построчно, после чего исполнять целый блок самостоятельно. Однако, если установить Lua на компьютер и запустить интерпретатор в интерактивном режиме, то мы можем видеть, что исполнение идет построчно с соответствующими обозначениями по мере ввода, что блок пока не является завершённым. Так как интерактивный режим это то, что предоставляется в стандартной поставке, то мы можем посмотреть его код. Он находится в файле lua.c. Нас интересует функция doREPL и все, что она использует. Дабы не придумывать велосипед, я для получения функций интерактивного режима в проекте сделал порт этого кода в отдельный класс, который назвал по имени исходной функции lua_repl, который использует printf для вывода информации в консоль и имеет публичный метод add_lua_string для добавления строки, полученной из объекта класса serial_cli, описанного выше.
Так как в проекте уже существует единая система инициализации и обслуживания глобальных объектов, то указатель на объект класса lua_repl передается объекту глобального класса player::base здесь. В методе start объекта класса player::base (объявленного здесь. Тут же и происходит его вызов из main), производится вызов метода init объекта класса lua_repl с приоритетом задачи FreeRTOS 3 (в проекте можно назначать приоритет задачи от 1, до 4. Где 1 — наименьший приоритет, а 4 — наибольший). По окончании успешной инициализации глобальный класс запускает планировщик FreeRTOS и интерактивный режим начинает свою работу.
Проблемы, появляющиеся при портировании
Ниже будет приведен перечень проблем, с которыми я столкнулся во время порта Lua машины.
Исполняются 2-3 однострочных скрипта присвоения значения переменной, затем все падает в hard fault
Проблема была в методе realloc. Требуется не просто повторно выделить блок, но еще и скопировать содержимое старого (о чем писал выше).
При попытке напечатать значение интерпретатор падает в hard fault
Тут было уже сложнее обнаружить проблему, но по итогу удалось выяснить, что для печати используется snprintf. Так как lua хранит значения в double (или float в нашем случае), то требуется printf (и его производные) с поддержкой плавающей точки (о тонкостях настройки printf я писал здесь).
Требования к энергонезависимой (flash) памяти
Вот некоторые замеры, которые я сделал, позволяющие судить о том, сколько нужно выделить энергонезависимой (flash) памяти для интеграции Lua-машины в проект. Компиляция производилась с использованием gcc-arm-none-eabi-8-2018-q4-major. Использовалась версия Lua 5.4. Ниже в замерах под фразой «без Lua» подразумевается невключение в проект непосредственно интерпретатора и методов взаимодействие с ним и его библиотеками, а так же объекта класса lua_repl. Все низкоуровневые сущности (включая переопределения для работы функций printf и fwrite) остаются в составе проекта. Размер FreeRTOS кучи составляет 1024*25 байт. Остальное занимают глобальные сущьности проекта.
Требования к оперативной памяти
Так как потребление оперативной памяти зависит всецело от задачи, то я приведу сводную таблицу потребленной памяти сразу после включения машины с разным набором библиотек (она выводится командой print(collectgarbage(«count»)*1024)).
Состав | Использовано RAM |
Lua c библиотекой base | 4809 |
Lua c библиотеками base, coroutine, table, string | 6407 |
luaL_openlibs | 12769 |
В случае использования всех библиотек размер требуемой оперативной памяти значительно растет в сравнении с предыдущими комплектами. Однако его использование в немалой части приложений не обязательно.
Помимо этого так же выделяется 4 кб на стек задачи, в которой происходит выполнение Lua-машины.
Дальнейшее использование
Для полноценного использования машины в проекте далее потребуется описать все требуемые кодом бизнес-логики интерфейсы к аппаратной части или служебным объектам проекта. Однако это уже тема отдельной статьи.
Язык Lua на службе у пентестеров
Содержание статьи
Разработчики давно поняли: чтобы сделать программу по-настоящему гибкой и расширяемой, нужно добавить внутрь хороший скриптовый язык. Тем более что придумывать ничего не надо: есть Lua, который прекрасно интегрируется, чем и воспользовались создатели Wireshark, Nmap и даже малвари.
WARNING
Intro
Хакер #179. Интернет вещей — новый вектор атак
После небольшого изучения темы оказалось, что поддержка языка реализована в популярных утилитах, с которыми приходится иметь дело каждый день, при этом я почему-то обходил такую возможность стороной. Пришло время это исправить.
Расширение для Wireshark
В работе мне довольно часто приходится анализировать большие объемы трафика, причем искать в них вполне конкретные паттерны. Поддержка Lua для написания расширений в Wireshark может сэкономить кучу времени (которую можно потратить, скажем, на сон).
При небольшой сноровке можно писать довольно сложные сценарии для поиска паттернов и анализа трафика. Но для примера мы возьмем что-нибудь простое и показательное — напишем простой скрипт для парсинга полей имени хоста и cookies. И параллельно разберем несколько ошибок, которые могут возникнуть в процессе разработки расширения.
Пример выполнения Lua-скрипта в Wireshark
Скрипт также можно выполнить через консоль, используя tshark:
Так как скрипт небольшой, я приведу код сразу, а далее мы его разберем по частям:
Итак, поехали. Сначала для более удобного вывода нужных нам данных создадим отдельное окно для текстовой информации:
В принципе, можно уже выполнить эту строку после запуска Wireshark, и мы получим желанное окно, которое представлено на скриншоте.
Созданное окно с помощью расширения в Wirehark
Далее создадим переменные, которые будут обращаться к полям host и cookie HTTP-протокола:
Таким образом можно обращаться к любым полям любого протокола, а как мы все помним, этот замечательный снифер из коробки знает структуру колоссального числа протоколов и умеет «молотить» их на понятные части на лету (впрочем, для неизвестного протокола легко прописать структуру).
Теперь напишем функцию инициализации приемника пакетов и установим фильтр на HTTP-протокол:
Далее все совсем просто, переводим в строки полученные переменные и добавляем к созданному окну:
Единственный нюанс: объединить переменные с помощью операции объединения строк нельзя, поскольку такие данные имеют тип userdata, а Lua не может напрямую их изменять. Но это можно обойти через метатаблицы.
Теперь выполним наш скрипт и запустим снифинг пакетов.
Работа нашего расширения в Wireshark
Работает! Но как видишь, у расширения есть минус — он выводит одинаковые кукисы, поэтому в идеале надо сделать таблицу, где будут храниться старые значения, и добавить проверку на уникальность. Но это я оставляю тебе в качестве домашнего задания. Если же ты не справишься или ты из тех людей, которые любят готовые рецепты, то полная версия будет ждать тебя на моем GitHub-аккаунте.
В сноске ты найдешь ссылки на вики и API с более подробной информацией по написанию расширений. Помимо этого, приведены примеры рабочих скриптов, один из которых сохраняет VoIP-звонки из пакетов в отдельные файлы и работает с SQL базой данных.
Расширение для Nmap
Разберем стандартную структуру скрипта. В начале каждого сценария добавляются стандартные библиотеки:
Далее идут метаданные:
После этого идет непосредственно код сценария. Для примера напишем простейший скрипт для брута пользователей популярной CMS LiveStreet.
Вначале определим стандартные переменные и подключим недостающие библиотеки:
Далее составим класс Driver для брута из соответствующей библиотеки. Напишем функцию инициализации с указанием хоста, порта и пути до формы логина:
Функции коннекта и дисконнекта оставим без изменений:
Далее пишем проверку, чтобы посмотреть успешность коннекта и наличие поля для пароля:
Ну и самая главная часть — функция логина. Сначала составляем HTTP-запрос:
Далее указываем результат, после которого считается, что пара логин с паролем, переданные в запросе, были правильными:
В случае с сайтами на WordPress значение будет 302. Другой вариант — проверять появление нового HTML-элемента на странице (например, после логина обязательно появится ссылка для выхода пользователя из системы):
Сохраним полученный скрипт с расширением nse и положим рядом два файла: users.txt и passwords.txt — с некоторым количеством имен пользователей и паролей. В случае LiveStreet можно даже напарсить валидные имена пользователей или добавить этот функционал в скрипт.
Теперь запустим полученный сценарий (полный исходник скрипта можно скачать из моего GitHub-репозитория):
Если все пройдет успешно, то ты увидишь отчет о проделанном брутфорсе.
Отчет Nmap-скрипта для брута пользователей LiveStreet CMS
Как видишь, код не такой сложный, что позволяет быстро написать свой скрипт под определенную цель, с которой ты столкнулся в процессе проведения пентеста.
Шпаргалка по Lua
Первое, что надо знать, — Lua — это язык с динамическим определением данных, то есть переменные получают тип на лету в зависимости от своего содержания. Всего используется восемь типов:
Условные операторы и циклы довольны стандартны:
Операции также почти все знакомые:
Функцию мы рассмотрим на примере тестового скрипта в нашей программе. Более подробно про этот язык можешь прочитать по ссылкам в сносках. Для написания программ достаточно будет Notepad++ или Sublime Text.
Outro
Вообще, использование Lua растет и растет. Так, на момент написания статьи была опубликована новость, что появился плагин для Olly Debugger версии 2+, добавляющий поддержку Lua.
Впрочем, язык не ограничивается одними программами для ИБ-сферы и может пригодиться в самых разных ситуациях. Как я уже говорил, его часто используют для игр, например World of Warcraft в своих аддонах (предоставляя API и довольно неплохую документацию по нему). Помимо игровых приложений, его задействуют такие программы, как Setup Factory. На нем написаны многие инсталляторы: Apache, nginx, Adobe Photoshop Lightroom, VLC и многие другие. Там, где нужно реализовать программируемое поведение программы, — интегрированный интерпретатор Lua точно будет очень неплохим вариантом.
Встраиваем компилятор в свою программу
Итак, главная фишка Lua в том, что его можно встроить в любую программу и предложить всем желающим расширить ее возможности. Как это сделать? Я покажу на примере Visual C++ программы. Стоит отметить, что сам Lua написан на чистом C, и это создает некоторые сложности при добавлении в программы с плюсами. На просторах Сети предлагается несколько вариантов добавления поддержки Lua в C++ программы. Кто-то предпочитает скачивать уже прекомпилированные библиотеки или готовые фреймворки, кто-то — компилировать из исходников самому, а кто-то — подключать исходники к своему проекту и, немного разобравшись с настройками в проекте, добиться действительного добавления Lua в свою программу. Мы с тобой как раз и разберем последний вариант.
Создадим новый консольный проект (я пользуюсь Visual Studio 2010). После чего добавим через свойства проекта директорию с исходниками:
где в каждом из разделов добавим в соответствующие поля MARKDOWN_HASHd8a960f245cb01e9a35ba16cf2a54fe6MARKDOWN_HASH путь к исходникам языка. Далее создаем раздел Lua, куда добавляем все файлы из папки MARKDOWN_HASH25d902c24283ab8cfbac54dfa101ad31MARKDOWN_HASH :
Добавление директории с исходниками Lua в проект.
Как я уже упомянул выше, Lua написан на чистом C, поэтому применим небольшой хак — добавим в начало своего файла следующие строки:
Далее инициализируем наш язык и подключим его библиотеки:
Теперь создадим цикл, который будет проверять входящие данные, пока они не закончатся (или не переполнят буфер), и выполнять их:
Теперь можно компилировать проект и запустить.
Работаем в скомпилированном интерпретаторе Lua
В ходе компиляции могут возникнуть ошибки:
Следующий проект создадим аналогичный первому, но теперь будем запускать свои сохраненные скрипты в самой программе, инициализируем Lua и добавим после:
Это и есть обещанный пример объявления и работы с функцией. Теперь загрузим его в память и выполним:
Теперь рассмотрим возможность вызова функций C-программы из Lua-скриптов. И для начала объявим функцию, которая будет вызываться из нашего языка:
Далее регистрируем функцию:
Прописываем функцию, которая загружает по переданному пути скрипт:
И запускаем загруженный скрипт:
Но, если заметил, вызов немного отличается от предыдущего проекта. Константа MARKDOWN_HASH5301b763e99f84122b8de40237a602d5MARKDOWN_HASH используется вместе с функцией MARKDOWN_HASHeb8e633c3ab4c927bba1d52e54fb33c3MARKDOWN_HASH для подсчета стека до и после вызова. Компилируем, не забывая про описанные выше ошибки.
Теперь пропишем вызов этой функции из Lua в отдельный скрипт и назовем его MARKDOWN_HASH21b648082b7edb48f91278eceb83c54aMARKDOWN_HASH :
Вызовем полученный файл из нашей программы:
И получим следующий вывод в консоль:
Вывод вызываемой C-функции из Lua
Готовые исходники проектов ты можешь взять из GitHub, но не забывай, что в них прописаны пути к моей Lua и для экономии места вырезаны функции обработки ошибок Lua-скриптов. Еще я не подробно расписал одну интересную тему — работу со стеком, которая позволяет обмениваться данными с основной программой, хотя главное мы затронули в последнем проекте.
Борис dukeBarman Рютин
Известный реверсер (@dukebarman), докладчик на множестве конференций, постоянный автор «Хакера» и большой фанат вселенных Blizzard. В данный момент работает руководителем проектов в «Digital Security» (@DSecRU).