Compare commits

..

No commits in common. "06b4bca63ac00d7b25743ca28f2b1f642f523893" and "ed77fc6b44db7dd631fb8ca48ee5737ffedc5f14" have entirely different histories.

2 changed files with 20 additions and 36 deletions

View File

@ -2,10 +2,9 @@
Efficient KF3/DMM game launcher. Efficient KF3/DMM game launcher.
## Requirements ## Requirements
- Python 3 and pip. Python 3 and pip.
- DMM account that owns KF3
KF3 installed from DMM is recommended but not required. KF3 installed from DMM. DMM itself does not need to be running.
## Instructions ## Instructions
Download the files from this repo. Unzip them to a safe place. Download the files from this repo. Unzip them to a safe place.
@ -14,16 +13,11 @@ Run `run_KF3.bat`, it will ask you for some information.
It will ask for the location of the KF3 executable. Generally this will be `C:\users\[your name]\KFP2G\けもフレ3.exe`. It will ask for the location of the KF3 executable. Generally this will be `C:\users\[your name]\KFP2G\けもフレ3.exe`.
Input your DMM email and then password when prompted, and set the desired trust level (more info on that below). If you choose not to use the public VPN, then you will need to enable your own Japanese VPN while logging in to the game. Input your DMM email and then password when prompted, and `yes` if you want to use the public VPN to connect to DMM's server. If you choose not to use the public VPN, then you will need to enable your own Japanese VPN while logging in to the game.
If you make a mistake or you want to change login details, edit the `kfp2g.cfg` config file, or delete it to run the configuration again. If you make a mistake or you want to change login details, edit the `kfp2g.cfg` config file, or delete it to run the configuration again.
If KF3 needs to update, use `update_and_run_KF3.bat` and it will install any new updates. If KF3 needs to update, use `update_and_run_KF3.bat` and it will install any new updates.
## Trust levels ## Known issues
DMM uses Geo-blocking for some requests, that requires the user to be in Japan. This can be bypassed with a VPN. I have access to an OpenVPN server owned by HAV0X that can be used to bypass this restriction. However, this comes with several security vulnerabilities for the user: Retrieving launch arguments - error 308: Every once in a while, DMM games may have updated terms of service that need to be accepted. This can only be done via the official DMM app.
- trust level 2 - Your login and password are sent to my device. The request is processed and sent through the VPN to DMM server, and my device sends you the response. I could access the information sent in the request if I wanted to (I will not do that though).
- trust level 1 - My device is used as a proxy. A connection is made between your device, my device, and DMM, with no way of me viewing the request data (that I know of). This takes longer.
- trust level 0 - You use your own VPN. The launcher will prompt you to enable/disable VPN when needed.
I do not intentionally view or store any data sent through my server. There is no guarantee of my and HAV0X's proxy server being active at any given time.

View File

@ -4,6 +4,7 @@ import json
import hashlib import hashlib
import pathlib import pathlib
import argparse import argparse
import requests
import subprocess import subprocess
import urllib.parse import urllib.parse
from uuid import getnode from uuid import getnode
@ -11,7 +12,6 @@ from datetime import datetime, timedelta
parser = argparse.ArgumentParser(description='DMM bypass script') 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('-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")
parser.add_argument('-u', '--update', help="Check for game update before launching", action='store_true') parser.add_argument('-u', '--update', help="Check for game update before launching", action='store_true')
args = parser.parse_args() args = parser.parse_args()
@ -32,25 +32,15 @@ if not os.path.exists(config_name):
"file_path" : input('Enter full file path to KF3 .exe: ').strip('\"'), "file_path" : input('Enter full file path to KF3 .exe: ').strip('\"'),
"dmm_login" : input('DMM Login (email): '), "dmm_login" : input('DMM Login (email): '),
"dmm_password" : input('DMM Password: '), "dmm_password" : input('DMM Password: '),
"use_proxy" : input('Your login tokens will be sent through my VPN machine.\nIs that okay? (yes/no): ').lower() == "yes"
} }
while True:
try:
trust_level = int(input('\t0 - Use your own VPN\n\t1 - Use Katboi\'s VPN as a proxy (slower, more secure)\n\t2 - Use Katboi\'s VPN to process the request (fastest, unsecure)\nTrust level (0-2): '))
if trust_level < 0 or trust_level > 2: raise Exception()
config["trust_level"] = trust_level
break
except:
print("Value must be in 0-2 range")
save_config(config_name, config) save_config(config_name, config)
else: else:
config = load_config(config_name) config = load_config(config_name)
print(f'Loaded settings from {config_name}') print(f'Loaded settings from {config_name}')
config["update_game"] = args.update config["update_game"] = args.update
config["game_type"] = args.type
import requests
import dmmUpdater import dmmUpdater
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from pypasser import reCaptchaV3 from pypasser import reCaptchaV3
@ -112,11 +102,11 @@ def retrieve_auth_keys(login, password, token, captcha, session : requests.Sessi
print("Failed to log in:", e) print("Failed to log in:", e)
return None, None return None, None
def agree_to_game_terms(game_id, trust_level): def agree_to_game_terms(game_id, use_proxy):
try: try:
print("Accepting updated game terms of use") print("Accepting updated game terms of use")
headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", "Client-App": "DMMGamePlayer5", "Client-version": "5.3.12", "Content-Type": "application/json"} headers = {"User-Agent": "DMMGamePlayer5-Win/5.3.12 Electron/32.1.0", "Client-App": "DMMGamePlayer5", "Client-version": "5.3.12", "Content-Type": "application/json"}
url = "https://katworks.sytes.net/KF/Api/DMM/agreement" if trust_level == 2 else "https://katworks.sytes.net/proxy/agreement" if trust_level == 1 else "https://apidgp-gameplayer.games.dmm.com/v5/agreement/confirm/client" url = "" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/agreement/confirm/client"
data = {"product_id":game_id,"is_notification":False,"is_myapp":False} data = {"product_id":game_id,"is_notification":False,"is_myapp":False}
result = requests.post(url, headers=headers, json=data) result = requests.post(url, headers=headers, json=data)
result.raise_for_status() result.raise_for_status()
@ -128,21 +118,21 @@ def agree_to_game_terms(game_id, trust_level):
print("Failed to accept terms of use:", e) print("Failed to accept terms of use:", e)
return False return False
def retrieve_launch_params(game_id, game_type, mac_addr, hdd_serial, motherboard, login_secure, login_session, trust_level, update_game): def retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy, update_game):
try: try:
print("Retrieving launch arguments") 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"} data = {"product_id":game_id,"game_type":"GCL","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"} 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} cookies = {"login_secure_id":login_secure, "login_session_id":login_session}
if update_game: if update_game:
url = "https://katworks.sytes.net/KF/Api/DMM/update" if trust_level == 2 else "https://katworks.sytes.net/proxy/launchAndUpdate" if trust_level == 1 else "https://apidgp-gameplayer.games.dmm.com/v5/r2/launch/cl" url = "https://katworks.sytes.net/KF/Api/DMM/update" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/r2/launch/cl"
else: else:
url = "https://katworks.sytes.net/KF/Api/DMM/launch" if trust_level == 2 else "https://katworks.sytes.net/proxy/launch" if trust_level == 1 else "https://apidgp-gameplayer.games.dmm.com/v5/launch/cl" url = "https://katworks.sytes.net/KF/Api/DMM/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 = requests.post(url, cookies=cookies, headers=headers, json=data)
result.raise_for_status() result.raise_for_status()
result_json = result.json() result_json = result.json()
if result_json["result_code"] == 308: if result_json["result_code"] == 308:
if agree_to_game_terms(game_id, trust_level): if agree_to_game_terms(game_id, use_proxy):
result = requests.post(url, cookies=cookies, headers=headers, json=data) result = requests.post(url, cookies=cookies, headers=headers, json=data)
result.raise_for_status() result.raise_for_status()
result_json = result.json() result_json = result.json()
@ -160,13 +150,12 @@ def retrieve_launch_params(game_id, game_type, mac_addr, hdd_serial, motherboard
def main(config): def main(config):
#required arguments #required arguments
game_id = config["game_id"] game_id = config["game_id"]
game_type = config["game_type"]
exe_location = config["file_path"] exe_location = config["file_path"]
login = urllib.parse.quote_plus(config["dmm_login"]) login = urllib.parse.quote_plus(config["dmm_login"])
password = urllib.parse.quote_plus(config["dmm_password"]) password = urllib.parse.quote_plus(config["dmm_password"])
#optional arguments #optional arguments
update_game = config.get("update_game", False) update_game = config.get("update_game", False)
trust_level = config.get("trust_level", 0) use_proxy = config.get("use_proxy", False)
saved_login = config.get("saved_login", None) saved_login = config.get("saved_login", None)
#dmm requires these values #dmm requires these values
mac_addr = get_mac() mac_addr = get_mac()
@ -201,10 +190,10 @@ def main(config):
del(config["update_game"]) del(config["update_game"])
save_config(config_name, config) save_config(config_name, config)
if trust_level == 0: input("Enable VPN now and press Enter") if not use_proxy: input("Enable VPN now and press Enter")
#execute_args, file_list_url, file_access_params #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, trust_level, update_game) launch_data : dict = retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy, update_game)
execute_args : str = launch_data.get("execute_args", None) execute_args : str = launch_data.get("execute_args", None)
if execute_args == None: if execute_args == None:
@ -223,14 +212,15 @@ def main(config):
install_dir = launch_data["install_dir"] install_dir = launch_data["install_dir"]
game_root_path = get_game_root_path(exe_location, install_dir, exec_name) game_root_path = get_game_root_path(exe_location, install_dir, exec_name)
if trust_level == 0: input("Disable VPN now and press Enter") if not use_proxy: input("Disable VPN now and press Enter")
if update_game: if update_game:
dmmUpdater.update_game(game_root_path, file_list_url, file_list_params) dmmUpdater.update_game(game_root_path, file_list_url, file_list_params)
print("Starting game") print("Starting game")
args = [os.path.join(game_root_path, exec_name)] + execute_args.split() args = [os.path.join(game_root_path, exec_name)] + execute_args.split()
print(args)
subprocess.Popen(args, start_new_session=True) subprocess.Popen(args, start_new_session=True)
input("Done. Press enter to exit") input("Done. Press enter to exit (this will close the game)")
main(config) main(config)