You've already forked JapariBypass
Compare commits
1 Commits
master
...
06b4bca63a
| Author | SHA1 | Date | |
|---|---|---|---|
| 06b4bca63a |
12
README.md
12
README.md
@@ -14,8 +14,16 @@ 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`.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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
|
||||
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:
|
||||
- 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.
|
||||
164
dmmBypass.py
164
dmmBypass.py
@@ -9,43 +9,43 @@ 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")
|
||||
parser.add_argument('-u', '--update', help="Check for game update before launching", action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
def load_config(game_id):
|
||||
config_name = game_id + ".cfg"
|
||||
if not os.path.exists(config_name):
|
||||
return None
|
||||
else:
|
||||
with open(config_name, "rt", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
def load_config(config_name):
|
||||
with open(config_name, "rt", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
def save_config(game_id, config):
|
||||
with open(game_id + '.cfg', 'wt', encoding="utf-8") as f:
|
||||
def save_config(config_name, config):
|
||||
with open(config_name, 'wt', encoding="utf-8") as f:
|
||||
json.dump(config, f, indent=1, ensure_ascii=False)
|
||||
|
||||
config = load_config(args.game)
|
||||
config_name = args.game + '.cfg'
|
||||
|
||||
if config is None:
|
||||
subprocess.check_call([sys.executable, "-m", "pip", "install", "requests", "beautifulsoup4"])
|
||||
if not os.path.exists(config_name):
|
||||
subprocess.check_call([sys.executable, "-m", "pip", "install", "requests", "beautifulsoup4", "PyPasser"])
|
||||
config = {
|
||||
"game_id" : args.game,
|
||||
"file_path" : input('Enter full file path to KF3 .exe: ').strip('\"'),
|
||||
"dmm_login" : input('DMM Login (email): '),
|
||||
"dmm_password" : input('DMM Password: '),
|
||||
"use_proxy" : input('Your login data will be sent through my VPN.\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(args.game, config)
|
||||
save_config(config_name, config)
|
||||
else:
|
||||
print(f'Loaded settings from {args.game}.cfg')
|
||||
config = load_config(config_name)
|
||||
print(f'Loaded settings from {config_name}')
|
||||
|
||||
config["update_game"] = args.update
|
||||
config["game_type"] = args.type
|
||||
@@ -53,6 +53,7 @@ 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()
|
||||
@@ -78,54 +79,44 @@ 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_oauth_code(login, password, session : requests.Session):
|
||||
def retrieve_login_token(session : requests.Session):
|
||||
try:
|
||||
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)
|
||||
print("Retrieving login form")
|
||||
url = "https://accounts.dmm.com/service/login/password"
|
||||
result = session.get(url)
|
||||
result.raise_for_status()
|
||||
page = BeautifulSoup(result.content, 'html.parser')
|
||||
token = page.find('input', attrs={"name":"token"}).get("value")
|
||||
|
||||
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
|
||||
|
||||
return token
|
||||
except Exception as e:
|
||||
print("Failed to retrieve login form:", e)
|
||||
|
||||
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 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 agree_to_game_terms(game_id, use_proxy):
|
||||
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 agree_to_game_terms(game_id, trust_level):
|
||||
try:
|
||||
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"}
|
||||
url = "https://katworks.sytes.net/proxy/agreement" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/agreement/confirm/client"
|
||||
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"
|
||||
data = {"product_id":game_id,"is_notification":False,"is_myapp":False}
|
||||
result = requests.post(url, headers=headers, json=data)
|
||||
result.raise_for_status()
|
||||
@@ -137,25 +128,22 @@ 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, access_token, session: requests.Session, use_proxy = False, update_game = False):
|
||||
def retrieve_launch_params(game_id, game_type, mac_addr, hdd_serial, motherboard, login_secure, login_session, trust_level, update_game):
|
||||
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",
|
||||
"actauth":access_token}
|
||||
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}
|
||||
if update_game:
|
||||
url = "https://katworks.sytes.net/proxy/launchAndUpdate" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/r2/launch/cl"
|
||||
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"
|
||||
else:
|
||||
url = "https://katworks.sytes.net/proxy/launch" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/launch/cl"
|
||||
result = session.post(url, headers=headers, json=data, verify=SSL_VERIFY)
|
||||
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"
|
||||
result = requests.post(url, cookies=cookies, headers=headers, json=data)
|
||||
result.raise_for_status()
|
||||
result_json = result.json()
|
||||
if result_json["result_code"] == 308:
|
||||
if agree_to_game_terms(game_id, use_proxy):
|
||||
result = session.post(url, headers=headers, json=data, verify=SSL_VERIFY)
|
||||
if agree_to_game_terms(game_id, trust_level):
|
||||
result = requests.post(url, cookies=cookies, headers=headers, json=data)
|
||||
result.raise_for_status()
|
||||
result_json = result.json()
|
||||
if result_json["result_code"] != 100:
|
||||
@@ -167,13 +155,7 @@ def retrieve_launch_params(game_id, game_type, mac_addr, hdd_serial, motherboard
|
||||
data = result_json["data"]
|
||||
return data
|
||||
except Exception as e:
|
||||
if str(e).startswith("203:"):
|
||||
config = load_config(game_id)
|
||||
config["saved_login"] = None
|
||||
save_config(game_id, config)
|
||||
print("DMM session has expired. Saved login data cleared. Please run the program again.")
|
||||
else:
|
||||
print("Failed to retrieve launch arguments:", e)
|
||||
print("Failed to retrieve launch arguments:", e)
|
||||
|
||||
def main(config):
|
||||
#required arguments
|
||||
@@ -184,7 +166,7 @@ def main(config):
|
||||
password = urllib.parse.quote_plus(config["dmm_password"])
|
||||
#optional arguments
|
||||
update_game = config.get("update_game", False)
|
||||
use_proxy = config.get("use_proxy", False)
|
||||
trust_level = config.get("trust_level", 0)
|
||||
saved_login = config.get("saved_login", None)
|
||||
#dmm requires these values
|
||||
mac_addr = get_mac()
|
||||
@@ -201,30 +183,30 @@ def main(config):
|
||||
saved_login = None
|
||||
|
||||
if saved_login is not None:
|
||||
access_token = saved_login["access_token"]
|
||||
print("Loaded token from previous session")
|
||||
login_secure, login_session = saved_login["login_secure"], saved_login["login_session"]
|
||||
print("Loaded login data from previous session")
|
||||
else:
|
||||
oauth_code = retrieve_oauth_code(login, password, session)
|
||||
if oauth_code == None:
|
||||
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:
|
||||
return
|
||||
|
||||
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}
|
||||
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}
|
||||
del(config["update_game"])
|
||||
save_config(game_id, config)
|
||||
save_config(config_name, config)
|
||||
|
||||
if not use_proxy: input("Enable VPN now and press Enter")
|
||||
if trust_level == 0: 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, access_token, session, use_proxy, update_game)
|
||||
|
||||
if launch_data == None:
|
||||
return
|
||||
launch_data : dict = retrieve_launch_params(game_id, game_type, mac_addr, hdd_serial, motherboard, login_secure, login_session, trust_level, update_game)
|
||||
|
||||
execute_args : str = launch_data.get("execute_args", None)
|
||||
|
||||
if execute_args == None:
|
||||
return
|
||||
|
||||
@@ -241,7 +223,7 @@ def main(config):
|
||||
install_dir = launch_data["install_dir"]
|
||||
game_root_path = get_game_root_path(exe_location, install_dir, exec_name)
|
||||
|
||||
if not use_proxy: input("Disable VPN now and press Enter")
|
||||
if trust_level == 0: input("Disable VPN now and press Enter")
|
||||
|
||||
if update_game:
|
||||
dmmUpdater.update_game(game_root_path, file_list_url, file_list_params)
|
||||
|
||||
@@ -6,14 +6,10 @@ 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, verify=SSL_VERIFY)
|
||||
result = requests.get(url)
|
||||
result.raise_for_status()
|
||||
data = result.json()["data"]
|
||||
return data["domain"], data["file_list"]
|
||||
@@ -75,7 +71,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, verify=SSL_VERIFY)
|
||||
response = requests.get(url, timeout=10, stream=True)
|
||||
total_size = int(response.headers.get("content-length", 0))
|
||||
block_size = 1024 * 1024
|
||||
downloaded = 0
|
||||
|
||||
Reference in New Issue
Block a user