XLive (Games for Windows Live)

Overview

Warning

Some observations are hard to interpret as XLive looooves to crash a lot when attempting to tamper it. For example: XLive itself has to be modified in order to make it debuggable which might yield incorrect results.

XLive uses several anti-tampering tricks:

TrickBypass
Signature Check
Modification CheckByte patch (the irony lol)
IAT ObfuscationHook by ordinal
Hook ProtectionSubstitution
Hardware/Software-Breakpoint ChecksThread Suspension
Function Obfuscation and Encryption

Call Order

At launch

  • XNetGetTitleXnAddr
  • XLivePreTranslateMessage
  • XUserGetSigninState
  • XLiveInitializeEx
    • On failure: engine warns of debugger usage and shuts down.
  • XNetStartup
    • If XLiveInitializeEx succeeded.
  • XOnlineStartup
    • If XNetStartup succeeded.
  • XSocketWSAGetLastError
    • If XNetStartup errored.
  • XHVCreateEngine
  • XUserGetSigninInfo
  • XUserGetSigninStat
  • XNotifyCreateListener
  • XNotifyPositionUI
  • XPresenceInitialize
  • XLiveSetDebugLevel
    • If XNotifyCreateListener succeeded.
  • XSocketNTOHS
    • If XNotifyCreateListener succeeded.
  • XNetSetSystemLinkPort
    • If XNotifyCreateListener succeeded.
  • XLiveInput
    • Called frequently. Game stalls if call errored.
  • XLiveRender
    • Called frequently.
  • XUserGetName
  • XFriendsCreateEnumerator
  • XUserCreateAchievementEnumerator
  • XUserSetContext
  • XNotifyGetNext
    • Called frequently.

When pressing start

  • XShowSigninUI

When pressing Live

  • XShowGuideUI

When starting a game

  • XUserGetName
  • XUserGetName
  • XFriendsCreateEnumerator
  • XUserSetContext

When triggering an achievement

  • XUserWriteAchievements

Game exit

  • XLiveUninitialize
  • XOnlineCleanup
  • XLiveOnDestroyDevice
  • XLiveUninitialize
  • XNetCleanup

Threads

The game seems to be in a playable state even when XLive's "Main" thread gets suspended. Obviously this will make it freeze if you want to interact with XLive but at least this will bypass the anti-debugging checks.

IndexStart AddressDescription
00x146ab8UI
10x146ab8Main
20x146ab8Anti-Debugger
30x146ab8Anti-Debugger
40x146ab8Anti-Debugger
50x146ab8Anti-Debugger
60x146ab8Anti-Debugger
70x146ab8Anti-Debugger
80x4f1077Connection
90xda3f3Notifications
100xda3f3Notifications

It's probably possible to patch a few places to get rid of the debugger checks but I suspect that it won't be that easy. Let's say XLive works like this and we write a naive patch:

void xlive()
{
  // Try jumping after all the checks
  Debugger_check();

  // This will be a problem if this sets important values
  Operation_with_side_effects();

  Another_debugger_check();
}

There is another problem which I suspect is how XLive actually works, since there are so many threads of "Main":

void xlive()
{
  // Try jumping after all the checks
  Debugger_check();

  // Wake up other thread
  Signal_next_thread_and_wait();

  // Other thread will continue somewhere with its own debugger checks
  Another_debugger_check();
}

This is too painful to try and verify. All of this is a lot more complicated with 8 "Main" threads and a ton of different debugger checks. It's actually hilarious if you see the amount of debugger confusion for yourself.

Signature Check

XLive will not initialize properly without first validating GridGame.exe with GridGame.exe.cat for modifications. The .cat file is a security catalogue file which is signed by Microsoft Windows Games for Live:

DetailDescription
SignerMicrosoft LIVE PCA
Signing TimeSaturday, 30 October 2010 01:55:25

XLive does periodic checks on the code segments of all loaded files that have catalogue entries, including itself.

Todo

Isn't it a bit too late to check if the file has been modified if it already launched?

Todo

Figure out if Microsoft messed up and we can simply bypass this too.

Modification Check

From current observation the game seems to always check for modifications when loading the save file by calling XLiveUnprotectData. There is a simple two-byte-patch method that was originally found by several people in 2009 and before which eliminates this check.

Hook Protection

Some functions are protected by obfuscation and anti-tampering tricks! XLive checks if the correct return address from the caller's module (the base module) is on the stack. A traditional function hook by calling the original function would not work. However XLive does not check if the call is a substitution :^).

__declspec(naked) void hook()
{
  // save registers
  // do whatever...
  // restore registers

  // return address should be on the correct stack location 
  __asm jmp to_original_function;
}

The reason why hooking through a trampoline function does not work is because of a direct call to a decrypt function. This function will use the return address to decrypt the rest of the function body. What's interesting is that they modify the stack using Return Oriented Programming (ROP) in order to jump to the next decrypt function or to another ROP gadget. In several other function locations some new code will be written and executed. Although not all calls to sub-functions are encrypted.

__declspec(naked) void xlive_5034()
{
  // Stack:
  //     0x20ff0000 -> return address of caller
  //                   higher bits for the module base might be checked

  __asm {
      call decrypt_xlive_5034;
  };

  // Stack:
  //     0x20ff0004 -> "return address" for decryption function
  //                   which will be used to execute the body

  __asm {
      encoded_metadata_bytes;
      // Rest of the encrypted body
  };
}

The values below the calling function are metadata which are used to determine the size and the address of the body. Thread synchronization primitives (omitted in the code-snippet below) are being used at the beginning which seem to be the main reason why stepping through this function with a debugger would cause a crash. The function mostly consists of operations which decrypt the function body. There are several reads to global variables which hold additional information.

// Partially reversed decrypt function
void decrypt_xlive_5034()
{
  auto ra = uintptr_t(_ReturnAddress());
  auto encoded = *(int32_t*)ra;

  auto body_size = (encoded >> 4) + 0x4fff - (((encoded >> 4) + 0x4fff) % 0x5000);
  auto address_of_body = ra + (encoded & 3) + 4;

  auto offset = encoded >> 4;
  auto offset_into_body = address_of_body + offset;
  auto relative_address = address_of_body - module_base;

  auto var1 = *(int32_t*)offset_into_body;
  auto var2 = *(int32_t*)(offset_into_body + 4);

  auto length = var2 & 0xffffffff;
  auto address = module_base + (var1 & 0xffffffff);

  // ...
}

In very special cases the return address of the caller (the process module) will be used. This will fail if we simply call an XLive function from any other module.

Todo

Figure out if it actually is the main module.

One odd thing seems to happen with XLiveCloseProtectedDataContext though which might be a bug in XLive. This function will crash the process if a call to XLiveUnprotectData fails to read parts of a corrupted save file. However the call to XLiveCloseProtectedDataContext will succeed if it gets called from a different module.

Imported Functions (IAT)

Warning

XLive has more exported functions than the game has imported.

OrdinalNameNotes
3XSocketCreate
4XSocketClose
6XSocketIOCTLSocket
7XSocketSetSockOpt
8XSocketGetSockOpt
9XSocketGetSockName
11XSocketBind
12XSocketConnect
13XSocketListen
14XSocketAccept
15XSocketSelect
18XSocketRecvcalled frequently
20XSocketRecvFrom
22XSocketSendcalled frequently
24XSocketSendTo
27XSocketWSAGetLastError
38XSocketNTOHS
51XNetStartup
52XNetCleanup
53XNetRandom
55XNetRegisterKey
56XNetUnregisterKey
57XNetXnAddrToInAddr
58XNetServerToInAddr
63XNetUnregisterInAddr
67XNetDnsLookup
68XNetDnsRelease
69XNetQosListen
70XNetQosLookup
71XNetQosServiceLookup
72XNetQosRelease
73XNetGetTitleXnAddr
75XNetGetEthernetLinkStatus
77XNetQosGetListenStats
84XNetSetSystemLinkPort
651XNotifyGetNextcalled frequently
652XNotifyPositionUI
1082XGetOverlappedExtendedError
1083XGetOverlappedResult
5001XLiveInputcalled frequently
5002XLiveRendercalled frequently
5003XLiveUninitialize
5006XLiveOnDestroyDevice
5007XLiveOnResetDevice
5008XHVCreateEngine
5016XLivePBufferAllocate
5017XLivePBufferFree
5030XLivePreTranslateMessagecalled frequently
5031XLiveSetDebugLevel
5034XLiveProtectData
5035XLiveUnprotectData
5036XLiveCreateProtectedDataContext
5038XLiveCloseProtectedDataContext
5206XShowMessagesUI
5208XShowGameInviteUI
5209XShowMessageComposeUI
5210XShowFriendRequestUI
5212XShowCustomPlayerListUIcalled frequently
5214XShowPlayerReviewUI
5215XShowGuideUI
5216XShowKeyboardUI
5250XShowAchievementsUI
5251XCloseHandle
5252XShowGamerCardUI
5256XEnumerate
5258XLiveSignout
5259XLiveSignin
5260XShowSigninUI
5262XUserGetSigninStatecalled frequently
5263XUserGetName
5264XUserAreUsersFriends
5265XUserCheckPrivilege
5267XUserGetSigninInfo
5270XNotifyCreateListener
5271XShowPlayersUI
5274XUserAwardGamerPicture
5275XShowFriendsUI
5277XUserSetContext
5278XUserWriteAchievements
5279XUserReadAchievementPicture
5280XUserCreateAchievementEnumerator
5281XUserReadStats
5284XUserCreateStatsEnumeratorByRank
5286XUserCreateStatsEnumeratorByXuid
5292XUserSetContextEx
5293XUserSetPropertyEx
5294XLivePBufferGetByteArray
5297XLiveInitializeEx
5300XSessionCreate
5303XStringVerify
5305XStorageUploadFromMemory
5306XStorageEnumerate
5310XOnlineStartup
5311XOnlineCleanup
5318XSessionStart
5312XFriendsCreateEnumerator
5313XPresenceInitialize
5314XUserMuteListQuery
5315XInviteGetAcceptedInfo
5316XInviteSend
5317XSessionWriteStats
5320XSessionSearchByID
5321XSessionSearch
5322XSessionModify
5324XOnlineGetNatType
5326XSessionJoinRemote
5327XSessionJoinLocal
5328XSessionGetDetails
5329XSessionFlushStats
5330XSessionDelete
5331XUserReadProfileSettings
5332XSessionEnd
5333XSessionArbitrationRegister
5335XTitleServerCreateEnumerator
5336XSessionLeaveRemote
5337XUserWriteProfileSettings
5338XPresenceSubscribe
5340XPresenceCreateEnumerator
5342XSessionModifySkill
5343XSessionCalculateSkill
5344XStorageBuildServerPath
5345XStorageDownloadToMemory
5350XLiveContentCreateAccessHandle
5355XLiveContentGetPath
5356XLiveContentGetDisplayName
5360XLiveContentCreateEnumerator
5365XShowMarketplaceUI
5367XContentGetMarketplaceCounts
5372XMarketplaceCreateOfferEnumerator

Files

Located in AppData\Local\Microsoft\Xlive.

Content:

  • <XUID>\FFFE07D1\00010000
    • <XUID>_MountPt
      • <TITLE_ID>.gpd
      • Account
      • FFFE07D1.gpd
      • tile_32.png
      • tile_64.png
    • <XUID>

Titles:

  • <TITLE_ID> (425607F3 for Tron: Evolution)
    • config.bin
    • Token.bin

The XUID for each profile is a 64-bit number generated from the "PCID" stored in the registry, E0000YYYXXXXXXXX, where XXXXXXX is a snippet of the PCID and YYY is an incrementing value.

FFFE07D1 is to signify the "Xbox 360 Dashboard" title, and 00010000 to signify that the content is a User Profile. The .gpd files are endian-reversed Xbox 360 XDBF files, used to store achievements and some statistics (Gamer Profile Data) for each game played. The "Account" file is an endian-reversed Xbox 360 STFS header, without a console signature or attached filesystem (as it is all in _MountPt).

config.bin stores the "machine account" and other XLive configuration data for the title, and Token.bin stores an encrypted copy of the 5x5 product key used for sign-in.

Authentication

Using Kerberos Protocol.

Kerberos: 40.64.89.190

Xbox: tgs.prod.xboxlive.com, 65.55.42.217

Client

Command: GFWLClient.exe /NoAutoSignIn /NoInterface

Catalog

Tron: Evolution Media ID: 66acd000-77fe-1000-9115-d804425607f3

Host: catalog.xboxlive.com

API: GET /Catalog/Catalog.asmx/Query?methodName=

FindGameOffers

NamesValues
Localeen-US
LegalLocaleen-US
Store3
PageSize10
PageNum1
DetailView3
OfferFilterLevel1
MediaIds66acd000-77fe-1000-9115-d804425607f3
UserTypes3
MediaTypes1
MediaTypes5
MediaTypes18
MediaTypes19
MediaTypes20
MediaTypes21
MediaTypes22
MediaTypes23
MediaTypes30
MediaTypes34
MediaTypes37

FindGames

NamesValues
Localeen-US
LegalLocaleen-US
Store3
PageSize10
PageNum1
DetailView5
Relations2
UserTypes2
UserTypes3
MediaIds66acd000-77fe-1000-9115-d804425607f3
MediaTypes1
MediaTypes5
MediaTypes18
MediaTypes19
MediaTypes20
MediaTypes21
MediaTypes22
MediaTypes23
MediaTypes30
MediaTypes34
MediaTypes37
ImageFormats5
ImageSizes15