From a6d3b243871916e58859f81c742d4272e66e4683 Mon Sep 17 00:00:00 2001 From: Naruse <71993948+DevilProMT@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:13:11 +0800 Subject: [PATCH] feat: Implement dispatch encryption --- Config.json | 30 +-- hi3 | 16 +- requirements.txt | 2 +- sdk_server/__init__.py | 39 ++-- sdk_server/controllers/dispatch_controller.py | 202 ++++++++++-------- sdk_server/models/granter_login_body.py | 5 +- sdk_server/models/risky_check.py | 4 +- sdk_server/models/shield_verify_body.py | 3 +- 8 files changed, 172 insertions(+), 129 deletions(-) diff --git a/Config.json b/Config.json index b271183..2856285 100644 --- a/Config.json +++ b/Config.json @@ -1,15 +1,19 @@ { - "LogLevel": "INFO", - "MaxSessions": 10, - "GameServer": { - "Ip": "127.0.0.1", - "Port": 16100 - }, - "SdkServer": { - "Ip": "127.0.0.1", - "Port": 80 - }, - "VerboseLevel":1, - "RegionName":"MikuBH3", - "UseLocalCache":false + "LogLevel": "INFO", + "GameServer": { + "IP": "127.0.0.1", + "Port": 16100 + }, + "SDKServer": { + "IP": "127.0.0.1", + "Port": 80 + }, + "VerboseLevel": 1, + "RegionName": "MikuBH3", + "UseLocalCache": false, + "AESKeys": { + "7.9.0_gf_pc": "36 31 65 37 64 33 65 66 33 32 30 63 31 35 66 66 61 64 37 61 66 32 31 34 61 64 65 64 32 34 33 38", + "7.8.0_os_pc": "64 34 32 33 30 30 31 62 32 36 38 34 62 33 62 30 61 33 30 38 66 37 65 35 63 30 61 38 66 33 65 32" + }, + "EnableDispatchEncryption": true } \ No newline at end of file diff --git a/hi3 b/hi3 index 7edda9b..6a6a919 100644 --- a/hi3 +++ b/hi3 @@ -1,13 +1,19 @@ -from game_server.config import * -from sdk_server import HandleSdkServer,HandleSslSdkServer +import threading +from sdk_server import HandleSdkServer, HandleSslSdkServer from game_server import GameServer +from utils.config import Config -SdkThread = threading.Thread(target=HandleSdkServer, args=(Config.GameServer.Ip, Config.GameServer.Port, Config.SdkServer.Port)) +SdkThread = threading.Thread( + target=HandleSdkServer, + args=(Config.GameServer.IP, Config.GameServer.Port, Config.SDKServer.Port), +) SdkThreadSsl = threading.Thread(target=HandleSslSdkServer) SdkThread.start() SdkThreadSsl.start() gameserver = GameServer() -GameThread = threading.Thread(target=gameserver.main, args=(Config.GameServer.Ip, Config.GameServer.Port)) -GameThread.start() \ No newline at end of file +GameThread = threading.Thread( + target=gameserver.main, args=(Config.GameServer.IP, Config.GameServer.Port) +) +GameThread.start() diff --git a/requirements.txt b/requirements.txt index eafc13c..671afb4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ betterproto==1.2.5 -dynaconf==3.2.6 Flask==3.0.3 loguru==0.7.2 pydantic==2.9.2 pymongo==4.6.3 Requests==2.32.3 +dacite==1.8.1 \ No newline at end of file diff --git a/sdk_server/__init__.py b/sdk_server/__init__.py index 6f7442a..a774793 100644 --- a/sdk_server/__init__.py +++ b/sdk_server/__init__.py @@ -1,13 +1,20 @@ -from game_server.config import * +from enum import Enum +import logging +from pathlib import Path + +from flask import Flask, send_from_directory from sdk_server.controllers.account_controller import account_blueprint from sdk_server.controllers.config_controller import config_blueprint from sdk_server.controllers.dispatch_controller import dispatch_blueprint +from utils.logger import Info + class VerboseLevel(Enum): SILENT = 0 NORMAL = 1 DEBUG = 2 + class RequestLoggingMiddleware: suppressed_routes = ["/report", "/sdk/dataUpload"] @@ -16,15 +23,18 @@ class RequestLoggingMiddleware: self.verbose_level = verbose_level def __call__(self, environ, start_response): - path = environ.get('PATH_INFO', '') - method = environ.get('REQUEST_METHOD', '').upper() + path = environ.get("PATH_INFO", "") + method = environ.get("REQUEST_METHOD", "").upper() def custom_start_response(status, headers, *args): status_code = int(status.split()[0]) if self.verbose_level.value > VerboseLevel.NORMAL.value: Info(f"{status_code} {method} {path}") - elif self.verbose_level.value > VerboseLevel.SILENT.value and path not in self.suppressed_routes: + elif ( + self.verbose_level.value > VerboseLevel.SILENT.value + and path not in self.suppressed_routes + ): Info(f"{status_code} {method} {path}") return start_response(status, headers, *args) @@ -32,7 +42,6 @@ class RequestLoggingMiddleware: return self.app(environ, custom_start_response) - app = Flask(__name__) @@ -43,27 +52,31 @@ resources_path = Path(__file__).resolve().parent.parent / "resources/statics" if not resources_path.exists(): resources_path.mkdir(parents=True) -@app.route('/statics/') + +@app.route("/statics/") def serve_statics(filename): return send_from_directory(resources_path, filename) + app.register_blueprint(account_blueprint) app.register_blueprint(config_blueprint) app.register_blueprint(dispatch_blueprint) -def HandleSdkServer(ServerIp, GameServerPort, SdkServerPort): - app.config['SERVER_IP'] = ServerIp - app.config['GAME_SERVER_PORT'] = GameServerPort - log = logging.getLogger('werkzeug') +def HandleSdkServer(ServerIp, GameServerPort, SdkServerPort): + app.config["SERVER_IP"] = ServerIp + app.config["GAME_SERVER_PORT"] = GameServerPort + + log = logging.getLogger("werkzeug") log.setLevel(logging.ERROR) - Info("HTTP server started on port 80") + Info(f"HTTP server started on port {SdkServerPort}") app.run(host=ServerIp, port=SdkServerPort) + def HandleSslSdkServer(): - log = logging.getLogger('werkzeug') + log = logging.getLogger("werkzeug") log.setLevel(logging.ERROR) Info("HTTPS server started on port 443") - app.run(host='127.0.0.1', port=443,ssl_context='adhoc') + app.run(host="127.0.0.1", port=443, ssl_context="adhoc") diff --git a/sdk_server/controllers/dispatch_controller.py b/sdk_server/controllers/dispatch_controller.py index 358597c..037cb97 100644 --- a/sdk_server/controllers/dispatch_controller.py +++ b/sdk_server/controllers/dispatch_controller.py @@ -1,90 +1,111 @@ -from game_server.config import * +import json +import re +import time +from flask import Blueprint, Response, jsonify, request +from utils.aes import encrypt_ecb +from utils.config import Config -dispatch_blueprint = Blueprint('dispatch', __name__) +dispatch_blueprint = Blueprint("dispatch", __name__) -@dispatch_blueprint.route('/query_dispatch', methods=['GET']) + +@dispatch_blueprint.route("/query_dispatch", methods=["GET"]) def query_dispatch(): - version = request.args.get('version') + version = request.args.get("version") response_data = { - 'retcode': 0, - 'region_list': [{ - 'retcode': 0, - 'dispatch_url': f"http://{Config.GameServer.Ip}/query_gateway", - 'name': Config.RegionName, - 'title': "", - 'ext': get_ext(version) - }] + "retcode": 0, + "region_list": [ + { + "retcode": 0, + "dispatch_url": f"http://{Config.GameServer.IP}/query_gateway", + "name": Config.RegionName, + "title": "", + "ext": get_ext(version), + } + ], } + + if Config.EnableDispatchEncryption: + return Response( + encrypt_ecb(Config.AESKeys.get(version), json.dumps(response_data)), + mimetype="text/plain", + ) + return jsonify(response_data) -@dispatch_blueprint.route('/query_gateway', methods=['GET']) + +@dispatch_blueprint.route("/query_gateway", methods=["GET"]) def query_gateway(): - version = request.args.get('version') - gameserver = { - 'ip': Config.GameServer.Ip, - 'port': Config.GameServer.Port - } - + version = request.args.get("version") + gameserver = {"ip": Config.GameServer.IP, "port": Config.GameServer.Port} + response_data = { - 'retcode': 0, - 'msg': "", - 'region_name': Config.RegionName, - 'account_url': f"http://{Config.GameServer.Ip}/account", - 'account_url_backup': f"http://{Config.GameServer.Ip}/account", - 'asset_bundle_url_list': get_asset_bundle_url_list(version), - 'ex_audio_and_video_url_list': get_ex_audio_and_video_url_list(version), - 'ex_resource_url_list': get_ex_resource_url_list(version), - 'ext': get_ext(version), - 'gameserver': gameserver, - 'gateway': gameserver, - 'is_data_ready': True, - 'oaserver_url': f"http://{Config.GameServer.Ip}/oaserver", - 'server_cur_time': int(time.time()), - 'server_cur_timezone': 8, - 'server_ext': { - 'cdkey_url': f"http://{Config.GameServer.Ip}/common", - 'mihoyo_sdk_env': "2" - } + "retcode": 0, + "msg": "", + "region_name": Config.RegionName, + "account_url": f"http://{Config.GameServer.IP}/account", + "account_url_backup": f"http://{Config.GameServer.IP}/account", + "asset_bundle_url_list": get_asset_bundle_url_list(version), + "ex_audio_and_video_url_list": get_ex_audio_and_video_url_list(version), + "ex_resource_url_list": get_ex_resource_url_list(version), + "ext": get_ext(version), + "gameserver": gameserver, + "gateway": gameserver, + "is_data_ready": True, + "oaserver_url": f"http://{Config.GameServer.IP}/oaserver", + "server_cur_time": int(time.time()), + "server_cur_timezone": 8, + "server_ext": { + "cdkey_url": f"http://{Config.GameServer.IP}/common", + "mihoyo_sdk_env": "2", + }, } - + + if Config.EnableDispatchEncryption: + return Response( + encrypt_ecb(Config.AESKeys.get(version), json.dumps(response_data)), + mimetype="text/plain", + ) + return jsonify(response_data) + def get_ext(version): return { - 'ai_use_asset_bundle': "0" if Config.UseLocalCache else "1", - 'apm_log_level': "0", - 'apm_log_dest': "2", - 'apm_switch': "0", - 'apm_switch_game_log': "1", - 'apm_switch_crash': "1", - 'block_error_dialog': "1", - 'elevator_model_path': "GameEntry/EVA/StartLoading_Model", - 'data_use_asset_bundle': "1", - 'enable_watermark': "1", - 'ex_audio_and_video_url_list': get_ex_audio_and_video_url_list(version), - 'ex_res_buff_size': "10485760", - 'ex_res_pre_publish': "0", - 'ex_res_use_http': "1", - 'ex_resource_url_list': get_ex_resource_url_list(version), - 'is_xxxx': "0", - 'mtp_switch': "0", - 'network_feedback_enable': "0", - 'offline_report_switch': "0", - 'forbid_recharge': "1", - 'is_checksum_off': "0" if Config.UseLocalCache else "1", - 'res_use_asset_bundle': "1", - 'show_version_text': "0", - 'update_streaming_asb': "0", - 'use_multy_cdn': "1", - 'show_bulletin_button': "1", - 'show_bulletin_empty_dialog_bg': "0" + "ai_use_asset_bundle": "0" if Config.UseLocalCache else "1", + "apm_log_level": "0", + "apm_log_dest": "2", + "apm_switch": "0", + "apm_switch_game_log": "1", + "apm_switch_crash": "1", + "block_error_dialog": "1", + "elevator_model_path": "GameEntry/EVA/StartLoading_Model", + "data_use_asset_bundle": "1", + "enable_watermark": "1", + "ex_audio_and_video_url_list": get_ex_audio_and_video_url_list(version), + "ex_res_buff_size": "10485760", + "ex_res_pre_publish": "0", + "ex_res_use_http": "1", + "ex_resource_url_list": get_ex_resource_url_list(version), + "is_xxxx": "0", + "mtp_switch": "0", + "network_feedback_enable": "0", + "offline_report_switch": "0", + "forbid_recharge": "1", + "is_checksum_off": "0" if Config.UseLocalCache else "1", + "res_use_asset_bundle": "1", + "show_version_text": "0", + "update_streaming_asb": "0", + "use_multy_cdn": "1", + "show_bulletin_button": "1", + "show_bulletin_empty_dialog_bg": "0", } + def get_asset_bundle_url_list(version): # Compile the regex pattern regex = re.compile(r"^(.*?)_(os|gf|global)_(.*?)$") match = regex.match(version) - value = match.group(2) + value = match.group(2) if Config.UseLocalCache: return get_local_url_list(value, version) # Proceed if there's a match @@ -93,35 +114,36 @@ def get_asset_bundle_url_list(version): if value == "os": return [ "https://bundle-aliyun-os.honkaiimpact3.com/asset_bundle/overseas01/1.1", - "https://hk-bundle-os-mihayo.akamaized.net/asset_bundle/overseas01/1.1" + "https://hk-bundle-os-mihayo.akamaized.net/asset_bundle/overseas01/1.1", ] elif value == "gf": if "beta" in version: return [ "https://autopatchbeta.bh3.com/asset_bundle/beta_release/1.0", - "https://autopatchbeta.bh3.com/asset_bundle/beta_release/1.0" + "https://autopatchbeta.bh3.com/asset_bundle/beta_release/1.0", ] return [ "https://bundle-qcloud.bh3.com/asset_bundle/android01/1.0", - "https://bundle.bh3.com/asset_bundle/android01/1.0" + "https://bundle.bh3.com/asset_bundle/android01/1.0", ] elif value == "global": return [ "http://hk-bundle-west-mihayo.akamaized.net/asset_bundle/usa01/1.1", - "http://bundle-aliyun-usa.honkaiimpact3.com/asset_bundle/usa01/1.1" + "http://bundle-aliyun-usa.honkaiimpact3.com/asset_bundle/usa01/1.1", ] else: return [ "https://bundle-aliyun-os.honkaiimpact3.com/asset_bundle/overseas01/1.1", - "https://hk-bundle-os-mihayo.akamaized.net/asset_bundle/overseas01/1.1" + "https://hk-bundle-os-mihayo.akamaized.net/asset_bundle/overseas01/1.1", ] return [] + def get_ex_audio_and_video_url_list(version): # Compile the regex pattern regex = re.compile(r"^(.*?)_(os|gf|global)_(.*?)$") match = regex.match(version) - value = match.group(2) + value = match.group(2) if Config.UseLocalCache: return get_local_url_list(value, version) if match: @@ -129,35 +151,36 @@ def get_ex_audio_and_video_url_list(version): if value == "os": return [ "bigfile-aliyun-os.honkaiimpact3.com/com.miHoYo.bh3oversea", - "hk-bigfile-os-mihayo.akamaized.net/com.miHoYo.bh3oversea" + "hk-bigfile-os-mihayo.akamaized.net/com.miHoYo.bh3oversea", ] elif value == "gf": if "beta" in version: return [ "autopatchbeta.bh3.com/tmp/CGAudio", - "autopatchbeta.bh3.com/tmp/CGAudio" + "autopatchbeta.bh3.com/tmp/CGAudio", ] return [ "bh3rd-beta-qcloud.bh3.com/tmp/CGAudio", - "bh3rd-beta.bh3.com/tmp/CGAudio" + "bh3rd-beta.bh3.com/tmp/CGAudio", ] elif value == "global": return [ "bh3rd-beta-qcloud.bh3.com/tmp/CGAudio", - "bh3rd-beta.bh3.com/tmp/CGAudio" + "bh3rd-beta.bh3.com/tmp/CGAudio", ] else: return [ "bh3rd-beta-qcloud.bh3.com/tmp/CGAudio", - "bh3rd-beta.bh3.com/tmp/CGAudio" + "bh3rd-beta.bh3.com/tmp/CGAudio", ] return [] + def get_ex_resource_url_list(version): - # Compile the regex pattern + # Compile the regex pattern regex = re.compile(r"^(.*?)_(os|gf|global)_(.*?)$") match = regex.match(version) - value = match.group(2) + value = match.group(2) if Config.UseLocalCache: return get_local_url_list(value, version) # Proceed if there's a match @@ -166,33 +189,30 @@ def get_ex_resource_url_list(version): if value == "os": return [ "bigfile-aliyun-os.honkaiimpact3.com/com.miHoYo.bh3oversea", - "hk-bigfile-os-mihayo.akamaized.net/com.miHoYo.bh3oversea" + "hk-bigfile-os-mihayo.akamaized.net/com.miHoYo.bh3oversea", ] elif value == "gf": if "beta" in version: return [ "autopatchbeta.bh3.com/tmp/beta", - "autopatchbeta.bh3.com/tmp/beta" + "autopatchbeta.bh3.com/tmp/beta", ] - return [ - "bundle-qcloud.bh3.com/tmp/Original", - "bundle.bh3.com/tmp/Original" - ] + return ["bundle-qcloud.bh3.com/tmp/Original", "bundle.bh3.com/tmp/Original"] elif value == "global": return [ "hk-bundle-west-mihayo.akamaized.net/tmp/com.miHoYo.bh3global", - "bigfile-aliyun-usa.honkaiimpact3.com/tmp/com.miHoYo.bh3global" + "bigfile-aliyun-usa.honkaiimpact3.com/tmp/com.miHoYo.bh3global", ] else: return [ "bigfile-aliyun-os.honkaiimpact3.com/com.miHoYo.bh3oversea", - "hk-bigfile-os-mihayo.akamaized.net/com.miHoYo.bh3oversea" + "hk-bigfile-os-mihayo.akamaized.net/com.miHoYo.bh3oversea", ] return [] + def get_local_url_list(type, version): return [ - f"http://{Config.GameServer.Ip}/statics/{type}/{version.replace('.', '_')}", - f"http://{Config.GameServer.Ip}/statics/{type}/{version.replace('.', '_')}" + f"http://{Config.GameServer.IP}/statics/{type}/{version.replace('.', '_')}", + f"http://{Config.GameServer.IP}/statics/{type}/{version.replace('.', '_')}", ] - diff --git a/sdk_server/models/granter_login_body.py b/sdk_server/models/granter_login_body.py index 28eed45..c92ae1a 100644 --- a/sdk_server/models/granter_login_body.py +++ b/sdk_server/models/granter_login_body.py @@ -1,13 +1,12 @@ -# sdkserver/models/granter_login_body.py - from pydantic import BaseModel -from typing import Optional + class GranterLoginBodyData(BaseModel): uid: str guest: bool token: str + class GranterLoginBody(BaseModel): app_id: int channel_id: int diff --git a/sdk_server/models/risky_check.py b/sdk_server/models/risky_check.py index 4145c3a..97d5097 100644 --- a/sdk_server/models/risky_check.py +++ b/sdk_server/models/risky_check.py @@ -1,13 +1,13 @@ -# sdkserver/models/risky_check.py - from pydantic import BaseModel from typing import Optional + class DataScheme(BaseModel): id: str action: str geetest: Optional[object] + class RiskyCheck(BaseModel): retcode: int message: str diff --git a/sdk_server/models/shield_verify_body.py b/sdk_server/models/shield_verify_body.py index 8d53943..900044d 100644 --- a/sdk_server/models/shield_verify_body.py +++ b/sdk_server/models/shield_verify_body.py @@ -1,5 +1,6 @@ from pydantic import BaseModel + class ShieldVerifyBody(BaseModel): token: str - uid: str \ No newline at end of file + uid: str