Как склеить луа стиллер

Добавил пользователь Дмитрий К.
Обновлено: 09.09.2024

-- Cortex Command Lua Scripting Tutorial --
___________________________________________

Автор - Ken "CaveCricket48" Do
Переводчик - Программист/ximximik

Владельцем всех прав на перевод является ximximik, поэтому любое копирование, распространение и изменение перевода без согласия владельца (ximximik) запрещено.

Вступление
Этот учебник научит вас, как использовать Lua в Cortex Command. Он написан для людей, которые не встречилались раньше с программированием совсем, и поэтому в нем не будут использованы сложные слова, которые есть в других учебниках. Лучше будет, если вы уже будете знать как работать с ini-кодом, прежде чем приступите к изучению программирования.

1.0 - Начало
-> 1.1 Создание Lua файла
-> 1.2 Прикрепление скрипта к объекту

2.0 - Основы Lua
-> 2.1 Комментарии
-> 2.2 Типы данных
-> 2.3 Переменные
-> 2.4 Операторы
-> 2.5 Таблицы
-> 2.6 Оператор "For"

3.0 - Cortex Command Lua stuff
-> 3.1 Этапы скрипта
-> 3.2 Функции
-> 3.3 Указатели

4.0 - Функции и свойства в Вики

5.0 - Пример скрипта
-> 5.1 Мина

----- 1.0 - Начало -----
________________________

В Cortex Command, Lua позволяет объектам делать то, что они обычно не были бы в состоянии сделать или создать особенные эффекты.

- 1.1 Создание Lua файла-

Принцип работы скриптов Lua в Cortex Command состоит в том, что скрипты присоединены к объектам, созданным с помощью ini. Чтобы сделать файл Lua в простом блокноте в Windows, просто создайте новый текстовый файл, перейдити в меню "Файл", "Сохранить Как. ", измените "Тип файла:" на "Все файлы" , и измените ".txt" в имени файла на ".lua". Если нет никакого расширения, просто добавьте ".lua" в конце имени файла. Потом нажмите "Сохранить". Вы создали файл Lua.

Совет от переводчика: Используйте Notepad++ вместо стандартного блокнота, так как в нем показывается весь синтаксис Lua, и вообще в нем легче работать.

- 1.2 Прикрепление скрипта к объекту -

Чтобы прикрепить Lua скрипт к объекту, созданному в ini файле, просто поместите "ScriptPath = " в блоке кода объекта с соответствующим табулированием.

----- 2.0 - Основы программирования Lua -----
________________________

--- 2.1 Комментарии ---

Комментарии в скрипте Lua - строки текста, которые не читаются игрой/программой, и в основном используются, чтобы аннотировать скрипт. Чтобы аннотировать, сделайте 2 дефиса, и что-нибудь после них на той же самой строке - комментарий.

--- 2.2 Типы данных ---

Типы данных - типы данных, которые используются для переменных и свойств. В CC есть 5 основных типов данных: числа, текст, boolean (логическая переменная), вектор, и ноль (nil). Первые 2 - понятно из названия то это. boolean - логическая переменная, т.е. её значения это истина (true) или ложь (false). Вектор в CC - координаты (X, Y). Числовые данные используются, печатая только числа. Текст используется при наличии пары кавычек ("") с текстом между ними. Вектор используется, следуя за словом "Vector " 2 числами разделенными запятой в двух круглых скобках. Ноль - обозначается как "nil" (чтобы не быть перепутанным с числом ноль).

--Логическая переменная
true
false

--Векторы
Vector(1,1)
Vector(5,2)
Vector(-7,6)

--- 2.3 Переменные ---

Переменные - это то в Lua, чему Вы можете дать значение. Они используются для того, чтобы хранить информацию, чтобы Вы могли снова вызвать это позже. Переменная состоит из своего имени, одного знака " bbCodeBlock">

Чтобы изменить переменную, просто напечатайте ту же самую переменную снова с новым значением:

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

В результате X будет равен 5.

Есть 2 типа переменных в CC: глобальная переменная и локальная. Глобальные переменные могут быть изменены скриптом любого объекта, вновь меняющие значение, и только одна переменная с таким именем может существовать. Глобальные переменные похожи на те выше в примерах.

Локальные переменные - переменные, у которых могут быть множественные копии, каждая из них привязана к объекту со скриптом, который создал их. Чтобы создать локальную переменную, напечатайте "local" перед именем переменной, разделив их пробелом. Установка значения локальной переменной равного значению другой локальной переменной работает подобным способом как и у глобальных переменных.

Это действие сделало переменную "varB" равной 5. Заметьте, что слово "local" не использовалось, когда мы выбирали переменную "varA".

Другое полезное свойство переменных - то, что Вы можете использовать переменные, чтобы установить определенное свойство в объект, и хранить объект в переменной. Храня объект в переменной, с ним легче взаимодействовать и легче вызывать указателем. Чтобы использовать переменные, для изменения свойства объекта, Вы нуждаетесь в переменной, которая является определенным указателем на объект (мы разберем это позже), сопровождающаяся точкой, названием свойства, знаком " bbCodeBlock">

"thing" это указатель на объект, "GetsHitByMOs" это свойство объекта в ini, и "false" это значение.

Обычно используют указатель, который уже определен как "self". Указатель всегда обращается к объекту, к которому присоединен скрипт.

--- 2.4 Операторы ---

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

Этот код проверяет, если переменная "X" равна "1", то совершает "содержимое" (" " не фактическое действие, только указатель места заполнения для других команд, до которых мы доберемся позже), если оператор будет истиной. Обратите внимание на "end" внизу блока кода, который говорит, что " ", часть этого оператора, закончено. Если Вы не напечатаете "end" после оператора (конец оператора после тоже), то Вы получите ошибку. Также заметьте, что есть 2 знака "=" в "строке "if". "==" нужно для того, чтобы проверить равенство, и " bbCodeBlock">

Внутри операторов "if", могут находится операторы "else". Эти операторы проверяют, выполняется ли условие оператора "if", и если оно не истинно (не выполняется), то
тогда совершаются действия в их содержимом.

Условие "Elseif" похоже на второй "if" оператор и работает также:

В операторе могут быть много условий "elseif", но только одно "else".

Списки (также называемые как таблицы и массивы) похожи на переменные, у которых могут быть множество значений. Чтобы создать список, напечатайте название списка сопровождаемое знаком " bbCodeBlock">

Добавление значения к списку подобно присвоению/изменению значения переменной. Вы должны напечатать имя списка, сопровождаемое парой квадратных скобок с заданным номером в списке, знаком " bbCodeBlock">

Числовой символ с именем списка перед этим, говорит число элементов в списке.

Тот же самый список как в вышеупомянутых примерах, имеет значение переменной "numbercheck" равное 2.

Чтобы очистить все значения списка, напечатайте имя списка, знак " bbCodeBlock">

-- Очистка всего списка
thelist = <>

-- Очистка одного значения
thelist[1] = nil

Списки могут быть глобальными и локальными, как переменные.

--- 2.6 Оператор For ---

Поскольку операторы используются, чтобы сделать множество повторяющихся действий в одном коротком участке памяти скрипта. Формат написания оператора "For" ниже.

Пример использования оператора "for".

Давайте рассмотрим каждую часть подробно:

for
- Стартовое ключевое слово

i = 1
- "i" - временная переменная, которая используется оператором "for", для сохранения, сколько раз он совершил действия (включая 1). "i" может быть изменен на любое имя как переменная. "1" это начальное значение для отсчета.

5
- Кол-во повторений " ".

Чтобы прервать оператор "for", включите "break" в оператор "if", который проверяет значение оператора "for" для соблюдения условия.

----- 3.0 - Cortex Command Lua stuff -----
________________________

--- 3.1 Этапы скрипта ---

Этапы скрипта - это части скрипта, которые выполняются только, когда объект был создан, когда он существует, или когда уничтожен. Этапы скрипта тоже требуют "end" как и операторы условий.

-- в промежутке между "function Create(self)" и "end" будет работать, только когда объект будет создан. (происходит только один раз)
function Create(self)

end

-- в промежутке между "function Update(self) и "end" будет работать, только когда этот объект существует.
function Update(self)

end

-- в промежутке между "function Destroy(self) и "end" будет работать, только когда этот объект уничтожается. (происходит только один раз)
function Destroy(self)

end

Функции - часть Lua, которые уже встроены в игру и взаимодействуют с содержимым в Cortex Command. Формат функции - название группы функции, двоеточие, имя функции, и её параметры (нужные данные для этого, чтобы это работало) разделяются запятыми в круглой скобке. Большинство названий групп функции Cortex Command могут быть заменены указателем чем-нибудь на то, с чем они смогут взаимодействовать. Пример функции ниже:

Информация о функции из Википедии Cortex Command Lua:

AddImpulseForce
Adds impulse force (or instant momentum) to this MovableObject for the next time Update() is called.

- An Vector with the impulse force vector that will directly be added to this MovableObject's momentum next Update(). In kg * m/s.
- A Vector with the offset, in METERS, of where the impulse is being applied relative to the center of this MovableObject.

"anactor" - указатель на актора, "AddImpulseForce" - название функции, и содержимое круглой скобки - данные, необходимые для работы функции. В блоке информации о функции, "Return value", - это то, что выводит функция, когда она выполняется.
Большинство функций в Cortex Command может быть найдено в wiki, так что убедитесь проверив это.

--- 3.3 Указатели ---

Хорошо, мы наконец добрались до указателей!
В Cortex Command указатели - переменные, которые "хранят" объект в себе. Или Вы можете представить это, как гигантскую стрелку, указывающую на объект. независимо от того, что свойства Вы устанавливаете на указатель или используете функцию на нем, это произведет эффект на тот объект, на который он "казывает.

Есть несколько способов создать указатели. Обычно используют специальный оператор "for", который проверяет все объекты определенного типа и является оператором, который проверяет, является ли объект тем, который вам нужен. Кусок кода скрипта ниже присоединен к объекту, который ищет актора, с именем "Dummy" и затем устанавливает на него указатель, названный "local pointer".

for actor in MovableMan.Actors do
if actor.PresetName == "Dummy" then
local pointer = actor
break
end
end

Что делает вышеупомянутый кусок - проверяет всех акторов в игре, пока он не находит агента с "PresetName" как "Dummy". Тогда это устанавливает указатель на этого актора как "local pointer" и останавливает оператор "for" с "break". Термин "актор" является указателем на актора, и передан "local pointer" для более простой справочной информации.

Здесь 2 других строки "for", которые получают указатели на устройствах и частицах.

----- 4.0 - Функции и свойства в Cortex Command Wiki -----
________________________

----- 5.0 - Пример скрипта -----
________________________

--- 5.1 Mine ---
Этот скрипт для простой мины, которая взрывается, когда акторы находятся в пределах определенного расстояния объекта со скриптом.

-- Пока объект существует
function Update(self)

-- Проверка всех акторов
for actor in MovableMan.Actors do

-- Использует функцию и свойство вектора, чтобы сохранить расстояние между актором и объект со скриптом в переменной.
local distcheck = SceneMan:shortestDistance(self.Pos,actor.Pos,true).Magnitude

-- Оператор, проверяющий, если проверяемое расстояние (переменная "local distcheck") меньше чем 10. Если так, совершите действие.
if distcheck
Я мертв :)


Lua не является интерпретированным языком и генерирует байт-код из другой программы, например, Java. Эта программа работает на виртуальной машине Lua VM. Большинство таких устройств называются стеками, но Lua отличается тем, что она основана на регистрах и является одной из первых широко используемых VM в игровой сфере.

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

Коротко про Луа


Lua — это простой в освоении и быстрый скриптовый язык с синтаксисом типа pascal. Это позволяет начинающим программистам писать свои собственные программы. Он используется во многих играх и приложениях для расширения существующей функциональности и программирования пользовательских последовательностей. В интернете есть много обучающих программ, которые помогают работать на этом языке, в том числе много информации можно найти на домашней странице Lua.

Это гибкий язык, он используется на нескольких направлениях:

Функционал этой программы можно использовать в L?VE, Defold и, конечно же, в Pico-8.

Основы синтаксиса

Lua использует очень упрощенный синтаксис. Пользователю не нужно применять точки с запятыми или большие пробелы, потому что эти знаки сами интерпретируются программой. Все переменные в скриптах Lua для quik по умолчанию глобальны. Поэтому всегда нужно указывать, когда локальные значения нужны для текущей области и предварительно объявлять их. Фактически на практике существует очень мало случаев, когда в действительности нужны глобальные значения.

Можно определить локальную переменную с ключевым словом local. При этом значение ее объявлять не нужно. Глобальные переменные означают контекст текущего сценария и поэтому не являются такими для каждого отдельного скрипта в игре, которые каждый работают самостоятельно и автономно.

Переменные могут содержать любую последовательность цифр, букв и подчеркиваний, но они не должны начинаться с числа. Null представляет ключевое слово, когда переменные не определены. Необъявленные неотличимы от объявленных переменных, которые не имеют значения. Примеры скриптов Lua:


Блоки кода и операторы if


Каждое значение в таблице учитывается, nil — если оно не установлено. Это означает, что невозможно определить, объявлено ли значение в таблице или это просто нуль, если оно никогда не было определено раньше. Это означает, что если ключи в таблице указаны и установлены на нуль, это не будет иметь никакого эффекта, вернее, оно будет таким, каким было бы в случае, если бы их и не определяли вовсе. В Lua таблицы с числовыми ключами начинаются с индекса 1, а не 0. Это то, что вызывает большую головную боль для программистов. Можно разделить встроенные элементы таблицы запятой или точкой с запятой.

Сложный тип данных

Таблицы считаются сложным типом данных в Lua — это означает, что переменные в них просто ссылаются на один и тот же объект в памяти, поэтому нельзя напрямую сравнивать два значения и ожидать, что они проведут что-либо. Тем не менее строки, числа, буквы и другие примитивные типы данных можно сравнивать напрямую.

Цифры в Lua используют только в одном виде — своего собственного числа. Нет таких типов, как int, float, long, double и других, как в прочих языках и это надо учитывать перед тем, как устанавливать Lua-скрипты. Технически все значения в Lua представляют собой числа с плавающей запятой.


Циклические типы Loops

Существует четыре типа циклов Loop, что в переводе означает петля или виток.

While (промежуточный) — это самый простой вид цикла и функций, как и следовало ожидать на других языках, и означает, что проверка проводится перед запуском внутреннего блока.

Numeric for loop (цифровой). Цикл применяет переменную local для своей области и увеличивает или уменьшает ее. Пользователь объявляет и определяет переменную, которую нужно использовать и за которой следует значение цели.

Generic for loop (генерируемый) — использует функцию итератора. Она вызывается повторением каждого цикла и возвращает значения, которые будут применены в ней.

Repeat loop (повторение) похож на while, за исключением того, что проверка условий проводится в конце блока, а не в начале. Перед тем как открыть lua скрипт нужно учитывать, что код внутри будет запускаться не менее одного раза. Цикл повторения обычно избегают в разработке этого языка, поскольку его прецедент ограничен и трудно читаем из-за того, что не использует стандартный do..end синтаксис.


Метод функций

Функции — это еще один сложный тип данных, можно передавать как переменные, и также можно вызвать их, чтобы выполнить код внутри. Они похожи на методы на других языках.

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


Глобальное пространство имен


Игровой лунный сценарий

Это очень простые первые шаги по написанию программы, но они являются хорошими отправными точками для взлома скриптов Garry Mod 10 Lua. Первый шаг — создают фактический файл сценария Lua. Открывают текст или редактор — блокнот будет отлично работать для этой цели. Далее сохраняют этот файл, прописывая, как на рисунке.


  • — имя пользователя паролей.
  • — каталог Steam для Garry’s Mod.


Функции — это модульные фрагменты кода, которые можно использовать несколько раз. В этом примере показано, как запустить скрипт Lua. Здесь создан список строк, но пользователь также может вставлять и другие объекты. Таблицы являются ассоциативными или хешированными. Это означает, что каждый элемент в списке может ссылаться на ключ.


Возможности Lua-Quick-Try-Out


Эта консоль предназначена для прямого ввода кода Lua (например, команда команды Lua (8 + 9) сразу выводит результат 17).

SAMP Lua-скрипты — это библиотека lua для MoonLoader, которая добавляет некоторые команды, чтобы упростить модификацию SA: MP,SAMP. Events дает возможность обрабатывать SA: MP входящие и исходящие низкоуровневые сетевые пакеты очень простым способом. Можно переписать данные, установив все аргументы в таблице в правильном порядке или можно прервать обработку любых пакетов, вернув false, которая предотвращает изменение пользователем позиции игрока.Перед тем как установить Lua-скрипт samp, можно добавить собственный обработчик пакетов.


Дистрибутив Lua-WoW

Так называется дистрибутив исходного кода Lua, модифицированный для соответствия среде аддонов World of Warcraft.

Эта версия языка настроена на соответствие среде Lua World of Warcraft. Все параметры совместимости 5.0-5.1 отключены, кроме openlib. Библиотека битлиба работает. Некоторые wow Lua скрипты — специфичные, Lua-библиотечные функции реализованы.

Игровой движок Pico-8



Pico-8 предназначена только для отображения символов верхнего регистра, поэтому можно просто набирать шрифт, нет необходимости использовать Shift или Caps-lock. Редактор также имеет синтаксическую раскраску для кода. Если посмотреть в верхнем правом углу редактора, можно увидеть несколько значков. Это встроенные инструменты для создания спрайтов, карт, звуков и музыки. Этот пример довольно простой, но дает представление о редакторе и языке.

Чтобы исследовать более интересные примеры, можно перейти в каталог DEMOS. Для этого вводят: INSTALL_DEMOS. После открывается каталог DEMOS с несколькими примерами программ для изучения. Одним из главных преимуществ Lua является его простота. Некоторые компании используют этот язык исключительно из-за этого, поскольку думают, что их сотрудники смогут работать лучше.

Некоторые очень простые языки, такие как Bash или Batch, не будут достаточно сильными, чтобы выполнять требуемые задачи, но Lua является одновременно мощной и простой программой. Другим важным преимуществом Lua скриптов для css v34 является их способность внедряться, что признается одной из важнейших характеристик этого продукта на протяжении всего этапа его развития. Такие игры, как World of Warcraft или ROBLOX, имеют возможность встраивать Lua в свое приложение.

Related Posts

При замене защитного стекла на смартфоне мало кто не беспокоится о соблюдении правил и выполняет…

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

Файл Luac содержит две части: заголовок файла и тело функции.

Формат заголовка файла

Первое поле ** signature ** Он определен в заголовочном файле lua.h, это LUA_SIGNATURE, а значение равно "\ 033Lua", где \ 033 представляет ключ. LUA_SIGNATURE - это 4 байта в начале файла Luac, это магический номер Luac, который используется для идентификации его как файла байт-кода Luac. Магическое число относительно распространено в различных двоичных форматах файлов: это первые несколько байтов определенного файла, которые указывают конкретный формат файла.

version Поле указывает версию формата файла Luac, а его значение соответствует скомпилированной версии Lua. Для файла Luac, созданного в версии 5.2 Lua, его значение равно 0x52.

** format ** Поле является идентификатором формата файла. Значение 0 представляет официальный формат, указывая, что это официально определенный формат файла. Значение этого поля не равно 0, что указывает на то, что это измененный формат файла Luac и может не загружаться обычной официальной виртуальной машиной Lua.

** endian ** Указывает порядок байтов, используемый Луаком. В настоящее время порядок байтов в основных компьютерах в основном включает LittleEndian и BigEndian. Если значение этого поля равно 1, это означает LittleEndian, а если оно равно 0, используется BigEndian.

** size_int Поле ** указывает размер байтов, занятых типом int. Поле size_size_t указывает размер байтов, занятых типом size_t. Существование этих двух полей должно быть совместимо с процессорами различных ПК и мобильных устройств, а также с их 32-разрядными и 64-разрядными версиями, поскольку на конкретном процессоре размер байтов, занимаемый этими двумя типами данных Отличается.

** size_Instruction Поле ** указывает размер одной инструкции в блоке кода байтового кода Luac. В настоящее время размер, занимаемый инструкцией инструкции, составляет фиксированные 4 байта, что означает, что Luac использует тот же формат инструкции длины, что, очевидно, обеспечивает удобство для хранения и декомпиляции команд Luac.

** size_lua_Number ** Поле идентифицирует размер данных типа lua_Number. lua_Number представляет тип числа в Lua, который может хранить целочисленные типы и типы с плавающей запятой. В коде Lua он использует LUA_NUMBER, его размер зависит от типа и размера данных с плавающей запятой, используемых в Lua, для плавающей запятой одинарной точности, LUA_NUMBER определяется как float, то есть 32-битный размер, для двойного Для точности с плавающей запятой она определяется как double, что означает длину 64 бита. В настоящее время Lua, скомпилированный на macOS, имеет размер 64 бита.

** lua_num_valid ** Поле обычно 0, используется для определения того, может ли тип lua_Number работать нормально.

** luac_tail ** Поля используются для сбора данных для ошибок преобразования. В Lua он использует LUAC_TAIL, который является фиксированной строкой содержимого: "\ x19 \ x93 \ r \ n \ x1a \ n".

После заголовка файла следует тело функции. В файле Luac вверху находится тело функции верхнего уровня. Тело функции может содержать несколько подфункций. Подфункции могут быть вложенными функциями или замыканиями. Они состоят из констант, инструкций кода, значений Upvalue и номеров строк. Локальные переменные и другая информация.

Тело функции

** ProtoHeader ** глава Прото. Его определение таково:

** code ** код операции lua vm
constants Оператор lua фактически удаляет логотип, соответствующий ключевому слову

Это оператор lua, поэтому его можно увидеть в файле luac, и можно предположить, что примерный оператор lua

** protos ** Следующее тело функции хранится в цепочке
** lines ** Сколько строк выражения lua существует в этой функции
** loc_vars ** Количество локальных переменных внутри функции
** names ** Имя локальной переменной внутри функции

Luac.exe хранится анализ процесса luac

Тело функции luac разделено на основную функцию и общую функцию в luac.exe.
1. Сохраните заголовок основной функции, код и строку оператора lua для хранения
2. Храните обычные функции
2.1, хранить тело обычной функции
2.2. Просматривать связанный список обычных функций до конца
3. Сохраните количество строк основных функций, параметров и имен параметров

Lua исходный код luac.c, DumpHeader Формат заголовка luac сохраняется и в ** DumpFunction ** Тело функции сохраняется.

** DumpHeader ** Формат заголовка байта LUAC_HEADERSIZE (12) сохраняется.

Процесс рекурсивного обхода тела хранимой функции.

Луак декомпиляция

Получить lua51 VM

функция lua_execute

В lua vm luac объясняет, что функцией для выполнения кода операции luac является luaV_execute, но некоторые скрывают имя функции lua_execute. Ниже приведена функция execute, найденная с помощью lua_resume.

Вызовите функцию luaD_rawrunprotected в lua_resume, передайте указатель функции resum в качестве параметра

Вызовите luaV_execute в функции resum

В luaV_execute есть 38 случаев переключения, а затем в lua vm игры также находится функция исполнения, что также составляет 38 случаев переключения. Анализируется таблица сравнения кодов операций:

В хакерской практике достаточно часто возникает потребность
внедрить в готовый exe-файл свой код (необязательно вредоносный) так, чтобы он получал управление первым, не вызывал ругательств со стороны антивирусов и вообще по возможности вел себя максимально скрытно, не бросаясь в глаза. Как это сделать? Мы знаем, как, и сейчас обстоятельно тебе это объясним.

Правильные хакеры так себя не ведут и склеивают программы самостоятельно. И это совсем нетрудно! Нужны лишь верный друг hiew и минимальные навыки программирования на Си. Но прежде чем брать быка за рога, сделаем одно важное уточнение. Внедряемый код не обязательно должен быть вирусом, червем, руткитом или любой другой вредоносной заразой, поэтому, во избежание недоразумений, условимся называть его X-кодом.

Как мы будем действовать

Всякий exe-файл импортирует одну или несколько динамических библиотек (Dynamic Link Library, или сокращенно DLL), прописанных в таблице импорта. При запуске exe-файла системный загрузчик анализирует таблицу импорта, загружает все перечисленные в ней динамические библиотеки, вызывая функцию DllMain для инициализации каждой DLL, и только после этого передает управление на оригинальную точку входа (Original Entry Point, или сокращенно OEP) запускаемого exe-файла.

Подготовка к экспериментам

$inject.exe
I'm nezumi

Берем в свои загребущие лапы hiew, открываем файл inject.exe, переходим в hex-режим по , давим для отображения PE-заголовка, нажимаем (Dir) и среди прочих элементов IMAGE_DATA_DIRECTORY выбираем секцию импорта (Import), расположенную в нашем случае по RVA-адресу, равному 5484h и раскинувшуюся в ширину на целых 28h байт (смотри рисунок 1).

Клавиша переносит нас к структуре Import Directory Table, о которой мы поговорим чуть позже. А пока обсудим, как найти указатель на Import Directory Table при отсутствии hiew'а.

Двойное слово, лежащее по смещению 80h от начала PE-заголовка (легко опознаваемого визуально по сигнатуре PE), и есть RVA-адрес, указывающий на Import Directory Table, а следующее двойное слово хранит ее размер. Так что для поиска таблицы
импорта hiew совсем необязателен.

Таблица импорта представляет собой достаточно сложное сооружение иерархического типа. Вершину иерархии занимает структура Import Directory Table, фактически являющаяся массивом подчиненных структур типа IMAGE_IMPORT_DESCRIPTOR, каждая из которых содержит RVA-указатель на имя загружаемой динамической библиотеки, ссылки на OriginalFirstThunk и FirstThunk с именами/ординалами импортируемых функций (причем поле OriginalFirstThunk не является обязательным и может быть равно нулю). Два других поля — TimeDateStamp (временная отметка) и ForwarderChain (форвардинг) - также необязательны, и потому для подключения своей собственной DLL нам необходимо заполнить всего лишь два поля структуры IMAGE_IMPORT_DESCRIPTOR: Name и FirstThunk, а также создать таблицу Import Address Table (сокращено IAT), импортирующую по меньшей мере одно имя (в данном случае
dummy).

Если вместо стройной иерархии структур в нашей голове образовалась каша, не стоит волноваться — это нормально! Постепенно она утрясется и все структуры встанут на свои места, так что оставим их дозревать, а сами сосредоточимся на текущих проблемах. Чтобы внедрить X-DLL в Import Directory Table, необходимо добавить еще один экземпляр структуры IMAGE_IMPORT_DESCRIPTOR. Но просто так сделать это не получится, поскольку сразу же за концом Import Directory Table начинается IAT первой динамической библиотеки, и нам просто некуда втиснуться, если, конечно, не перенести Import Directory Table в какое-нибудь другое место! А что?! И перенесем!

Теперь, прокручивая файл, клацаем

до тех пор, пока не выйдем на оперативный простор свободного места, оккупированного длинной вереницей нулей. В нашем случае это место располагается по адресу 405810h, непосредственно за концом таблицы импорта.

Далее нам необходимо скопировать оригинальную Import Directory Table на новое место, не забыв при этом зарезервировать место для одного элемента структуры типа IMAGE_IMPORT_DESCRIPTOR, в который мы чуть позже поместим нашу динамическую библиотеку. Она будет проинициализирована самой первой, что очень полезно для борьбы с антивирусами, иммунизирующими exe-файлы путем прививки им специальной dll-вакцины, выполняющей проверку целостности содержимого образа исполняемого файла.

Поскольку, как нетрудно подсчитать, размер структуры IMAGE_IMPORT_DESCRIPTOR составляет 14h байт, а незанятая область начинается с адреса 405810h, мы должны передвинуть курсор по адресу 405824h, нажать , выделить 28h байт (размер оригинальной Import Directory Table) и нажать еще раз, а потом обязательно переместить курсор в начало выделенного блока. Далее жмем (Get Block), вводим имя файла, в который мы только что сохранили блок, - idt-org и считываем его с диска.

Теперь возвращаемся в начало файла и корректируем RVA-адрес таблицы импорта, который в данном случае составит 5824h. У тебя может возникнуть вопрос: почему 5824h, а не 405824h?! Да потому что RVA-адреса получаются путем вычитания базового адреса (прописанного в заголовке PE-файла и в нашем случае равного 400000h) из виртуального адреса (равного 405824h). Причем, с учетом порядка старшинства байт, принятого на процессорах x86 (младшие биты располагаются по меньшим адресам), мы должны записать 24 58, а не 58 24, как делают многие начинающие хакеры, удивляясь потом, почему оно не работает.

Значит, открываем файл inject.exe в hiew'e, находим PE-сигнатуру, опускаем курсор вниз на 80h байт, видим там 84 54 (смотри рисунок 1), нажимаем для разрешения редактирования, меняем адрес на 24 58, сохраняем изменения по и выходим… за пивом. Пиво для хакеров — это святое!

Проверяем работоспособность файла — а вдруг она пострадала?! Запускаем inject.exe и (если все операции были проделаны правильно) на экране появится триумфальное приветствие. В противном же случае система откажется загружать файл или выбросит исключение.

Смочив пересохшее горло, приступаем к самой сложной и самой ответственной части — заполнению структуры IMAGE_IMPORT_DESCRIPTOR. Начнем с того, что переместим курсор в конец Import Directory Table, подогнав его к адресу 405850h, и запишем имя функции-пустышки (dummy), оканчивающееся нулем и предваренное двумя нулями, а следом за ним – имя внедряемой динамической библиотеки injected_dll.dll. Впрочем, порядок их расположения может быть и другим, системному загрузчику на такие мелочи уже давно положить.

Сделав это, перемещаемся на первый байт, ранее зарезервированный нами для структуры IMAGE_IMPORT_DESCRIPTOR, и начинаем колдовать. Первое двойное слово оставляем равным нулю. За ним идут 4 байта, отведенные для TimeDataStamp, и мы, желая слегка поизвращаться, занесем сюда IAT, то есть двойное слово, содержащее RVA-адрес импортируемой функции. В нашем случае эта функция зовется dummy, а ее имя (предваренное двумя нулями!) начинается с RVA-адреса 5850h. Учитывая обратный порядок байт на x86, пишем: 50 58. Пропустив следующее двойное слово (Forwarder Chain), в поле Name записываем RVA-адрес имени внедряемой динамической библиотеки injected_dll.dll, в нашем случае равный 5858h. Остается заполнить последнее поле — Import Address Table, содержащее RVA-адрес таблицы IAT, размещенной нами поверх поля TimeDateStamp с RVA-адресом, равным 5814h.

Вот, собственно говоря, и все… После добавления новой структуры IMAGE_IMPORT_DESCRIPTOR в массив Import Directory Table, последний будет выглядеть так:

Остается сущая мелочь. Надо вернуться в начало файла, отсчитать от PE-заголовка 80h байт, исправив указатель на таблицу импорта с 5824h на 5810h и увеличив ее размер до 3Сh. Сохраняем проделанные изменения и, набрав побольше воздуха в грудь, запускаем файл
inject.exe:

$inject.exe
hello,world!
I'm nezumi
good-bye,world!

Копирование X-DLL в NTFS-stream

Теперь запускаем файл inject.exe и убеждаемся, что его работоспособность в результате последних манипуляций ничуть не пострадала.

Переход от теории к практике

Внедрение своей собственной динамической библиотеки - это, конечно, очень хорошо, но на практике гораздо чаще приходится сталкиваться с тем, что требуется
внедрить чужой исполняемый файл. Что делать?! Преобразовывать его в DLL?! Конечно же нет! Достаточно просто слегка доработать нашу X-DLL, научив ее запускать exe-файлы посредством API-функции CreateFile, при этом сами исполняемые файлы можно (и нужно) поместить в именованные NTFS-потоки, число которых фактически неограниченно. Причем, если внедряемый exe тащит за собой динамические библиотеки или другие компоненты, они также могут быть внедрены в NTFS-потоки (естественно, в текущем каталоге их уже не окажется, и потому исполняемый файл придется подвергнуть правке на предмет изменения всех путей). Если же этот файл упакован (а большинство боевых утилит типа систем удаленного администрирования редко поставляются в открытом виде), наша X-DLL может перехватить API-функции CreateFile/LoadLibrary, автоматически отслеживая обращения к отсутствующим файлам и подсовывая вместо них соответствующие им именованные NTFS-потоки.

Другой немаловажный момент. Отправляя exe-файл с внедренной в него X-DLL по почте, записывая его на лазерный диск или любой другой не-NTFS-носитель, мы теряем все именованные потоки, и программа тут же отказывает в работе, ругаясь на то, что не может найти dll.

Повторяем процедуру пересылки файла по электронной почте еще раз, распаковываем полученный архив, запускаем inject.exe, и… о чудо! Он работает!

Заметай за собой следы (для грамотных парней)

Некоторые файлы (особенно упакованные протекторами) скрупулезно следят за своей целостностью и на попытку внедрения реагируют, прямо скажем, не совсем адекватно. Однако поскольку X-DLL получает управление вперед остальных, она может восстановить таблицу импорта в памяти, как будто все так и было, словно к ней никто и не прикасался. Для этого достаточно вызывать API-функцию
VirtualProtect, присвоив соответствующим регионами памяти атрибут
PAGE_READWRITE, восстановить таблицу импорта (оригинал которой легко сохранить в
X-DLL), а затем заново установить атрибут PAGE_READONLY с помощью все той же
VirtualProtect. Более того, X-DLL может выделить блок памяти из кучи с помощью API-функции VirtualAlloc и скопировать туда свое тело, которое, естественно, должно быть полностью перемещаемо, то есть сохранять работоспособность независимо от базового адреса загрузки. Далее остается только выгрузить ставшую ненужной X-DLL вызовом FreeLibrary (на тот случай, если какой-то хитрый механизм проверки целостности решит перечислить список загруженных модулей).
Маленький технический нюанс: на процессорах с поддержкой битов
NX/XD, запрещающий исполнение кода в страницах памяти, не имеющих соответствующих атрибутов, выделяемому блоку памяти следует присвоить атрибут
PAGE_EXECUTE_READWRITE. В противном случае, если у пользователя задействован аппаратный DEP для всех приложений (а не только для системных компонентов, как это происходит по умолчанию), вместо выполнения машинного кода система выбросит исключение, и выполнение троянизированной программы завершится в аварийном режиме.

В заголовке PE-файла имеется специальное поле, содержащее контрольную сумму. В подавляющем большинстве случаев оно равно нулю, но если это не так, то после вмешательства в таблицу импорта контрольную сумму необходимо пересчитать. Этим занимается утилита
EDITBIT.EXE, запущенная с ключом ‘/RELEASE’. Она входит как в штатную поставку компилятора Microsoft Visual
Studio, так и в Platform SDK, так что проблем с ее поиском возникнуть не должно.

Заключение

Технологии внедрения в исполняемые файлы не стоят на месте и развиваются вместе с защитными механизмами и операционными системами. Извечная проблема меча и щита — кто усовершенствуется первым. Использование готовых утилит, работающих в полностью автоматическом режиме, во-первых, непрестижно, а во-вторых, слишком ненадежно. Разработчики антивирусов даром свой хлеб не едят! Чтобы не погореть на мелочах, весь X-код следует писать самостоятельно. До тех пор пока он существует в единственном экземпляре, у защитных систем не остается никакого шанса предотвратить атаку!



Полную версию статьи
читай в майском номере
Хакера! Кроме того, в видеоинструкции на диске мы наглядно показываем, как
надо склеивать файлы. Рекомендую посмотреть видео сразу после прочтения статьи, чтобы разрешить все возникшие вопросы.
И конечно: на диске тебя ждут все упомянутые инструменты, примеры, компилятор Си для сборки приведенного кода, а также подборка
готовых джойнеров.

Читайте также: