From 5384cc966857155251ca30b74c036388d4e13e3a Mon Sep 17 00:00:00 2001 From: suti7yk5032 Date: Sat, 31 Aug 2024 16:13:25 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E7=94=A8=E3=82=AF?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=82=A2=E3=83=B3=E3=83=88=E3=81=AE=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dislocker_client_universal.py | 441 ++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) create mode 100644 dislocker_client_universal.py diff --git a/dislocker_client_universal.py b/dislocker_client_universal.py new file mode 100644 index 0000000..3867471 --- /dev/null +++ b/dislocker_client_universal.py @@ -0,0 +1,441 @@ +import os +import json +import tkinter.messagebox +import customtkinter +import subprocess +import requests +import hashlib +import string +import random +import tkinter +import threading +import sys +import shutil +import uuid +import time + +app_name = "Dislocker" +dislocker_dir = os.path.dirname(os.path.abspath(sys.argv[0])) + +os.chdir(dislocker_dir) + +resource_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resource") +config_dir_path = "./config/" +client_config_path = config_dir_path + "client.json" +if not os.path.isfile(client_config_path): + if not os.path.isdir(config_dir_path): + os.mkdir(config_dir_path) + + client_config = { + "initial": 1, + "auth_host_url": "http://localhost", + "pc_number": 1, + "master_password_hash": "", + "testing": 0, + "eraser": 1, + "pc_uuid": "", + "pc_token": "" + } + +elif os.path.isfile(client_config_path): + with open(client_config_path, "r") as r: + client_config = json.load(r) + +def master_password_gen(): + numbers = string.digits # (1) + password = ''.join(random.choice(numbers) for _ in range(10)) # (2) + password_hash = hashlib.md5(password.encode()).hexdigest() + result = {"password": password, "password_hash": password_hash} + return result + +def init(**kwargs): + if client_config["initial"] == 1: + pc_uuid = uuid.uuid4() + client_config["pc_uuid"] = str(pc_uuid) + + if "pc_number" in kwargs: + client_config["pc_number"] = int(kwargs["pc_number"]) + else: + tkinter.messagebox.showerror(title=f"{app_name} | 登録時にエラー", message=f"登録時にエラーが発生しました。\nPC番号が指定されていません。1個目の引数にPC番号、2個目の引数にワンタイムパスワードを指定して、もう一度お試しください。") + return 2 + + if "onetime" in kwargs: + onetime = str(kwargs["onetime"]) + else: + tkinter.messagebox.showerror(title=f"{app_name} | 登録時にエラー", message=f"登録時にエラーが発生しました。\nワンタイムパスワードが指定されていません。1個目の引数にPC番号、2個目の引数にワンタイムパスワードを指定して、もう一度お試しください。") + return 2 + + register_url = client_config["auth_host_url"] + "/register" + register_json = { + "pc_number": int(client_config["pc_number"]), + "pc_uuid": str(pc_uuid), + "onetime": onetime + } + + responce = requests.post(register_url, json=register_json) + + if responce.status_code == 200: + print("PCの情報が登録されました。") + responce_json = responce.json() + pc_token = str(responce_json["pc_token"]) + client_config["pc_token"] = pc_token + + master_password = master_password_gen() + msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 初回起動を検出", message=f"初回起動のようです。\nマスターパスワードを記録しておいてください。\nこれ以降二度と表示されることはないでしょう。\n\n{master_password["password"]}\n\nまた、認証先サーバーの接続先を指定してください。ロックを解除できなくなります。") + client_config["master_password_hash"] = master_password["password_hash"] + client_config["initial"] = 0 + + with open(client_config_path, "w") as w: + json.dump(client_config, w, indent=4) + return 2 + else: + msgbox = tkinter.messagebox.showerror(title=f"{app_name} | 登録時にエラー", message=f"登録時にエラーが発生しました。\nワンタイムパスワードが間違っている可能性があります。") + return 2 + else: + return 0 + + +class App(customtkinter.CTk): + def __init__(self): + super().__init__() + self.title(f"{app_name} | ロック中") + if client_config["testing"] == 1: + pass + else: + self.attributes('-fullscreen', True) + self.attributes('-topmost', True) + + self.frame = customtkinter.CTkFrame(self, corner_radius=0, fg_color='transparent') + self.frame.grid(row=0, column=0, sticky='nsew') + + lock = Lock() + + def exit(self): + self.unlock_taskmgr() + self.toast() + self.destroy() + + def handler_close(self): + pass + + +class Lock(customtkinter.CTkToplevel): + def __init__(self): + super().__init__() + if client_config["testing"] == 1: + self.title(f'{app_name} | PC番号 {client_config["pc_number"]} | テストモード') + else: + self.title(f'{app_name} | PC番号 {client_config["pc_number"]} | ロックされています') + + self.window_width = 600 + self.window_height = 320 + self.screen_width = self.winfo_screenwidth() + self.screen_height = self.winfo_screenheight() + self.center_x = int(self.screen_width/2 - self.window_width/2) + self.center_y = int(self.screen_height/2 - self.window_height/2) + self.geometry(f"{str(self.window_width)}x{str(self.window_height)}+{str(self.center_x)}+{str(self.center_y)}") + self.resizable(height=False, width=False) + self.attributes('-topmost', True) + self.grab_set() + self.lift() + self.protocol("WM_DELETE_WINDOW", self.handler_close) + + self.emoji_font = customtkinter.CTkFont(family="Noto Color Emoji", size=32) + self.title_font = customtkinter.CTkFont(family="Noto Sans CJK JP", size=32, weight="bold") + self.pc_number_font = customtkinter.CTkFont(family="Noto Sans CJK JP", size=64, weight="bold") + self.title_small_font = customtkinter.CTkFont(family="Noto Sans CJK JP", size=16) + self.general_font = customtkinter.CTkFont(family="Noto Sans CJK JP", size=18) + self.general_small_font = customtkinter.CTkFont(family="Noto Sans CJK JP", size=12) + self.textbox_font = customtkinter.CTkFont(family="Noto Sans CJK JP", size=14) + self.button_font = customtkinter.CTkFont(family="Noto Sans CJK JP", size=14) + + + self.msg_title_frame = customtkinter.CTkFrame(self, corner_radius=0, fg_color='transparent') + self.msg_title_frame.grid(row=0, column=0, padx=10, pady=10, sticky="nsew") + + self.icon_title_1 = customtkinter.CTkLabel(self.msg_title_frame, text='😎', font=self.emoji_font, justify="left") + self.icon_title_1.grid(row=0, column=0, padx=10, sticky="w") + + self.msg_title_1 = customtkinter.CTkLabel(self.msg_title_frame, text=f'ちょっと待って!! PC番号 | {client_config["pc_number"]}', font=self.title_font, justify="left") + self.msg_title_1.grid(row=0, column=1, padx=10, sticky="w") + + self.msg_title_2 = customtkinter.CTkLabel(self.msg_title_frame, text="本当にあなたですか?", font=self.title_small_font, justify="left") + self.msg_title_2.grid(row=1, column=1, padx=10, sticky="w") + + self.msg_subtitle_frame = customtkinter.CTkFrame(self, corner_radius=0, fg_color='transparent') + self.msg_subtitle_frame.grid(row=1, column=0, padx=10, pady=10, sticky="nsew") + self.msg_subtitle_frame.grid_columnconfigure(0, weight=1) + + self.msg_subtitle_1 = customtkinter.CTkLabel(self.msg_subtitle_frame, text='サインインするには、Discordのダイレクトメッセージに送信された\nパスワードを入力してください。', font=self.general_font, justify="left") + self.msg_subtitle_1.grid(row=0, column=0, padx=10, sticky="ew") + + self.msg_subtitle_2 = customtkinter.CTkLabel(self.msg_subtitle_frame, text='※ パスワードの有効期限は23:59までです。', font=self.general_small_font, justify="left") + self.msg_subtitle_2.grid(row=1, column=0, padx=10, sticky="w") + + self.input_frame = customtkinter.CTkFrame(self, corner_radius=0, fg_color='transparent') + self.input_frame.grid(row=2, column=0, padx=10, pady=10, sticky="nsew") + self.input_frame.columnconfigure(0, weight=1) + + self.password_entry = customtkinter.CTkEntry(self.input_frame, placeholder_text='パスワード', show='*', font=self.textbox_font) + self.password_entry.grid(row=0, column=0, padx=10, sticky="ew") + self.password_entry.bind("", self.auth_start_ev) + + self.button_frame = customtkinter.CTkFrame(self, corner_radius=0, fg_color='transparent') + self.button_frame.grid(row=3, column=0, padx=10, pady=10, sticky="nsew") + self.button_frame.columnconfigure(0, weight=3) + self.button_frame.columnconfigure(1, weight=1) + self.button_frame.columnconfigure(2, weight=1) + + self.signin_button = customtkinter.CTkButton(self.button_frame, text='サインイン', command=self.auth_start, font=self.button_font) + self.signin_button.grid(row=0, column=2, padx=10, sticky="e") + + self.signout_button = customtkinter.CTkButton(self.button_frame, text='サインアウト', command=self.signout, font=self.button_font) + self.signout_button.grid(row=0, column=1, padx=10, sticky="e") + + self.help_button = customtkinter.CTkButton(self.button_frame, text='ヘルプ', command=self.help_dummy, font=self.button_font) + self.help_button.grid(row=0, column=0, padx=10, sticky="w") + + def help_wakeup(self): + help = Help() + + def hash_genarate(self, source): + hashed = hashlib.md5(source.encode()) + return hashed.hexdigest() + + def auth_start(self): + auth_thread = threading.Thread(target=self.auth) + auth_thread.daemon = True + auth_thread.start() + + def auth_start_ev(self, event): + auth_thread = threading.Thread(target=self.auth) + auth_thread.daemon = True + auth_thread.start() + + def button_disable(self): + self.help_button.configure(state="disabled", fg_color="gray") + self.signin_button.configure(state="disabled", fg_color="gray") + self.signout_button.configure(state="disabled", fg_color="gray") + + def button_enable(self): + self.help_button.configure(state="normal", fg_color="#3c8dd0") + self.signin_button.configure(state="normal", fg_color="#3c8dd0") + self.signout_button.configure(state="normal", fg_color="#3c8dd0") + + + def auth(self): + self.button_disable() + password = str(self.password_entry.get()) + + if len(password) == 10: + print("マスターパスワードで認証を試行します。") + master_password_hash = self.hash_genarate(str(self.password_entry.get())) + if client_config["master_password_hash"] == master_password_hash: + print("マスターパスワードで認証しました。") + self.exit() + else: + print("マスターパスワードで認証できませんでした。") + self.withdraw() + msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 誤ったパスワード", message=f"パスワードが間違っています!") + self.msg_subtitle_1.configure(text='パスワードが間違っています! ') + self.button_enable() + self.deiconify() + + + print("認証サーバーにアクセスします。") + auth_url = client_config["auth_host_url"] + "/verify" + auth_json = { + "pc_number": int(client_config["pc_number"]), + "pc_uuid": str(client_config["pc_uuid"]), + "pc_token": str(client_config["pc_token"]), + "password": self.hash_genarate(str(self.password_entry.get())) + } + try: + responce = requests.post(auth_url, json=auth_json) + if responce.status_code == 200: + print("認証サーバー経由で認証しました。") + self.exit() + else: + print("認証サーバー経由での認証に失敗しました。") + self.withdraw() + msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 誤ったパスワード", message=f"パスワードが間違っています!") + self.msg_subtitle_1.configure(text='パスワードが間違っています! ') + self.button_enable() + self.deiconify() + except: + print("認証サーバーにアクセスできません。マスターパスワードで認証を試行します。") + master_password_hash = self.hash_genarate(str(self.password_entry.get())) + if client_config["master_password_hash"] == master_password_hash: + print("マスターパスワードで認証しました。") + self.exit() + else: + print("マスターパスワードで認証できませんでした。") + self.withdraw() + msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | ネットワークエラー", message=f"認証サーバーにアクセスできませんでした。\n続行するには、マスターパスワードを入力してください。") + self.msg_subtitle_1.configure(text='ネットワークエラーが発生しています。\n続行するには、マスターパスワードを入力して下さい。 ') + self.button_enable() + self.deiconify() + + def signout(self): + self.withdraw() + msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 未実装", message=f"ログアウトは未実装です。") + self.deiconify() + + def handler_close(self): + pass + + def help_dummy(self): + self.withdraw() + msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 未実装", message=f"ヘルプページは製作途中です。\nDiscordサーバーの指示に従って、認証を進めてください。") + self.deiconify() + + def exit(self): + self.destroy() + app.exit() + + +class Help(customtkinter.CTkToplevel): + def __init__(self): + super().__init__() + if client_config["testing"] == 1: + self.title(f'{app_name} | ヘルプ | テストモード') + else: + self.title(f'{app_name} | ヘルプ') + self.geometry("600x400") + self.resizable(height=False, width=False) + self.attributes('-topmost', True) + self.grab_set() + self.lift() + self.protocol("WM_DELETE_WINDOW", self.handler_close) + msgbox = tkinter.messagebox.showinfo(title=f"{app_name} | 未実装", message=f"ヘルプページは製作途中です。\nDiscordサーバーの指示に従って、認証を進めてください。") + self.destroy() + + def handler_close(self): + self.destroy() + +class Stop(): + def __init__(self) -> None: + pass + + def run(self): + print("停止処理を実行中...") + stop_thread = threading.Thread(target=self.stop) + stop_thread.run() + + + def delete_appdata(self, **kwargs): + process_name = kwargs["process_name"] + dir_path = kwargs["dir_path"] + + if not os.path.exists(dir_path): + print(f"エラー: 指定されたディレクトリ {dir_path} が存在しません。") + return 1 + + try: + # プロセスの終了 + subprocess.run(['taskkill', '/f', '/t', '/im', process_name]) + print(f"{process_name} を終了しました。") + + time.sleep(0.1) + + # ディレクトリの削除 + i = 1 + ic = 0 + while i == 1: + shutil.rmtree(dir_path) + if os.path.isdir(dir_path): + ic += 1 + if ic == 10: + i = 0 + else: + i = 0 + print(f"{dir_path} を削除しました。") + + return 0 + + except subprocess.CalledProcessError as e: + print(f"プロセス終了エラー: {e}") + return 1 + except PermissionError as e: + print(f"権限エラー: {e}") + return 1 + except Exception as error: + print("エラーが発生しました。\nエラー内容:") + print(f"エラータイプ: {error.__class__.__name__}") + print(f"エラー引数: {error.args}") + print(f"エラーメッセージ: {str(error)}") + return 1 + + def shutdown(self): + shutdown_command = subprocess.run(['shutdown', '/s', '/t', '1']) + + def stop(self): + print("停止処理を実行。") + if client_config["eraser"] == 1: + #appdata_local = os.path.expandvars("%LOCALAPPDATA%") + #appdata_roaming = os.path.expandvars("%APPDATA%") + #epic_del = app.delete_appdata(process_name="EpicGamesLauncher.exe", dir_path=f"{appdata_local}\\EpicGamesLauncher\\Saved") + #chrome_del = app.delete_appdata(process_name="chrome.exe", dir_path=f"{appdata_local}\\Google\\Chrome\\User Data") + #discord_del = app.delete_appdata(process_name="discord.exe", dir_path=f"{appdata_roaming}\\discord") + #steam_del = app.delete_appdata(process_name="steam.exe", dir_path=f"{appdata_local}\\Steam") + #ea_del = app.delete_appdata(process_name="EADesktop.exe", dir_path=f"{appdata_local}\\Electronic Arts") + #riot_del = app.delete_appdata(process_name="RiotClientServices.exe", dir_path=f"{appdata_local}\\Riot Games\\Riot Client") + pass + else: + print("削除処理をスキップ。") + stop_url = client_config["auth_host_url"] + "/stop" + stop_json = { + "pc_number": int(client_config["pc_number"]), + "pc_uuid": str(client_config["pc_uuid"]), + "pc_token": str(client_config["pc_token"]) + } + try: + responce = requests.post(stop_url, json=stop_json) + if responce.status_code == 200: + print("停止処理は成功しました。") + elif responce.status_code == 401: + print("認証に失敗しました。") + tkinter.messagebox.showwarning(title=f"{app_name} | エラー", message=f"認証に失敗しました。\nDiscordサーバーの指示に従って、停止処理を自身で行ってください。") + else: + print("内部エラーにより停止処理に失敗しました。") + result_msgbox = tkinter.messagebox.showwarning(title=f"{app_name} | エラー", message=f"内部エラーにより停止処理に失敗しました。\nDiscordサーバーの指示に従って、停止処理を自身で行ってください。") + except: + print("ネットワークエラーにより停止処理に失敗しました。") + result_msgbox = tkinter.messagebox.showwarning(title=f"{app_name} | エラー", message=f"ネットワークエラーにより停止処理に失敗しました。\nDiscordサーバーの指示に従って、停止処理を自身で行ってください。") + finally: + self.shutdown() + + +if __name__ == '__main__': + args = sys.argv + if len(args) >= 2: + if args[1] == "stop": + init_result = init() + if init_result == 1: + warning_msgbox = tkinter.messagebox.showwarning(title=f"{app_name} | 多重起動エラー", message=f"もう終了処理を行っています。\nPCがシャットダウンするまで、もう少しお待ちください。") + elif init_result == 2: + pass + else: + stop = Stop() + stop.run() + + elif args[1] == "setup": + init_result = init(pc_number=args[2], onetime=args[3]) + if init_result == 1: + warning_msgbox = tkinter.messagebox.showwarning(title=f"{app_name} | 多重起動エラー", message=f"すでに {app_name} は実行されています。\n正常に起動しない場合は、既に起動しているプロセスを終了してから、もう一度起動してみてください。") + elif init_result == 2: + pass + else: + pass + else: + print("引数エラー。") + else: + init_result = init() + if init_result == 1: + warning_msgbox = tkinter.messagebox.showwarning(title=f"{app_name} | 多重起動エラー", message=f"すでに {app_name} は実行されています。\n正常に起動しない場合は、既に起動しているプロセスを終了してから、もう一度起動してみてください。") + elif init_result == 2: + pass + else: + app = App() + app.protocol("WM_DELETE_WINDOW", app.handler_close) + app.mainloop()