init
This commit is contained in:
commit
222c73f667
|
@ -0,0 +1,145 @@
|
|||
import os
|
||||
import sys
|
||||
import wmi
|
||||
import hashlib
|
||||
import requests
|
||||
import subprocess
|
||||
import urllib.parse
|
||||
import dmmUpdater
|
||||
from uuid import getnode
|
||||
from bs4 import BeautifulSoup
|
||||
from pypasser import reCaptchaV3
|
||||
|
||||
def get_hash(data):
|
||||
sha_obj = hashlib.sha256()
|
||||
sha_obj.update(str(data).encode())
|
||||
hash_hex = sha_obj.hexdigest()
|
||||
return hash_hex
|
||||
|
||||
def get_mac():
|
||||
mac = getnode()
|
||||
return ':'.join(("%012X" % mac)[i:i+2] for i in range(0, 12, 2)).lower()
|
||||
|
||||
def get_motherboard():
|
||||
return wmi.WMI().Win32_BaseBoard()[0].SerialNumber
|
||||
|
||||
def retrieve_login_token(session : requests.Session):
|
||||
try:
|
||||
print("Retrieving login form")
|
||||
url = "https://accounts.dmm.com/service/login/password"
|
||||
result = session.get(url)
|
||||
page = BeautifulSoup(result.content, 'html.parser')
|
||||
token = page.find('input', attrs={"name":"token"}).get("value")
|
||||
return token
|
||||
except Exception as e:
|
||||
print("Failed to retrieve login form:", e)
|
||||
return None
|
||||
|
||||
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)
|
||||
return None
|
||||
|
||||
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)
|
||||
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_update_params(game_id, login_secure, login_session, use_proxy):
|
||||
try:
|
||||
print("Retrieving update file list")
|
||||
data = {"product_id":game_id,"game_type":"GCL","game_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}
|
||||
url = "https://katworks.sytes.net/KF/Api/DMM/filelist" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/r2/filelist/cl"
|
||||
result = requests.post(url, cookies=cookies, headers=headers, json=data)
|
||||
|
||||
data = result.json()["data"]
|
||||
game_version = data["latest_version"]
|
||||
print("Latest version:", game_version)
|
||||
file_list_url = data["file_list_url"]
|
||||
file_list_params = data["sign"]
|
||||
file_list_params = "?" + file_list_params.replace(";", "&").replace("CloudFront-", "")
|
||||
return file_list_url, file_list_params
|
||||
except Exception as e:
|
||||
print("Failed to retrieve update file list:", e)
|
||||
return
|
||||
|
||||
def retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy):
|
||||
try:
|
||||
print("Retrieving launch arguments")
|
||||
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"}
|
||||
cookies = {"login_secure_id":login_secure, "login_session_id":login_session}
|
||||
url = "https://katworks.sytes.net/KF/Api/DMM/launch" if use_proxy else "https://apidgp-gameplayer.games.dmm.com/v5/r2/launch/cl"
|
||||
result = requests.post(url, cookies=cookies, headers=headers, json=data)
|
||||
|
||||
data = result.json()["data"]
|
||||
return data["execute_args"]
|
||||
except Exception as e:
|
||||
print("Failed to retrieve launch arguments:", e)
|
||||
return None
|
||||
|
||||
def main(args):
|
||||
if len(args) != 7:
|
||||
print("Usage:",
|
||||
"\tpython dmmBypass.py game_id game_path email password update_game use_proxy",
|
||||
"\t- game_id: DMM code name of the game",
|
||||
"\t- game_path: full path to the game .exe. Wrap in \" if there are spaces in the path",
|
||||
"\t- email, password: dmm credentials",
|
||||
"\t- update_game: \"true\" to check for game update before launching",
|
||||
"\t- use_proxy: \"true\" to send required request through Katboi VPN. Otherwise use your own VPN",
|
||||
"\texample: python dmmBypass.py kfp2g \"D:\Games\KFP2G\けもフレ3.exe\" kat@email.com abc123 true", sep="\n")
|
||||
return
|
||||
|
||||
game_id = args[1]
|
||||
exe_location = args[2]
|
||||
login = urllib.parse.quote_plus(args[3])
|
||||
password = urllib.parse.quote_plus(args[4])
|
||||
update_game = args[5].lower() == "true"
|
||||
use_proxy = args[6].lower() == "true"
|
||||
mac_addr = get_mac()
|
||||
hdd_serial = get_hash('')
|
||||
motherboard = get_hash(get_motherboard())
|
||||
|
||||
with requests.Session() as session:
|
||||
token = retrieve_login_token(session)
|
||||
captcha = retrieve_captcha_token()
|
||||
|
||||
if token == None or captcha == None:
|
||||
return
|
||||
|
||||
#auth keys are also saved as cookies in session
|
||||
login_secure, login_session = retrieve_auth_keys(login, password, token, captcha, session)
|
||||
|
||||
if not use_proxy: input("Enable VPN now and press Enter")
|
||||
|
||||
if update_game:
|
||||
file_list_url, file_access_params = retrieve_update_params(game_id, login_secure, login_session, use_proxy)
|
||||
if file_list_url == None or file_access_params == None:
|
||||
return
|
||||
dmmUpdater.update_game(os.path.dirname(exe_location), file_list_url, file_access_params)
|
||||
|
||||
execute_args = retrieve_launch_params(game_id, mac_addr, hdd_serial, motherboard, login_secure, login_session, use_proxy)
|
||||
|
||||
if not use_proxy: input("Disable VPN now and press Enter")
|
||||
|
||||
print("Starting game")
|
||||
args = [exe_location] + execute_args.split()
|
||||
print(args)
|
||||
subprocess.Popen(args, start_new_session=True)
|
||||
input("Done. Press enter to exit")
|
||||
|
||||
args = sys.argv
|
||||
main(args)
|
|
@ -0,0 +1,80 @@
|
|||
import os
|
||||
import hashlib
|
||||
import requests
|
||||
import urllib.parse
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
import sys
|
||||
def progressbar(it, prefix="", size=60, out=sys.stdout): # Python3.3+
|
||||
"""https://stackoverflow.com/questions/3160699/python-progress-bar"""
|
||||
count = len(it)
|
||||
if count == 0: return
|
||||
def show(j):
|
||||
x = int(size*j/count)
|
||||
print("{}[{}{}] {}/{}".format(prefix, "#"*x, "."*(size-x), j, count),
|
||||
end='\r', file=out, flush=True)
|
||||
show(0)
|
||||
for i, item in enumerate(it):
|
||||
yield item
|
||||
show(i+1)
|
||||
print("\n", flush=True, file=out)
|
||||
|
||||
def get_file_list(url):
|
||||
url = "https://apidgp-gameplayer.games.dmm.com" + url
|
||||
print("Retrieving file list from " + url)
|
||||
data = requests.get(url).json()["data"]
|
||||
return data["domain"], data["file_list"]
|
||||
|
||||
def get_file_hash(file_path):
|
||||
if not os.path.exists(file_path):
|
||||
return None
|
||||
|
||||
with open(file_path, "rb") as f:
|
||||
file_hash = hashlib.md5()
|
||||
while chunk := f.read(8192):
|
||||
file_hash.update(chunk)
|
||||
|
||||
return file_hash.hexdigest()
|
||||
|
||||
def update_game(game_path, files_url, files_param):
|
||||
server_url, server_files = get_file_list(files_url)
|
||||
server_file_dict = {file["local_path"]: file for file in server_files}
|
||||
|
||||
local_files = [os.path.join(dp, f).replace("\\", "/") for dp, dn, filenames in os.walk(game_path) for f in filenames]
|
||||
local_file_dict = {"/" + os.path.relpath(r, game_path).replace("\\", "/"): {"abs_path":r, "hash":""} for r in local_files if not "BepInEx" in r}
|
||||
|
||||
files_to_download = []
|
||||
files_to_delete = []
|
||||
for server_file_key in server_file_dict.keys():
|
||||
server_file = server_file_dict[server_file_key]
|
||||
|
||||
if server_file_key in local_file_dict:
|
||||
local_file = local_file_dict[server_file_key]
|
||||
if server_file["force_delete_flg"]:
|
||||
files_to_delete.append(local_file["abs_path"])
|
||||
else:
|
||||
local_file["hash"] = get_file_hash(local_file["abs_path"])
|
||||
|
||||
if server_file["check_hash_flg"] and local_file["hash"] == server_file["hash"]:
|
||||
continue
|
||||
|
||||
download_url = urllib.parse.urljoin(server_url, server_file["path"]) + files_param
|
||||
download_path = game_path.replace("\\", "/") + server_file_key
|
||||
files_to_download.append({"url":download_url, "path":download_path})
|
||||
else:
|
||||
download_url = urllib.parse.urljoin(server_url, server_file["path"]) + files_param
|
||||
download_path = game_path.replace("\\", "/") + server_file_key
|
||||
files_to_download.append({"url":download_url, "path":download_path})
|
||||
|
||||
print("Files to download:", len(files_to_download))
|
||||
|
||||
for file in progressbar(files_to_download, "Downloading: ", 40):
|
||||
url, path = file["url"], file["path"]
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
urlretrieve(url, path)
|
||||
|
||||
# #files_to_delete is unused until fully tested
|
||||
# for file in files_to_delete:
|
||||
# os.remove(file)
|
||||
|
||||
return True
|
|
@ -0,0 +1,3 @@
|
|||
beautifulsoup4
|
||||
PyPasser
|
||||
wmi
|
|
@ -0,0 +1,36 @@
|
|||
@echo off
|
||||
chcp 65001
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
set file_name="kfp2g.cfg"
|
||||
|
||||
IF NOT EXIST %file_name% (
|
||||
set /p null="Make sure python is installed. In next step, required packages will be installed. Press Enter to continue"
|
||||
pip install -r requirements.txt
|
||||
set /p file_path=Enter full file path to KF3 .exe:
|
||||
set /p dmm_login=DMM Login:
|
||||
set /p dmm_password=DMM Password:
|
||||
set /p confirm="Your login tokens will be sent through Katboi's VPN machine, is that ok? Personal VPN is required if not (yes/no):"
|
||||
echo !confirm!
|
||||
if /i "!confirm!"=="yes" (
|
||||
set use_proxy=true
|
||||
) else (
|
||||
set use_proxy=false
|
||||
)
|
||||
echo !file_path!> %file_name%
|
||||
echo !dmm_login!>> %file_name%
|
||||
echo !dmm_password!>> %file_name%
|
||||
echo !use_proxy!>> %file_name%
|
||||
) ELSE (
|
||||
< %file_name% (
|
||||
set /p file_path=
|
||||
set /p dmm_login=
|
||||
set /p dmm_password=
|
||||
set /p use_proxy=
|
||||
)
|
||||
echo Loaded settings from %file_name%
|
||||
)
|
||||
|
||||
python dmmBypass.py kfp2g %file_path% %dmm_login% %dmm_password% false %use_proxy%
|
||||
|
||||
pause
|
|
@ -0,0 +1,36 @@
|
|||
@echo off
|
||||
chcp 65001
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
set file_name="kfp2g.cfg"
|
||||
|
||||
IF NOT EXIST %file_name% (
|
||||
set /p null="Make sure python is installed. In next step, required packages will be installed. Press Enter to continue"
|
||||
pip install -r requirements.txt
|
||||
set /p file_path=Enter full file path to KF3 .exe:
|
||||
set /p dmm_login=DMM Login:
|
||||
set /p dmm_password=DMM Password:
|
||||
set /p confirm="Your login tokens will be sent through Katboi's VPN machine, is that ok? Personal VPN is required if not (yes/no):"
|
||||
echo !confirm!
|
||||
if /i "!confirm!"=="yes" (
|
||||
set use_proxy=true
|
||||
) else (
|
||||
set use_proxy=false
|
||||
)
|
||||
echo !file_path!> %file_name%
|
||||
echo !dmm_login!>> %file_name%
|
||||
echo !dmm_password!>> %file_name%
|
||||
echo !use_proxy!>> %file_name%
|
||||
) ELSE (
|
||||
< %file_name% (
|
||||
set /p file_path=
|
||||
set /p dmm_login=
|
||||
set /p dmm_password=
|
||||
set /p use_proxy=
|
||||
)
|
||||
echo Loaded settings from %file_name%
|
||||
)
|
||||
|
||||
python dmmBypass.py kfp2g %file_path% %dmm_login% %dmm_password% true %use_proxy%
|
||||
|
||||
pause
|
Loading…
Reference in New Issue