Skip to content

Commit

Permalink
version 0.57
Browse files Browse the repository at this point in the history
  • Loading branch information
FriendsOfGalaxy committed Mar 25, 2021
1 parent a44df8b commit eb1d70a
Show file tree
Hide file tree
Showing 13 changed files with 356 additions and 60 deletions.
113 changes: 113 additions & 0 deletions nethook_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import sys
import pathlib
import shutil
import os
import typing as t
import psutil
import time
from collections import Counter


STEAM_PATH = R'C:\Program Files (x86)\Steam\steam.exe'
GALAXY_PATH = R'C:\Program Files (x86)\GOG Galaxy\GalaxyClient.exe'
NETHOOK_PATH = None # R'C:COMPLETE_ME\\NetHook2\NetHook2.dll'


def find_steam_ps(initial: t.Optional[psutil.Process]=None) -> t.Optional[psutil.Process]:
if initial and initial.is_running():
return initial
for p in psutil.process_iter(['exe'], ad_value=''):
if STEAM_PATH == p.info['exe']:
return p
return None


def restart_steam():
p = psutil.Popen([STEAM_PATH, "-shutdown", "-silent"])
p.wait()
steam_ps = find_steam_ps()
if steam_ps:
steam_ps.wait(timeout=10)
return psutil.Popen(STEAM_PATH)


def restart_galaxy():
p = psutil.Popen([GALAXY_PATH, '/command=shutdown'])
p.wait()
psutil.Popen(GALAXY_PATH)


def inject_nethook(steam_ps=None):
"""Requires running as admin"""
timeout = time.time() + 10
while True:
steam_ps = find_steam_ps(steam_ps)
assert steam_ps, 'NetHook requires Steam running'
if time.time() > timeout:
print('timeout on loading steamclint.dll by Steam')
break
if [dll for dll in steam_ps.memory_maps() if 'steamclient.dll' in dll.path]:
print('steamclient.dll loaded. Injecting nethook')
break
cmd = f'rundll32 "{NETHOOK_PATH}",Inject'
psutil.Popen(cmd)


def copy_results_to_common_dir(dest_dir, must_include=''):
os.makedirs(dest_dir, exist_ok=True)
steam_nethook_dir = pathlib.PurePath(STEAM_PATH).parent / 'nethook'
for d in os.listdir(steam_nethook_dir):
if steam_nethook_dir.name == d: # avoid recurssion
continue
for f in os.listdir(steam_nethook_dir / d):
if must_include not in f:
continue
src = steam_nethook_dir / d / f
dest = pathlib.PurePath(dest_dir) / (src.name + '_' + d + src.suffix) # originalname_timestamp.bin
shutil.copy(src, dest)


def print_statistics(first_signals=10):
steam_nethook_dir = pathlib.PurePath(STEAM_PATH).parent / 'nethook'
signals = Counter()
for root, dirs, files in os.walk(steam_nethook_dir):
signal_names = []
for i, f in enumerate(sorted(files)):
if i > first_signals:
break
signal_names.append(f.split('_')[-1])
signals.update(signal_names)
print(signals)


def main():
if NETHOOK_PATH is None:
print('set up nethook path in script first')

try:
no = int(sys.argv[1])
except (IndexError, ValueError):
print('Usage: <script> <number_of_restarts> <path_to_copy_chosen_signals>')
return

try:
output = sys.argv[2]
except IndexError:
output = 'output_nethook'

for i in range(no):
try:
print(f'Running {i+1}. time...')
steam_proc: psutil.Popen = restart_steam()
inject_nethook(steam_proc)
time.sleep(12) # Steam startup
except Exception as e:
print(repr(e))
continue

print_statistics()
copy_results_to_common_dir(output, must_include='ClientLogOnResponse')


if __name__ == "__main__":
main()
25 changes: 25 additions & 0 deletions src/local_machine_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import base64
import logging

from persistent_cache_state import PersistentCacheState


logger = logging.getLogger(__name__)


class LocalMachineCache:

def __init__(self, persistent_cache: dict, cache_state: PersistentCacheState):
self._cache_state = cache_state
self._persistent_cache = persistent_cache

@property
def machine_id(self) -> bytes:
return base64.b64decode(self._persistent_cache.get('machine_id', ''))

@machine_id.setter
def machine_id(self, machine_id: bytes):
self._persistent_cache['machine_id'] = base64.b64encode(machine_id).decode('utf-8')
self._cache_state.modified = True
logger.info("Set new machine ID: %s" % machine_id)

4 changes: 3 additions & 1 deletion src/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
StateFlags, local_games_list, get_state_changes, get_client_executable,
load_vdf, get_library_folders, get_app_manifests, app_id_from_manifest_path
)
from local_machine_cache import LocalMachineCache
from websocket_list import WebSocketList
from presence import presence_from_user_info
from friends_cache import FriendsCache
Expand Down Expand Up @@ -131,9 +132,10 @@ async def user_presence_update_handler(user_id: str, proto_user_info: ProtoUserI
def handshake_complete(self):
websocket_list = WebSocketList(self._client)
ownership_ticket_cache = OwnershipTicketCache(self.persistent_cache, self._persistent_storage_state)
local_machine_cache = LocalMachineCache(self.persistent_cache, self._persistent_storage_state)
self._steam_client = WebSocketClient(self._client, self._ssl_context, websocket_list, self._friends_cache,
self._games_cache, self._translations_cache, self._stats_cache,
self._times_cache, self._user_info_cache, ownership_ticket_cache)
self._times_cache, self._user_info_cache, local_machine_cache, ownership_ticket_cache)

async def shutdown(self):
self._regmon.close()
Expand Down
4 changes: 4 additions & 0 deletions src/protocol/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,10 @@ class EOSType(enum.IntEnum):
MacOS1010 = -87
MacOS1011 = -86
MacOS1012 = -85
MacOS1013 = -84
MacOS1014 = -83
MacOS1015 = -82
MacOS1016 = -81
MacOSMax = -1
LinuxUnknown = -203
Linux22 = -202
Expand Down
79 changes: 44 additions & 35 deletions src/protocol/protobuf_client.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import asyncio
import ipaddress
import struct
import gzip
import hashlib
import ipaddress
import json
import logging
import socket
import struct
from itertools import count
from typing import Awaitable, Callable,Dict, Optional, Any
from galaxy.api.errors import UnknownBackendResponse
from typing import Awaitable, Callable, Dict, Optional, Any
from typing import List, NamedTuple

from protocol.messages import steammessages_base_pb2, steammessages_clientserver_login_pb2, steammessages_player_pb2, \
steammessages_clientserver_friends_pb2, steammessages_clientserver_pb2, steamui_libraryroot_pb2, steammessages_clientserver_2_pb2
import vdf
from galaxy.api.errors import UnknownBackendResponse

from protocol.consts import EMsg, EResult, EAccountType, EFriendRelationship, EPersonaState
from protocol.messages import steammessages_base_pb2, steammessages_clientserver_login_pb2, steammessages_player_pb2, \
steammessages_clientserver_friends_pb2, steammessages_clientserver_pb2, steamui_libraryroot_pb2, \
steammessages_clientserver_2_pb2
from protocol.types import SteamId, ProtoUserInfo

import vdf
import hashlib

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
LOG_SENSITIVE_DATA = False
Expand All @@ -31,6 +33,8 @@ class ProtobufClient:
_PROTO_MASK = 0x80000000
_ACCOUNT_ID_MASK = 0x0110000100000000
_IP_OBFUSCATION_MASK = 0x606573A4
_MSG_PROTOCOL_VERSION = 65580
_MSG_CLIENT_PACKAGE_VERSION = 1561159470

def __init__(self, set_socket):
self._socket = set_socket
Expand Down Expand Up @@ -107,22 +111,15 @@ async def get_app_ownership_ticket(self, app_id: int):
async def register_auth_ticket_with_cm(self, ticket: bytes):
message = steammessages_clientserver_pb2.CMsgClientRegisterAuthTicketWithCM()
message.ticket = ticket
message.protocol_version = 65580
message.client_instance_id = 0 # ??
message.protocol_version = self._MSG_PROTOCOL_VERSION
await self._send(EMsg.ClientRegisterAuthTicketWithCM, message)

async def log_on_password(self, account_name, password, two_factor, two_factor_type):
async def log_on_password(self, account_name, password, two_factor, two_factor_type, machine_id, os_value, sentry):
def sanitize_password(password):
return ''.join([i if ord(i) < 128 else '' for i in password])

message = steammessages_clientserver_login_pb2.CMsgClientLogon()
message.account_name = account_name
message.protocol_version = 65580
message = self._prepare_log_on_msg(account_name, machine_id, os_value, sentry)
message.password = sanitize_password(password)
message.should_remember_password = True
message.supports_rate_limit_response = True
message.obfuscated_private_ip.v4 = self._get_obfuscated_private_ip()

if two_factor:
if two_factor_type == 'email':
message.auth_code = two_factor
Expand All @@ -131,25 +128,36 @@ def sanitize_password(password):
logger.info("Sending log on message using credentials")
await self._send(EMsg.ClientLogon, message)

async def log_on_token(self, steam_id, account_name, token, used_server_cell_id):
async def log_on_token(self, account_name, token, used_server_cell_id, machine_id, os_value, sentry):
message = self._prepare_log_on_msg(account_name, machine_id, os_value, sentry)
message.cell_id = used_server_cell_id
message.login_key = token
logger.info("Sending log on message using token")
await self._send(EMsg.ClientLogon, message)

def _prepare_log_on_msg(self, account_name: str, machine_id: bytes, os_value: int, sentry) -> "steammessages_clientserver_login_pb2.CMsgClientLogon":
message = steammessages_clientserver_login_pb2.CMsgClientLogon()
message.account_name = account_name
message.cell_id = used_server_cell_id
message.protocol_version = 65580
message.protocol_version = self._MSG_PROTOCOL_VERSION
message.client_package_version = self._MSG_CLIENT_PACKAGE_VERSION
message.client_language = "english"
message.should_remember_password = True
message.supports_rate_limit_response = True
message.login_key = token
message.steamguard_dont_remember_computer = False
message.obfuscated_private_ip.v4 = self._get_obfuscated_private_ip()
message.qos_level = 3
message.machine_name = socket.gethostname()
message.client_os_type = os_value if os_value >= 0 else 0
message.machine_id = machine_id

sentry = await self.sentry()
if sentry:
logger.info("Sentry present")
message.eresult_sentryfile = EResult.OK
message.sha_sentryfile = sentry
else:
message.eresult_sentryfile = EResult.FileNotFound

self.steam_id = steam_id
logger.info("Sending log on message using token")
await self._send(EMsg.ClientLogon, message)
return message

def _get_obfuscated_private_ip(self) -> int:
host, port = self._socket.local_address
Expand Down Expand Up @@ -224,9 +232,10 @@ async def get_presence_localization(self, appid, language='english'):
message.language = language

job_id = next(self._job_id_iterator)
await self._send(EMsg.ServiceMethodCallFromClient, message, job_id, None, target_job_name='Community.GetAppRichPresenceLocalization#1')
await self._send(EMsg.ServiceMethodCallFromClient, message, job_id, None,
target_job_name='Community.GetAppRichPresenceLocalization#1')

async def accept_update_machine_auth(self,jobid_target, sha_hash, offset, filename, cubtowrite):
async def accept_update_machine_auth(self, jobid_target, sha_hash, offset, filename, cubtowrite):
message = steammessages_clientserver_2_pb2.CMsgClientUpdateMachineAuthResponse()
message.filename = filename
message.eresult = EResult.OK
Expand All @@ -244,12 +253,12 @@ async def accept_new_login_token(self, unique_id, jobid_target):
await self._send(EMsg.ClientNewLoginKeyAccepted, message, None, jobid_target, None)

async def _send(
self,
emsg,
message,
source_job_id=None,
target_job_id=None,
target_job_name=None
self,
emsg,
message,
source_job_id=None,
target_job_id=None,
target_job_name=None
):
proto_header = steammessages_base_pb2.CMsgProtoBufHeader()
if self.steam_id is not None:
Expand Down
Loading

0 comments on commit eb1d70a

Please sign in to comment.