Skip to content

Instantly share code, notes, and snippets.

@socram8888
Created May 3, 2020 22:31
Show Gist options
  • Save socram8888/12e96619a809d248a480d5ca65950f55 to your computer and use it in GitHub Desktop.
Save socram8888/12e96619a809d248a480d5ca65950f55 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import asyncio
import websockets
import json
import uuid
import httpx
# I got these by MitM'ing a valid client!
DEVICE_TOKEN = None
TITLE_TOKEN = None
USER_TOKEN = None
assert(DEVICE_TOKEN)
assert(TITLE_TOKEN)
assert(USER_TOKEN)
xbl_token = None
xbl_auth = None
sess_id = str(uuid.uuid4())
rta_conn = None
conn_id = None
member_count = None
async def get_xbl_token():
global xbl_token
global xbl_auth
print('Requesting XBL token')
renew_req = {
"Properties": {
"DeviceToken": DEVICE_TOKEN,
"SandboxId": "RETAIL",
"TitleToken": TITLE_TOKEN,
"UserTokens": [
USER_TOKEN
]
},
"RelyingParty": "http://xboxlive.com",
"TokenType": "JWT"
}
reply = httpx.post('https://xsts.auth.xboxlive.com/xsts/authorize', json=renew_req)
if reply.status_code != 200:
raise Exception('Received an error trying to fetch XBL token')
xbl_auth = reply.json()
xbl_token = 'XBL3.0 x=%s;%s' % (xbl_auth['DisplayClaims']['xui'][0]['uhs'], xbl_auth['Token'])
print('Succeeded')
async def rta_open():
global rta_conn
global conn_id
uri = "wss://rta.xboxlive.com/connect"
rta_conn = await websockets.connect(uri, extra_headers={'Authorization': xbl_token})
print('Requesting session data')
await rta_conn.send('[1,1,"https://sessiondirectory.xboxlive.com/connections/"]')
sess_data = json.loads(await rta_conn.recv())
print('Got session data: %s' % repr(sess_data))
conn_id = sess_data[4]['ConnectionId']
async def rta_keepalive():
print('Keeping alive the session')
async for message in rta_conn:
print('Received message: %s' % repr(message))
await update_member_count()
async def update_member_count():
global member_count
client = httpx.AsyncClient()
client.headers['Authorization'] = xbl_token
client.headers['x-xbl-contract-version'] = '107'
print('Getting member count')
reply = await client.get('https://sessiondirectory.xboxlive.com/serviceconfigs/4fc10100-5f7a-4470-899b-280835760c07/sessionTemplates/MinecraftLobby/sessions/' + sess_id)
if reply.status_code != 200:
print('Oops, error fetching member count: ' % reply.text)
return
reply = reply.json()
new_member_count = reply['membersInfo']['count']
print('Members: %d' % new_member_count)
if member_count != new_member_count:
member_count = new_member_count
print('Updating member count')
join_req = {"members":{"me":{"properties":{"system":{"active":True,"connection":conn_id}}}},"properties":{"custom":{"MemberCount":member_count}}}
reply = await client.put('https://sessiondirectory.xboxlive.com/serviceconfigs/4fc10100-5f7a-4470-899b-280835760c07/sessionTemplates/MinecraftLobby/sessions/' + sess_id, json=join_req)
print(reply.text)
async def sess_publish():
client = httpx.AsyncClient()
client.headers['Authorization'] = xbl_token
client.headers['x-xbl-contract-version'] = '107'
publish_req = {
"members": {
"me": {
"constants": {
"system": {
"initialize": True,
"xuid": xbl_auth['DisplayClaims']['xui'][0]['xid']
}
},
"properties": {
"system": {
"active": True,
"connection": conn_id,
"secureDeviceAddress": "",
"subscription": {
"changeTypes": [ "everything" ],
"id": conn_id
}
}
}
}
},
"properties": {
"custom": {
"BroadcastSetting": 3,
"Joinability": "joinable_by_friends",
"LanGame": True,
"MaxMemberCount": 10,
"MemberCount": 0,
"OnlineCrossPlatformGame": True,
"SupportedConnections": [
{
"ConnectionType": 1,
"HostIpAddress": "192.168.0.245",
"HostPort": 19132,
"RakNetGUID": ""
},
{
"ConnectionType": 3,
"HostIpAddress": "81.202.155.93",
"HostPort": 19132,
"RakNetGUID": ""
}
],
"hostName": xbl_auth['DisplayClaims']['xui'][0]['gtg'],
"levelId": "wx+aXvsoDwI=",
"ownerId": xbl_auth['DisplayClaims']['xui'][0]['xid'],
"protocol": 390,
"rakNetGUID": "9395016890158178779",
"version": "1.14.60",
"worldName": "¡A fregar!",
"worldType": "Survival"
},
"system": {
"closed": False,
"joinRestriction": "followed",
"readRestriction": "followed"
}
}
}
print('Publishing service')
reply = await client.put('https://sessiondirectory.xboxlive.com/serviceconfigs/4fc10100-5f7a-4470-899b-280835760c07/sessionTemplates/MinecraftLobby/sessions/' + sess_id, json=publish_req, timeout=3)
print(reply.text)
await asyncio.sleep(1)
print('Checking handle')
handles_req = {"sessionRef":{"name":sess_id,"scid":"4fc10100-5f7a-4470-899b-280835760c07","templateName":"MinecraftLobby"},"type":"activity","version":1}
reply = await client.post('https://sessiondirectory.xboxlive.com/handles', json=handles_req)
print(reply.text)
await asyncio.sleep(1)
while True:
client.headers['Authorization'] = xbl_token
await update_member_count()
await asyncio.sleep(120)
async def presence():
client = httpx.AsyncClient()
client.headers['Authorization'] = xbl_token
client.headers['x-xbl-contract-version'] = '3'
while True:
client.headers['Authorization'] = xbl_token
print('Keeping alive presence')
keepalive = {'state': 'active'}
xid = xbl_auth['DisplayClaims']['xui'][0]['xid']
reply = await client.post('https://userpresence.xboxlive.com/users/xuid(%s)/devices/current/titles/current' % xid, json=keepalive)
if reply.status_code == 200:
print('Presence OK')
else:
print('Presence error:' + reply.text)
await asyncio.sleep(60)
async def renew_token():
while True:
await asyncio.sleep(10 * 60)
await get_xbl_token()
loop = asyncio.get_event_loop()
loop.run_until_complete(get_xbl_token())
loop.run_until_complete(rta_open())
loop.create_task(rta_keepalive())
loop.create_task(sess_publish())
loop.create_task(presence())
loop.create_task(renew_token())
loop.run_forever()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment