Skip to content

Commit

Permalink
Merge pull request #59 from fa0311/develop-v4
Browse files Browse the repository at this point in the history
4.8.0
  • Loading branch information
fa0311 authored Mar 22, 2023
2 parents cc08a3f + 8da4979 commit 0b0850d
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 199 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/build
/dist
/*.spec
/__pycache__
__pycache__
*.exe
*.bytes
/.venv
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"program": "DMMGamePlayerFastLauncher.py",
"console": "integratedTerminal",
"justMyCode": true,
"args": ["umamusume", "--non-bypass-uac"]
"args": ["umamusume", "--non-bypass-uac", "--debug"]
}
]
}
218 changes: 28 additions & 190 deletions DMMGamePlayerFastLauncher.py
Original file line number Diff line number Diff line change
@@ -1,188 +1,14 @@
import subprocess
import requests
import argparse
import json
import glob
import win32crypt
import ctypes
import random
import hashlib
import sqlite3
import os
import time
from urllib.parse import urlparse
import win32security
import sys
import base64
from Crypto.Cipher import AES


class DgpSession:
DGP5_PATH: str
HEADERS: dict[str, str]
PROXY: dict[str, str | None]
DGP5_HEADERS: dict[str, str]
DGP5_LAUNCH_PARAMS: dict[str, str]
db: sqlite3.Connection
session: requests.Session
cookies: requests.cookies.RequestsCookieJar

def __init__(self, https_proxy_uri: str | None = None):
requests.packages.urllib3.disable_warnings()
self.DGP5_PATH = os.environ["APPDATA"] + "\\dmmgameplayer5\\"
self.PROXY = {"https": https_proxy_uri}
self.HEADERS = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36",
}
self.DGP5_HEADERS = {
"Host": "apidgp-gameplayer.games.dmm.com",
"Connection": "keep-alive",
"User-Agent": "DMMGamePlayer5-Win/17.1.2 Electron/17.1.2",
"Client-App": "DMMGamePlayer5",
"Client-version": "17.1.2",
}
self.DGP5_LAUNCH_PARAMS = {
"game_type": "GCL",
"game_os": "win",
"launch_type": "LIB",
"mac_address": self.gen_rand_address(),
"hdd_serial": self.gen_rand_hex(),
"motherboard": self.gen_rand_hex(),
"user_os": "win",
}
self.open()

def gen_rand_hex(self):
return hashlib.sha256(str(random.random()).encode()).hexdigest()

def gen_rand_address(self):
hex = self.gen_rand_hex()
address = ""
for x in range(12):
address += hex[x]
if x % 2 == 1:
address += ":"
return address[:-1]

def write(self):
aes_key = self.get_aes_key()
for cookie_row in self.db.cursor().execute("select * from cookies"):
value = self.session.cookies.get(cookie_row[3])
v10, nonce, _, _ = self.split_encrypted_data(cookie_row[5])
cipher = AES.new(aes_key, AES.MODE_GCM, nonce)
decrypt_data, mac = cipher.encrypt_and_digest(value.encode())
data = self.join_encrypted_data(v10, nonce, decrypt_data, mac)
self.db.execute(
"update cookies set encrypted_value = ? where name = ?",
(memoryview(data), cookie_row[3]),
)
self.db.commit()

def read(self):
aes_key = self.get_aes_key()
for cookie_row in self.db.cursor().execute("select * from cookies"):
_, nonce, data, mac = self.split_encrypted_data(cookie_row[5])
cipher = AES.new(aes_key, AES.MODE_GCM, nonce)
value = cipher.decrypt_and_verify(data, mac).decode()
cookie_data = {
"name": cookie_row[3],
"value": value,
"domain": cookie_row[1],
"path": cookie_row[6],
"secure": cookie_row[8],
}
self.session.cookies.set_cookie(
requests.cookies.create_cookie(**cookie_data)
)

def write_cache(self, file: str = "cookie.bytes"):
contents = []
for cookie in self.session.cookies:
cookie_dict = dict(
version=cookie.version,
name=cookie.name,
value=cookie.value,
port=cookie.port,
domain=cookie.domain,
path=cookie.path,
secure=cookie.secure,
expires=cookie.expires,
discard=cookie.discard,
comment=cookie.comment,
comment_url=cookie.comment_url,
rfc2109=cookie.rfc2109,
rest=cookie._rest,
)
contents.append(cookie_dict)
data = win32crypt.CryptProtectData(
json.dumps(contents).encode(), "DMMGamePlayerFastLauncher"
)
with open(file, "wb") as f:
f.write(data)

def read_cache(self, file: str = "cookie.bytes"):
open(file, "a+")
with open(file, "rb") as f:
data = f.read()
_, contents = win32crypt.CryptUnprotectData(data)
for cookie in json.loads(contents):
self.session.cookies.set_cookie(
requests.cookies.create_cookie(**cookie)
)

def get(self, url: str) -> requests.Response:
return self.session.get(url, headers=self.HEADERS, proxies=self.PROXY)

def post(self, url: str) -> requests.Response:
return self.session.post(url, headers=self.HEADERS, proxies=self.PROXY)

def lunch(self, url: str, product_id: str) -> requests.Response:
json = {"product_id": product_id}
json.update(self.DGP5_LAUNCH_PARAMS)
return self.session.post(
url,
headers=self.DGP5_HEADERS,
proxies=self.PROXY,
json=json,
verify=False,
)

def get_config(self):
with open(self.DGP5_PATH + "dmmgame.cnf", "r", encoding="utf-8") as f:
config = f.read()
return json.loads(config)

def open(self):
self.db = sqlite3.connect(self.DGP5_PATH + "Network/Cookies")
self.session = requests.session()
self.cookies = self.session.cookies

def get_aes_key(self):
with open(self.DGP5_PATH + "\\Local State", "r") as f:
local_state = json.load(f)
encrypted_key = base64.b64decode(
local_state["os_crypt"]["encrypted_key"].encode()
)[5:]
key = win32crypt.CryptUnprotectData(encrypted_key, None, None, None, 0)[1]
return key

def split_encrypted_data(self, encrypted_data: bytes) -> bytes:
return (
encrypted_data[0:3],
encrypted_data[3:15],
encrypted_data[15:-16],
encrypted_data[-16:],
)

def join_encrypted_data(
self, v10: bytes, nonce: bytes, data: bytes, mac: bytes
) -> bytes:
return v10 + nonce + data + mac

def close(self):
self.db.close()
from lib.DGPSession import *


class ErrorManagerType:
Expand All @@ -203,6 +29,7 @@ def __init__(

class ErrorManager:
skip: bool = False
debug: bool = False
argument_error: ErrorManagerType = ErrorManagerType(
message="Could not parse argument.",
solution="Is the product_id specified correctly?",
Expand Down Expand Up @@ -253,16 +80,21 @@ class ErrorManager:

def error(self, error: ErrorManagerType, log: str | None = None):
output = filter(
lambda x: x != None,
lambda x: x is not None,
[error.message, error.solution, error.url, log],
)
if self.skip:
self.info("\n".join(output))
else:
raise Exception("\n".join(output))

def info(self, text: str):
print(text)
def info(self, text: str, e: Exception | None = None):
if e is None:
print(text)
elif self.debug:
raise e
else:
print(text)


class ProcessManager:
Expand Down Expand Up @@ -356,6 +188,7 @@ def run_bypass_uac():
argpar.add_argument("--game-args", default=None)
argpar.add_argument("--login-force", action="store_true")
argpar.add_argument("--skip-exception", action="store_true")
argpar.add_argument("--debug", action="store_true")
argpar.add_argument("--https-proxy-uri", default=None)
argpar.add_argument("--non-request-admin", action="store_true")
argpar.add_argument("--non-bypass-uac", action="store_true")
Expand All @@ -365,23 +198,27 @@ def run_bypass_uac():
try:
arg = argpar.parse_args()
error_manager.skip = arg.skip_exception
error_manager.debug = arg.debug
process_manager.non_request_admin = arg.non_request_admin
process_manager.non_bypass_uac = arg.non_bypass_uac
except:
except Exception as e:
error_manager.error(error=ErrorManager.argument_error)

session = DgpSession(arg.https_proxy_uri)

try:
session.read()
except:
error_manager.info("Read Error")
except Exception as e:
error_manager.info("Read Error", e)
try:
session.read_cache()
except:
error_manager.info("Read Cache Error")
except Exception as e:
error_manager.info("Read Cache Error", e)

if session.cookies.get("login_session_id") == None or arg.login_force:
if arg.login_force:
requests.cookies.remove_cookie_by_name(session.cookies, "login_session_id")

if session.cookies.get("login_session_id") == None:
response = session.get("https://apidgp-gameplayer.games.dmm.com/v5/loginurl")
url = response.json()["data"]["url"]
token = urlparse(url).path.split("path=")[-1]
Expand All @@ -390,27 +227,28 @@ def run_bypass_uac():
session.get(
f"https://accounts.dmm.com/service/login/token/=/path={token}/is_app=false"
)
except:
except Exception as e:
pass

if session.cookies.get("login_session_id") == None:
error_manager.info("Login Error")
try:
session.read_cache()
except:
error_manager.info("Read Cache Error")
except Exception as e:
error_manager.info("Read Cache Error", e)

if session.cookies.get("login_session_id") == None:
error_manager.error(error=ErrorManager.login_error)

try:
session.write()
except:
error_manager.info("Write Error")
except Exception as e:
error_manager.info("Write Error", e)


try:
session.write_cache()
except:
except Exception as e:
error_manager.info("Write Cache Error")

session.close()
Expand Down
9 changes: 3 additions & 6 deletions DMMGamePlayerProductIdChecker.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import os
import json
import pandas as pd
from lib.DGPSession import *

DGP5_PATH = os.environ["APPDATA"] + "\\dmmgameplayer5\\"

with open(DGP5_PATH + "dmmgame.cnf", "r", encoding="utf-8") as f:
config = f.read()
dpg5_config = json.loads(config)
session = DgpSession()

table = [
(contents["productId"], contents["detail"]["path"], contents["detail"]["version"])
for contents in dpg5_config["contents"]
for contents in session.get_config()["contents"]
]

df = pd.DataFrame(table, columns=["productId", "path", "version"])
Expand Down
9 changes: 9 additions & 0 deletions docs/README-advance.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
| --game-args | | None | String | None | |
| --login-force | | Flase | Bool | deprecated |
| --skip-exception | | False | Bool | |
| --debug | | False | Bool | |
| --https-proxy-uri | | None | String | None | |
| --non-request-admin | | False | Bool | deprecated |
| --non-bypass-uac | | False | Bool | |
Expand Down Expand Up @@ -92,6 +93,14 @@ Unity 製ゲームの引数はここに詳しく載ってます
例:
`%AppData%\DMMGamePlayerFastLauncher\DMMGamePlayerFastLauncher.exe umamusume --skip-exception`

### debug

デバックモードです
バグ報告の際など指示がある場合に付けてください

例:
`%AppData%\DMMGamePlayerFastLauncher\DMMGamePlayerFastLauncher.exe umamusume --debug`

### https-proxy-uri

https プロキシを設定します
Expand Down
Binary file added docs/img/windows_defender1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/windows_defender2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 0b0850d

Please sign in to comment.