diff --git a/dmmBypass.py b/dmmBypass.py index fa8c327..0998d5c 100644 --- a/dmmBypass.py +++ b/dmmBypass.py @@ -9,6 +9,10 @@ import urllib.parse from uuid import getnode from datetime import datetime, timedelta +# Set to False to allow packet sniffing/capture. +# Set separately for dmmUpdater +SSL_VERIFY = True + parser = argparse.ArgumentParser(description='DMM bypass script') parser.add_argument('-g', '--game', type=str, help="DMM code name of the game", default="kfp2g") parser.add_argument('-t', '--type', help="DMM game type (ACL/GCL)", default="GCL") @@ -30,7 +34,7 @@ def save_config(game_id, config): config = load_config(args.game) if config is None: - subprocess.check_call([sys.executable, "-m", "pip", "install", "requests", "beautifulsoup4", "PyPasser"]) + subprocess.check_call([sys.executable, "-m", "pip", "install", "requests", "beautifulsoup4"]) config = { "game_id" : args.game, "file_path" : input('Enter full file path to KF3 .exe: ').strip('\"'), @@ -49,7 +53,6 @@ config["game_type"] = args.type import requests import dmmUpdater from bs4 import BeautifulSoup -from pypasser import reCaptchaV3 def get_hash(data): sha_obj = hashlib.sha256() @@ -75,38 +78,48 @@ def get_game_root_path(user_path, game_dir, exe_name): else: raise Exception(f"Path to game files is incorrect! Ensure it points to {exe_name} or contains {game_dir}!") -def retrieve_login_token(session : requests.Session): +def retrieve_oauth_code(login, password, session : requests.Session): try: - print("Retrieving login form") - url = "https://accounts.dmm.com/service/login/password" - result = session.get(url) + print("Retrieving login url") + url = "https://apidgp-gameplayer.games.dmm.com/v5/auth/login/url" + data = {"prompt":"choose"} + result = session.post(url, json=data, verify=SSL_VERIFY) + result.raise_for_status() + url = result.json()["data"]["url"] + encoded_url = urllib.parse.quote_plus(url.replace("https://accounts.dmm.com/service/oauth/select/=/path=", "").replace("oauth/select/", "oauth/").replace("accounts?prompt=choose", "accounts")) + + print("Retrieving login form token") + result = session.get(url, verify=SSL_VERIFY) result.raise_for_status() page = BeautifulSoup(result.content, 'html.parser') token = page.find('input', attrs={"name":"token"}).get("value") - return token + + print("Retrieving oauth link") + url = "https://accounts.dmm.com/service/oauth/authenticate" + headers = {"Content-Type": "application/x-www-form-urlencoded", + "check_done_login":"true"} + data = f"token={token}&login_id={login}&password={password}&use_auto_login=1&path={encoded_url}&recaptchaToken=" + result = session.post(url, data, headers=headers, verify=SSL_VERIFY) + result.raise_for_status() + page = BeautifulSoup(result.content, 'html.parser') + final_url = page.find('input', attrs={"id":"ga-param-service-url"}).get("value") + + print("Retrieving oauth token") + result = session.get(final_url, allow_redirects=False, verify=SSL_VERIFY) + redirect_url = result.next.url + oauth_token = redirect_url.split("=")[-1] + + return oauth_token + except Exception as e: print("Failed to retrieve login form:", e) -def retrieve_captcha_token(): - try: - captcha = reCaptchaV3("https://www.google.com/recaptcha/enterprise/anchor?ar=1&k=6LfZLQEVAAAAAC-8pKwFNuzVoJW4tfUCghBX_7ZE&co=aHR0cHM6Ly9hY2NvdW50cy5kbW0uY29tOjQ0Mw..&hl=ja&v=1Bq_oiMBd4XPUhKDwr0YL1Js&size=invisible") - return captcha - except Exception as e: - print("Failed to solve captcha:", e) - -def retrieve_auth_keys(login, password, token, captcha, session : requests.Session): - try: - print("Logging in") - url = "https://accounts.dmm.com/service/login/password/authenticate" - data = f"token={token}&login_id={login}&password={password}&prompt=&device=games-player&recaptchaToken={captcha}" - headers = {"Content-Type": "application/x-www-form-urlencoded"} - result = session.post(url, data, headers=headers) - result.raise_for_status() - cookies = result.cookies.get_dict() - return cookies["login_secure_id"], cookies["login_session_id"] - except Exception as e: - print("Failed to log in:", e) - return None, None +def retrieve_access_token(oauth_token, session: requests.Session): + url = "https://apidgp-gameplayer.games.dmm.com/v5/auth/accesstoken/issue" + result = session.post(url, json={"code":oauth_token}, verify=SSL_VERIFY) + result.raise_for_status() + data = result.json()["data"] + return data["access_token"], data["expires_in_seconds"] def agree_to_game_terms(game_id, use_proxy): try: @@ -124,22 +137,25 @@ def agree_to_game_terms(game_id, use_proxy): print("Failed to accept terms of use:", e) return False -def retrieve_launch_params(game_id, game_type, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy = False, update_game = False): +def retrieve_launch_params(game_id, game_type, mac_addr, hdd_serial, motherboard, access_token, session: requests.Session, use_proxy = False, update_game = False): try: print("Retrieving launch arguments") data = {"product_id":game_id,"game_type":game_type,"game_os":"win","launch_type":"LIB","mac_address":mac_addr,"hdd_serial":hdd_serial,"motherboard":motherboard,"user_os":"win"} - headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", "Client-App": "DMMGamePlayer5", "Client-version": "5.3.12", "Content-Type": "application/json"} - cookies = {"login_secure_id":login_secure, "login_session_id":login_session} + headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", + "Client-App": "DMMGamePlayer5", + "Client-version": "5.3.12", + "Content-Type": "application/json", + "actauth":access_token} if update_game: url = "https://katworks.sytes.net/proxy/launchAndUpdate" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/r2/launch/cl" else: url = "https://katworks.sytes.net/proxy/launch" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/launch/cl" - result = requests.post(url, cookies=cookies, headers=headers, json=data) + result = session.post(url, headers=headers, json=data, verify=SSL_VERIFY) result.raise_for_status() result_json = result.json() if result_json["result_code"] == 308: if agree_to_game_terms(game_id, use_proxy): - result = requests.post(url, cookies=cookies, headers=headers, json=data) + result = session.post(url, headers=headers, json=data, verify=SSL_VERIFY) result.raise_for_status() result_json = result.json() if result_json["result_code"] != 100: @@ -185,28 +201,24 @@ def main(config): saved_login = None if saved_login is not None: - login_secure, login_session = saved_login["login_secure"], saved_login["login_session"] - print("Loaded login data from previous session") + access_token = saved_login["access_token"] + print("Loaded token from previous session") else: - token = retrieve_login_token(session) - captcha = retrieve_captcha_token() - - if token == None or captcha == None: - return - - login_secure, login_session = retrieve_auth_keys(login, password, token, captcha, session) - if login_secure == None or login_session == None: + oauth_code = retrieve_oauth_code(login, password, session) + if oauth_code == None: return - login_expiration = int((current_time + timedelta(days=364)).timestamp()) #expire in less than a year - config["saved_login"] = saved_login = {"login_secure" : login_secure, "login_session" : login_session, "expiration": login_expiration} + access_token, expiration_seconds = retrieve_access_token(oauth_code, session) + + token_expiration = int((current_time + timedelta(seconds=expiration_seconds)).timestamp()) + config["saved_login"] = saved_login = {"access_token" : access_token, "expiration": token_expiration} del(config["update_game"]) save_config(game_id, config) if not use_proxy: input("Enable VPN now and press Enter") #execute_args, file_list_url, file_access_params - launch_data : dict = retrieve_launch_params(game_id, game_type, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy, update_game) + launch_data : dict = retrieve_launch_params(game_id, game_type, mac_addr, hdd_serial, motherboard, access_token, session, use_proxy, update_game) if launch_data == None: return diff --git a/dmmUpdater.py b/dmmUpdater.py index e0742b6..8cbae6c 100644 --- a/dmmUpdater.py +++ b/dmmUpdater.py @@ -6,10 +6,14 @@ import urllib.parse from urllib.request import urlretrieve from pathlib import Path +# Set to False to allow packet sniffing/capture. +# Set separately for dmmBypass +SSL_VERIFY = True + def get_file_list(url): url = "https://apidgp-gameplayer.games.dmm.com" + url print("Retrieving file list from " + url) - result = requests.get(url) + result = requests.get(url, verify=SSL_VERIFY) result.raise_for_status() data = result.json()["data"] return data["domain"], data["file_list"] @@ -71,7 +75,7 @@ def update_game(game_path, files_url, files_param): file = files_to_download[index] url, path = file["url"], file["path"] - response = requests.get(url, timeout=10, stream=True) + response = requests.get(url, timeout=10, stream=True, verify=SSL_VERIFY) total_size = int(response.headers.get("content-length", 0)) block_size = 1024 * 1024 downloaded = 0